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

View file

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

View file

@ -64,27 +64,20 @@ const (
connectionRetryInterval = time.Second * 5
)
// futureResult holds information about a future promise to deliver the result
// of an asynchronous request.
type futureResult struct {
reply *btcjson.Reply
err error
}
// sendPostDetails houses an HTTP POST request to send to an RPC server as well
// as the original JSON-RPC command and a channel to reply on when the server
// responds with the result.
type sendPostDetails struct {
command btcjson.Cmd
request *http.Request
responseChan chan *futureResult
responseChan chan *response
}
// jsonRequest holds information about a json request that is used to properly
// detect, interpret, and deliver a reply to it.
type jsonRequest struct {
cmd btcjson.Cmd
responseChan chan *futureResult
responseChan chan *response
}
// Client represents a Bitcoin RPC client which allows easy access to the
@ -231,70 +224,96 @@ func (c *Client) trackRegisteredNtfns(cmd btcjson.Cmd) {
}
}
// handleMessage is the main handler for incoming requests. It enforces
// authentication, parses the incoming json, looks up and executes handlers
// (including pass through for standard RPC commands), sends the appropriate
// response. It also detects commands which are marked as long-running and
// sends them off to the asyncHander for processing.
type (
// inMessage is the first type that an incoming message is unmarshaled
// into. It supports both requests (for notification support) and
// responses. The partially-unmarshaled message is a notification if
// the embedded ID (from the response) is nil. Otherwise, it is a
// response.
inMessage struct {
ID *uint64 `json:"id"`
*rawNotification
*rawResponse
}
// rawNotification is a partially-unmarshaled JSON-RPC notification.
rawNotification struct {
Method string `json:"method"`
Params []json.RawMessage `json:"params"`
}
// rawResponse is a partially-unmarshaled JSON-RPC response. For this
// to be valid (according to JSON-RPC 1.0 spec), ID may not be nil.
rawResponse struct {
Result json.RawMessage `json:"result"`
Error *btcjson.Error `json:"error"`
}
)
// response is the raw bytes of a JSON-RPC result, or the error if the response
// error object was non-null.
type response struct {
result []byte
err error
}
// result checks whether the unmarshaled response contains a non-nil error,
// returning an unmarshaled btcjson.Error (or an unmarshaling error) if so.
// If the response is not an error, the raw bytes of the request are
// returned for further unmashaling into specific result types.
func (r rawResponse) result() (result []byte, err error) {
if r.Error != nil {
return nil, r.Error
}
return r.Result, nil
}
// handleMessage is the main handler for incoming notifications and responses.
func (c *Client) handleMessage(msg []byte) {
// Attempt to unmarshal the message as a known JSON-RPC command.
if cmd, err := btcjson.ParseMarshaledCmd(msg); err == nil {
// Commands that have an ID associated with them are not
// notifications. Since this is a client, it should not
// be receiving non-notifications.
if cmd.Id() != nil {
// Invalid response
log.Warnf("Remote server sent a non-notification "+
"JSON-RPC Request (Id: %v)", cmd.Id())
// Attempt to unmarshal the message as either a notifiation or response.
in := inMessage{}
err := json.Unmarshal(msg, &in)
if err != nil {
log.Warnf("Remote server sent invalid message: %v", err)
return
}
// JSON-RPC 1.0 notifications are requests with a null id.
if in.ID == nil {
ntfn := in.rawNotification
if ntfn == nil {
log.Warn("Malformed notification: missing " +
"method and parameters")
return
}
if ntfn.Method == "" {
log.Warn("Malformed notification: missing method")
return
}
// params are not optional: nil isn't valid (but len == 0 is)
if ntfn.Params == nil {
log.Warn("Malformed notification: missing params")
return
}
// Deliver the notification.
log.Tracef("Received notification [%s]", cmd.Method())
c.handleNotification(cmd)
log.Tracef("Received notification [%s]", in.Method)
c.handleNotification(in.rawNotification)
return
}
// The message was not a command/notification, so it should be a reply
// to a previous request.
var r btcjson.Reply
if err := json.Unmarshal([]byte(msg), &r); err != nil {
log.Warnf("Unable to unmarshal inbound message as " +
"notification or response")
if in.rawResponse == nil {
log.Warn("Malformed response: missing result and error")
return
}
// Ensure the reply has an id.
if r.Id == nil {
log.Warnf("Received response with no id")
return
}
// Ensure the id is the expected type.
fid, ok := (*r.Id).(float64)
if !ok {
log.Warnf("Received unexpected id type: %T (value %v)",
*r.Id, *r.Id)
return
}
id := uint64(fid)
log.Tracef("Received response for id %d (result %v)", id, r.Result)
id := *in.ID
log.Tracef("Received response for id %d (result %s)", id, in.Result)
request := c.removeRequest(id)
// Nothing more to do if there is no request associated with this reply.
if request == nil || request.responseChan == nil {
log.Warnf("Received unexpected reply: %s (id %d)", r.Result, id)
return
}
// Unmarshal the reply into a concrete result if possible and deliver
// it to the associated channel.
reply, err := btcjson.ReadResultCmd(request.cmd.Method(), []byte(msg))
if err != nil {
log.Warnf("Failed to unmarshal reply to command [%s] "+
"(id %d): %v", request.cmd.Method(), id, err)
request.responseChan <- &futureResult{reply: nil, err: err}
log.Warnf("Received unexpected reply: %s (id %d)", in.Result,
id)
return
}
@ -303,8 +322,9 @@ func (c *Client) handleMessage(msg []byte) {
// can automatically be re-established on reconnect.
c.trackRegisteredNtfns(request.cmd)
// Deliver the reply.
request.responseChan <- &futureResult{reply: &reply, err: nil}
// Deliver the response.
result, err := in.rawResponse.result()
request.responseChan <- &response{result: result, err: err}
}
// wsInHandler handles all incoming messages for the websocket connection
@ -576,24 +596,17 @@ func (c *Client) handleSendPostMessage(details *sendPostDetails) {
log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id())
httpResponse, err := c.httpClient.Do(details.request)
if err != nil {
details.responseChan <- &futureResult{reply: nil, err: err}
details.responseChan <- &response{result: nil, err: err}
return
}
// Read the raw bytes and close the response.
respBytes, err := btcjson.GetRaw(httpResponse.Body)
resp, err := btcjson.GetRaw(httpResponse.Body)
if err != nil {
details.responseChan <- &futureResult{reply: nil, err: err}
details.responseChan <- &response{result: nil, err: err}
return
}
// Unmarshal the reply into a concrete result if possible.
reply, err := btcjson.ReadResultCmd(cmd.Method(), respBytes)
if err != nil {
details.responseChan <- &futureResult{reply: nil, err: err}
return
}
details.responseChan <- &futureResult{reply: &reply, err: nil}
details.responseChan <- &response{result: resp, err: nil}
}
// sendPostHandler handles all outgoing messages when the client is running
@ -620,8 +633,8 @@ cleanup:
for {
select {
case details := <-c.sendPostChan:
details.responseChan <- &futureResult{
reply: nil,
details.responseChan <- &response{
result: nil,
err: ErrClientShutdown,
}
@ -637,17 +650,17 @@ cleanup:
// sendPostRequest sends the passed HTTP request to the RPC server using the
// HTTP client associated with the client. It is backed by a buffered channel,
// so it will not block until the send channel is full.
func (c *Client) sendPostRequest(req *http.Request, command btcjson.Cmd, responseChan chan *futureResult) {
func (c *Client) sendPostRequest(req *http.Request, command btcjson.Cmd, responseChan chan *response) {
// Don't send the message if shutting down.
select {
case <-c.shutdown:
responseChan <- &futureResult{reply: nil, err: ErrClientShutdown}
responseChan <- &response{result: nil, err: ErrClientShutdown}
default:
}
c.sendPostChan <- &sendPostDetails{
request: req,
command: command,
request: req,
responseChan: responseChan,
}
}
@ -655,9 +668,9 @@ func (c *Client) sendPostRequest(req *http.Request, command btcjson.Cmd, respons
// newFutureError returns a new future result channel that already has the
// passed error waitin on the channel with the reply set to nil. This is useful
// to easily return errors from the various Async functions.
func newFutureError(err error) chan *futureResult {
responseChan := make(chan *futureResult, 1)
responseChan <- &futureResult{err: err}
func newFutureError(err error) chan *response {
responseChan := make(chan *response, 1)
responseChan <- &response{err: err}
return responseChan
}
@ -665,27 +678,10 @@ func newFutureError(err error) chan *futureResult {
// reply or any errors. The examined errors include an error in the
// futureResult and the error in the reply from the server. This will block
// until the result is available on the passed channel.
func receiveFuture(responseChan chan *futureResult) (interface{}, error) {
func receiveFuture(f chan *response) ([]byte, error) {
// Wait for a response on the returned channel.
response := <-responseChan
if response.err != nil {
return nil, response.err
}
// At this point, the command was either sent to the server and
// there is a response from it, or it is intentionally a nil result
// used to bybass sends for cases such a requesting notifications when
// there are no handlers.
reply := response.reply
if reply == nil {
return nil, nil
}
if reply.Error != nil {
return nil, reply.Error
}
return reply.Result, nil
r := <-f
return r.result, r.err
}
// marshalAndSendPost marshals the passed command to JSON-RPC and sends it to
@ -694,10 +690,10 @@ func receiveFuture(responseChan chan *futureResult) (interface{}, error) {
// and closed for each command when using this method, however, the underlying
// HTTP client might coalesce multiple commands depending on several factors
// including the remote server configuration.
func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *futureResult) {
func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *response) {
marshalledJSON, err := json.Marshal(cmd)
if err != nil {
responseChan <- &futureResult{reply: nil, err: err}
responseChan <- &response{result: nil, err: err}
return
}
@ -709,7 +705,7 @@ func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *futureRe
url := protocol + "://" + c.config.Host
req, err := http.NewRequest("POST", url, bytes.NewReader(marshalledJSON))
if err != nil {
responseChan <- &futureResult{reply: nil, err: err}
responseChan <- &response{result: nil, err: err}
return
}
req.Close = true
@ -724,10 +720,10 @@ func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *futureRe
// marshalAndSend marshals the passed command to JSON-RPC and sends it to the
// server. It returns a response channel on which the reply will be delivered.
func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *futureResult) {
marshalledJSON, err := json.Marshal(cmd)
func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *response) {
marshalledJSON, err := cmd.MarshalJSON()
if err != nil {
responseChan <- &futureResult{reply: nil, err: err}
responseChan <- &response{result: nil, err: err}
return
}
@ -739,12 +735,12 @@ func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *futureResult
// response channel on which the reply will be deliver at some point in the
// future. It handles both websocket and HTTP POST mode depending on the
// configuration of the client.
func (c *Client) sendCmd(cmd btcjson.Cmd) chan *futureResult {
func (c *Client) sendCmd(cmd btcjson.Cmd) chan *response {
// Choose which marshal and send function to use depending on whether
// the client running in HTTP POST mode or not. When running in HTTP
// POST mode, the command is issued via an HTTP client. Otherwise,
// the command is issued via the asynchronous websocket channels.
responseChan := make(chan *futureResult, 1)
responseChan := make(chan *response, 1)
if c.config.HttpPostMode {
c.marshalAndSendPost(cmd, responseChan)
return responseChan
@ -804,8 +800,8 @@ func (c *Client) Disconnect() {
c.requestLock.Lock()
for e := c.requestList.Front(); e != nil; e = e.Next() {
req := e.Value.(*jsonRequest)
req.responseChan <- &futureResult{
reply: nil,
req.responseChan <- &response{
result: nil,
err: ErrClientDisconnect,
}
}
@ -834,8 +830,8 @@ func (c *Client) Shutdown() {
c.requestLock.Lock()
for e := c.requestList.Front(); e != nil; e = e.Next() {
req := e.Value.(*jsonRequest)
req.responseChan <- &futureResult{
reply: nil,
req.responseChan <- &response{
result: nil,
err: ErrClientShutdown,
}
}

View file

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

80
net.go
View file

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

392
notify.go
View file

@ -7,7 +7,9 @@ package btcrpcclient
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"github.com/conformal/btcjson"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
@ -68,9 +70,9 @@ func newNotificationState() *notificationState {
// result waiting on the channel with the reply set to nil. This is useful
// to ignore things such as notifications when the caller didn't specify any
// notification handlers.
func newNilFutureResult() chan *futureResult {
responseChan := make(chan *futureResult, 1)
responseChan <- &futureResult{reply: nil}
func newNilFutureResult() chan *response {
responseChan := make(chan *response, 1)
responseChan <- &response{result: nil, err: nil}
return responseChan
}
@ -161,183 +163,192 @@ type NotificationHandlers struct {
// for this package needs to be updated for a new notification type or
// the caller is using a custom notification this package does not know
// about.
OnUnknownNotification func(ntfn interface{})
OnUnknownNotification func(method string, params []json.RawMessage)
}
// handleNotification examines the passed notification type, performs
// conversions to get the raw notification types into higher level types and
// delivers the notification to the appropriate On<X> handler registered with
// the client.
func (c *Client) handleNotification(cmd btcjson.Cmd) {
func (c *Client) handleNotification(ntfn *rawNotification) {
// Ignore the notification if the client is not interested in any
// notifications.
if c.ntfnHandlers == nil {
return
}
switch ntfn := cmd.(type) {
switch ntfn.Method {
// OnBlockConnected
case *btcws.BlockConnectedNtfn:
case btcws.BlockConnectedNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnBlockConnected == nil {
return
}
hash, err := btcwire.NewShaHashFromStr(ntfn.Hash)
blockSha, blockHeight, err := parseChainNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received block connected notification with "+
"invalid hash string: %q", ntfn.Hash)
log.Warnf("Received invalid block connected "+
"notification: %v", err)
return
}
c.ntfnHandlers.OnBlockConnected(hash, ntfn.Height)
c.ntfnHandlers.OnBlockConnected(blockSha, blockHeight)
// OnBlockDisconnected
case *btcws.BlockDisconnectedNtfn:
case btcws.BlockDisconnectedNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnBlockDisconnected == nil {
return
}
hash, err := btcwire.NewShaHashFromStr(ntfn.Hash)
blockSha, blockHeight, err := parseChainNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received block disconnected notification "+
"with invalid hash string: %q", ntfn.Hash)
log.Warnf("Received invalid block connected "+
"notification: %v", err)
return
}
c.ntfnHandlers.OnBlockDisconnected(hash, ntfn.Height)
c.ntfnHandlers.OnBlockDisconnected(blockSha, blockHeight)
// OnRecvTx
case *btcws.RecvTxNtfn:
case btcws.RecvTxNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnRecvTx == nil {
return
}
// Decode the serialized transaction hex to raw bytes.
serializedTx, err := hex.DecodeString(ntfn.HexTx)
tx, block, err := parseChainTxNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received recvtx notification with invalid "+
"transaction hex '%q': %v", ntfn.HexTx, err)
}
// Deserialize the transaction.
var msgTx btcwire.MsgTx
err = msgTx.Deserialize(bytes.NewReader(serializedTx))
if err != nil {
log.Warnf("Received recvtx notification with "+
"transaction that failed to deserialize: %v",
log.Warnf("Received invalid recvtx notification: %v",
err)
return
}
c.ntfnHandlers.OnRecvTx(btcutil.NewTx(&msgTx), ntfn.Block)
c.ntfnHandlers.OnRecvTx(tx, block)
// OnRedeemingTx
case *btcws.RedeemingTxNtfn:
case btcws.RedeemingTxNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnRedeemingTx == nil {
return
}
// Decode the serialized transaction hex to raw bytes.
serializedTx, err := hex.DecodeString(ntfn.HexTx)
tx, block, err := parseChainTxNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received redeemingtx notification with "+
"invalid transaction hex '%q': %v", ntfn.HexTx,
err)
log.Warnf("Received invalid redeemingtx "+
"notification: %v", err)
return
}
// Deserialize the transaction.
var msgTx btcwire.MsgTx
err = msgTx.Deserialize(bytes.NewReader(serializedTx))
if err != nil {
log.Warnf("Received redeemingtx notification with "+
"transaction that failed to deserialize: %v",
err)
}
c.ntfnHandlers.OnRedeemingTx(btcutil.NewTx(&msgTx), ntfn.Block)
c.ntfnHandlers.OnRedeemingTx(tx, block)
// OnRescanProgress
case *btcws.RescanProgressNtfn:
case btcws.RescanProgressNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnRescanProgress == nil {
return
}
c.ntfnHandlers.OnRescanProgress(ntfn.LastProcessed)
lastProcessed, err := parseRescanProgressNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received invalid rescanprogress "+
"notification: %v", err)
return
}
c.ntfnHandlers.OnRescanProgress(lastProcessed)
// OnTxAccepted
case *btcws.TxAcceptedNtfn:
case btcws.TxAcceptedNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnTxAccepted == nil {
return
}
hash, err := btcwire.NewShaHashFromStr(ntfn.TxID)
hash, amt, err := parseTxAcceptedNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received tx accepted notification with "+
"invalid hash string: %q", ntfn.TxID)
log.Warnf("Received invalid tx accepted "+
"notification: %v", err)
return
}
c.ntfnHandlers.OnTxAccepted(hash, btcutil.Amount(ntfn.Amount))
c.ntfnHandlers.OnTxAccepted(hash, amt)
// OnTxAcceptedVerbose
case *btcws.TxAcceptedVerboseNtfn:
case btcws.TxAcceptedVerboseNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnTxAcceptedVerbose == nil {
return
}
c.ntfnHandlers.OnTxAcceptedVerbose(ntfn.RawTx)
rawTx, err := parseTxAcceptedVerboseNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received invalid tx accepted verbose "+
"notification: %v", err)
return
}
c.ntfnHandlers.OnTxAcceptedVerbose(rawTx)
// OnBtcdConnected
case *btcws.BtcdConnectedNtfn:
case btcws.BtcdConnectedNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnBtcdConnected == nil {
return
}
c.ntfnHandlers.OnBtcdConnected(ntfn.Connected)
connected, err := parseBtcdConnectedNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received invalid btcd connected "+
"notification: %v", err)
return
}
c.ntfnHandlers.OnBtcdConnected(connected)
// OnAccountBalance
case *btcws.AccountBalanceNtfn:
case btcws.AccountBalanceNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnAccountBalance == nil {
return
}
balance, err := btcjson.JSONToAmount(ntfn.Balance)
account, bal, conf, err := parseAccountBalanceNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received account balance notification with "+
"an amount that does not parse: %v",
ntfn.Balance)
log.Warnf("Received invalid account balance "+
"notification: %v", err)
return
}
c.ntfnHandlers.OnAccountBalance(ntfn.Account,
btcutil.Amount(balance), ntfn.Confirmed)
c.ntfnHandlers.OnAccountBalance(account, bal, conf)
// OnWalletLockState
case *btcws.WalletLockStateNtfn:
case btcws.WalletLockStateNtfnMethod:
// Ignore the notification is the client is not interested in
// it.
if c.ntfnHandlers.OnWalletLockState == nil {
return
}
c.ntfnHandlers.OnWalletLockState(ntfn.Locked)
// The account name is not notified, so the return value is
// discarded.
_, locked, err := parseWalletLockStateNtfnParams(ntfn.Params)
if err != nil {
log.Warnf("Received invalid wallet lock state "+
"notification: %v", err)
return
}
c.ntfnHandlers.OnWalletLockState(locked)
// OnUnknownNotification
default:
@ -345,13 +356,250 @@ func (c *Client) handleNotification(cmd btcjson.Cmd) {
return
}
c.ntfnHandlers.OnUnknownNotification(ntfn)
c.ntfnHandlers.OnUnknownNotification(ntfn.Method, ntfn.Params)
}
}
// wrongNumParams is an error type describing an unparseable JSON-RPC
// notificiation due to an incorrect number of parameters for the
// expected notification type. The value is the number of parameters
// of the invalid notification.
type wrongNumParams int
// Error satisifies the builtin error interface.
func (e wrongNumParams) Error() string {
return fmt.Sprintf("wrong number of parameters (%d)", e)
}
// parseChainNtfnParams parses out the block hash and height from the parameters
// of blockconnected and blockdisconnected notifications.
func parseChainNtfnParams(params []json.RawMessage) (*btcwire.ShaHash,
int32, error) {
if len(params) != 2 {
return nil, 0, wrongNumParams(len(params))
}
// Unmarshal first parameter as a string.
var blockShaStr string
err := json.Unmarshal(params[0], &blockShaStr)
if err != nil {
return nil, 0, err
}
// Unmarshal second parameter as an integer.
var blockHeight int32
err = json.Unmarshal(params[1], &blockHeight)
if err != nil {
return nil, 0, err
}
// Create ShaHash from block sha string.
blockSha, err := btcwire.NewShaHashFromStr(blockShaStr)
if err != nil {
return nil, 0, err
}
return blockSha, blockHeight, nil
}
// parseChainTxNtfnParams parses out the transaction and optional details about
// the block it's mined in from the parameters of recvtx and redeemingtx
// notifications.
func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx,
*btcws.BlockDetails, error) {
if len(params) == 0 || len(params) > 2 {
return nil, nil, wrongNumParams(len(params))
}
// Unmarshal first parameter as a string.
var txHex string
err := json.Unmarshal(params[0], &txHex)
if err != nil {
return nil, nil, err
}
// If present, unmarshal second optional parameter as the block details
// JSON object.
var block *btcws.BlockDetails
if len(params) > 1 {
err = json.Unmarshal(params[1], &block)
if err != nil {
return nil, nil, err
}
}
// Hex decode and deserialize the transaction.
serializedTx, err := hex.DecodeString(txHex)
if err != nil {
return nil, nil, err
}
var msgTx btcwire.MsgTx
err = msgTx.Deserialize(bytes.NewReader(serializedTx))
if err != nil {
return nil, nil, err
}
// TODO: Change recvtx and redeemingtx callback signatures to use
// nicer types for details about the block (block sha as a
// btcwire.ShaHash, block time as a time.Time, etc.).
return btcutil.NewTx(&msgTx), block, nil
}
// parseRescanProgressNtfnParams parses out the height of the last rescanned
// from the parameters of a rescanprogress notification.
func parseRescanProgressNtfnParams(params []json.RawMessage) (int32, error) {
if len(params) != 1 {
return 0, wrongNumParams(len(params))
}
// Unmarshal first parameter as an integer.
var height int32
err := json.Unmarshal(params[0], &height)
if err != nil {
return 0, err
}
return height, nil
}
// parseTxAcceptedNtfnParams parses out the transaction hash and total amount
// from the parameters of a txaccepted notification.
func parseTxAcceptedNtfnParams(params []json.RawMessage) (*btcwire.ShaHash,
btcutil.Amount, error) {
if len(params) != 2 {
return nil, 0, wrongNumParams(len(params))
}
// Unmarshal first parameter as a string.
var txShaStr string
err := json.Unmarshal(params[0], &txShaStr)
if err != nil {
return nil, 0, err
}
// Unmarshal second parameter as an integer.
var amt int64
err = json.Unmarshal(params[1], &amt)
if err != nil {
return nil, 0, err
}
// Decode string encoding of transaction sha.
txSha, err := btcwire.NewShaHashFromStr(txShaStr)
if err != nil {
return nil, 0, err
}
return txSha, btcutil.Amount(amt), nil
}
// parseTxAcceptedVerboseNtfnParams parses out details about a raw transaction
// from the parameters of a txacceptedverbose notification.
func parseTxAcceptedVerboseNtfnParams(params []json.RawMessage) (*btcjson.TxRawResult,
error) {
if len(params) != 1 {
return nil, wrongNumParams(len(params))
}
// Unmarshal first parameter as a raw transaction result object.
var rawTx btcjson.TxRawResult
err := json.Unmarshal(params[0], &rawTx)
if err != nil {
return nil, err
}
// TODO: change txacceptedverbose notifiation callbacks to use nicer
// types for all details about the transaction (i.e. decoding hashes
// from their string encoding).
return &rawTx, nil
}
// parseBtcdConnectedNtfnParams parses out the connection status of btcd
// and btcwallet from the parameters of a btcdconnected notification.
func parseBtcdConnectedNtfnParams(params []json.RawMessage) (bool, error) {
if len(params) != 1 {
return false, wrongNumParams(len(params))
}
// Unmarshal first parameter as a boolean.
var connected bool
err := json.Unmarshal(params[0], &connected)
if err != nil {
return false, err
}
return connected, nil
}
// parseAccountBalanceNtfnParams parses out the account name, total balance,
// and whether or not the balance is confirmed or unconfirmed from the
// parameters of an accountbalance notification.
func parseAccountBalanceNtfnParams(params []json.RawMessage) (account string,
balance btcutil.Amount, confirmed bool, err error) {
if len(params) != 3 {
return "", 0, false, wrongNumParams(len(params))
}
// Unmarshal first parameter as a string.
err = json.Unmarshal(params[0], &account)
if err != nil {
return "", 0, false, err
}
// Unmarshal second parameter as a floating point number.
var fbal float64
err = json.Unmarshal(params[1], &fbal)
if err != nil {
return "", 0, false, err
}
// Unmarshal third parameter as a boolean.
err = json.Unmarshal(params[2], &confirmed)
if err != nil {
return "", 0, false, err
}
// Bounds check amount.
bal, err := btcjson.JSONToAmount(fbal)
if err != nil {
return "", 0, false, err
}
return account, btcutil.Amount(bal), confirmed, nil
}
// parseWalletLockStateNtfnParams parses out the account name and locked
// state of an account from the parameters of a walletlockstate notification.
func parseWalletLockStateNtfnParams(params []json.RawMessage) (account string,
locked bool, err error) {
if len(params) != 2 {
return "", false, wrongNumParams(len(params))
}
// Unmarshal first parameter as a string.
err = json.Unmarshal(params[0], &account)
if err != nil {
return "", false, err
}
// Unmarshal second parameter as a boolean.
err = json.Unmarshal(params[1], &locked)
if err != nil {
return "", false, err
}
return account, locked, nil
}
// FutureNotifyBlocksResult is a future promise to deliver the result of a
// NotifyBlocksAsync RPC invocation (or an applicable error).
type FutureNotifyBlocksResult chan *futureResult
type FutureNotifyBlocksResult chan *response
// Receive waits for the response promised by the future and returns an error
// if the registration was not successful.
@ -405,7 +653,7 @@ func (c *Client) NotifyBlocks() error {
// FutureNotifySpentResult is a future promise to deliver the result of a
// NotifySpentAsync RPC invocation (or an applicable error).
type FutureNotifySpentResult chan *futureResult
type FutureNotifySpentResult chan *response
// Receive waits for the response promised by the future and returns an error
// if the registration was not successful.
@ -484,7 +732,7 @@ func (c *Client) NotifySpent(outpoints []*btcwire.OutPoint) error {
// FutureNotifyNewTransactionsResult is a future promise to deliver the result
// of a NotifyNewTransactionsAsync RPC invocation (or an applicable error).
type FutureNotifyNewTransactionsResult chan *futureResult
type FutureNotifyNewTransactionsResult chan *response
// Receive waits for the response promised by the future and returns an error
// if the registration was not successful.
@ -542,7 +790,7 @@ func (c *Client) NotifyNewTransactions(verbose bool) error {
// FutureNotifyReceivedResult is a future promise to deliver the result of a
// NotifyReceivedAsync RPC invocation (or an applicable error).
type FutureNotifyReceivedResult chan *futureResult
type FutureNotifyReceivedResult chan *response
// Receive waits for the response promised by the future and returns an error
// if the registration was not successful.
@ -630,7 +878,7 @@ func (c *Client) NotifyReceived(addresses []btcutil.Address) error {
// FutureRescanResult is a future promise to deliver the result of a RescanAsync
// or RescanEndHeightAsync RPC invocation (or an applicable error).
type FutureRescanResult chan *futureResult
type FutureRescanResult chan *response
// Receive waits for the response promised by the future and returns an error
// if the rescan was not successful.

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

374
wallet.go
View file

@ -5,7 +5,7 @@
package btcrpcclient
import (
"fmt"
"encoding/json"
"github.com/conformal/btcjson"
"github.com/conformal/btcnet"
"github.com/conformal/btcutil"
@ -19,24 +19,24 @@ import (
// FutureGetTransactionResult is a future promise to deliver the result
// of a GetTransactionAsync RPC invocation (or an applicable error).
type FutureGetTransactionResult chan *futureResult
type FutureGetTransactionResult chan *response
// Receive waits for the response promised by the future and returns detailed
// information about a wallet transaction.
func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
result, ok := reply.(*btcjson.GetTransactionResult)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"gettransaction: %T\n", reply)
// Unmarshal result as a gettransaction result object
var getTx btcjson.GetTransactionResult
err = json.Unmarshal(res, &getTx)
if err != nil {
return nil, err
}
return result, nil
return &getTx, nil
}
// GetTransactionAsync returns an instance of a type that can be used to get the
@ -69,26 +69,21 @@ func (c *Client) GetTransaction(txHash *btcwire.ShaHash) (*btcjson.GetTransactio
// FutureListTransactionsResult is a future promise to deliver the result of a
// ListTransactionsAsync, ListTransactionsCountAsync, or
// ListTransactionsCountFromAsync RPC invocation (or an applicable error).
type FutureListTransactionsResult chan *futureResult
type FutureListTransactionsResult chan *response
// Receive waits for the response promised by the future and returns a list of
// the most recent transactions.
func (r FutureListTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// No transactions.
if reply == nil {
return nil, nil
}
// Ensure the returned data is the expected type.
transactions, ok := reply.([]btcjson.ListTransactionsResult)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"listtransactions: %T\n", reply)
// Unmarshal result as an array of listtransaction result objects.
var transactions []btcjson.ListTransactionsResult
err = json.Unmarshal(res, &transactions)
if err != nil {
return nil, err
}
return transactions, nil
@ -167,7 +162,7 @@ func (c *Client) ListTransactionsCountFrom(account string, count, from int) ([]b
// FutureListUnspentResult is a future promise to deliver the result of a
// ListUnspentAsync, ListUnspentMinAsync, ListUnspentMinMaxAsync, or
// ListUnspentMinMaxAddressesAsync RPC invocation (or an applicable error).
type FutureListUnspentResult chan *futureResult
type FutureListUnspentResult chan *response
// Receive waits for the response promised by the future and returns all
// unspent wallet transaction outputs returned by the RPC call. If the
@ -175,21 +170,16 @@ type FutureListUnspentResult chan *futureResult
// or ListUnspentMinMaxAddressesAsync, the range may be limited by the
// parameters of the RPC invocation.
func (r FutureListUnspentResult) Receive() ([]btcjson.ListUnspentResult, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// No unspent transaction outputs.
if reply == nil {
return nil, nil
}
// Ensure the returned data is the expected type.
unspent, ok := reply.([]btcjson.ListUnspentResult)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"listunspent: %T\n", reply)
// Unmarshal result as an array of listunspent results.
var unspent []btcjson.ListUnspentResult
err = json.Unmarshal(res, &unspent)
if err != nil {
return nil, err
}
return unspent, nil
@ -291,25 +281,25 @@ func (c *Client) ListUnspentMinMaxAddresses(minConf, maxConf int, addrs []btcuti
// FutureListSinceBlockResult is a future promise to deliver the result of a
// ListSinceBlockAsync or ListSinceBlockMinConfAsync RPC invocation (or an
// applicable error).
type FutureListSinceBlockResult chan *futureResult
type FutureListSinceBlockResult chan *response
// Receive waits for the response promised by the future and returns all
// transactions added in blocks since the specified block hash, or all
// transactions if it is nil.
func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
listResult, ok := reply.(*btcjson.ListSinceBlockResult)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"listsinceblock: %T\n", reply)
// Unmarshal result as a listsinceblock result object.
var listResult btcjson.ListSinceBlockResult
err = json.Unmarshal(res, &listResult)
if err != nil {
return nil, err
}
return listResult, nil
return &listResult, nil
}
// ListSinceBlockAsync returns an instance of a type that can be used to get
@ -376,7 +366,7 @@ func (c *Client) ListSinceBlockMinConf(blockHash *btcwire.ShaHash, minConfirms i
// FutureSetTxFeeResult is a future promise to deliver the result of a
// SetTxFeeAsync RPC invocation (or an applicable error).
type FutureSetTxFeeResult chan *futureResult
type FutureSetTxFeeResult chan *response
// Receive waits for the response promised by the future and returns the result
// of setting an optional transaction fee per KB that helps ensure transactions
@ -413,20 +403,21 @@ func (c *Client) SetTxFee(fee btcutil.Amount) error {
// FutureSendToAddressResult is a future promise to deliver the result of a
// SendToAddressAsync RPC invocation (or an applicable error).
type FutureSendToAddressResult chan *futureResult
type FutureSendToAddressResult chan *response
// Receive waits for the response promised by the future and returns the hash
// of the transaction sending the passed amount to the given address.
func (r FutureSendToAddressResult) Receive() (*btcwire.ShaHash, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
txHash, ok := reply.(string)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"sendtoaddress: %T\n", reply)
// Unmarshal result as a string.
var txHash string
err = json.Unmarshal(res, &txHash)
if err != nil {
return nil, err
}
return btcwire.NewShaHashFromStr(txHash)
@ -500,22 +491,22 @@ func (c *Client) SendToAddressComment(address btcutil.Address, amount btcutil.Am
// FutureSendFromResult is a future promise to deliver the result of a
// SendFromAsync, SendFromMinConfAsync, or SendFromCommentAsync RPC invocation
// (or an applicable error).
type FutureSendFromResult chan *futureResult
type FutureSendFromResult chan *response
// Receive waits for the response promised by the future and returns the hash
// of the transaction sending amount to the given address using the provided
// account as a source of funds.
func (r FutureSendFromResult) Receive() (*btcwire.ShaHash, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
txHash, ok := reply.(string)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"sendfrom: %T\n", reply)
// Unmarshal result as a string.
var txHash string
err = json.Unmarshal(res, &txHash)
if err != nil {
return nil, err
}
return btcwire.NewShaHashFromStr(txHash)
@ -622,22 +613,22 @@ func (c *Client) SendFromComment(fromAccount string, toAddress btcutil.Address,
// FutureSendManyResult is a future promise to deliver the result of a
// SendManyAsync, SendManyMinConfAsync, or SendManyCommentAsync RPC invocation
// (or an applicable error).
type FutureSendManyResult chan *futureResult
type FutureSendManyResult chan *response
// Receive waits for the response promised by the future and returns the hash
// of the transaction sending multiple amounts to multiple addresses using the
// provided account as a source of funds.
func (r FutureSendManyResult) Receive() (*btcwire.ShaHash, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
txHash, ok := reply.(string)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"sendmany: %T\n", reply)
// Unmashal result as a string.
var txHash string
err = json.Unmarshal(res, &txHash)
if err != nil {
return nil, err
}
return btcwire.NewShaHashFromStr(txHash)
@ -760,22 +751,22 @@ func (c *Client) SendManyComment(fromAccount string,
// FutureAddMultisigAddressResult is a future promise to deliver the result of a
// AddMultisigAddressAsync RPC invocation (or an applicable error).
type FutureAddMultisigAddressResult chan *futureResult
type FutureAddMultisigAddressResult chan *response
// Receive waits for the response promised by the future and returns the
// multisignature address that requires the specified number of signatures for
// the provided addresses.
func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
addr, ok := reply.(string)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"addmultisigaddress: %T\n", reply)
// Unmarshal result as a string.
var addr string
err = json.Unmarshal(res, &addr)
if err != nil {
return nil, err
}
return btcutil.DecodeAddress(addr, &btcnet.MainNetParams)
@ -811,24 +802,24 @@ func (c *Client) AddMultisigAddress(requiredSigs int, addresses []btcutil.Addres
// FutureCreateMultisigResult is a future promise to deliver the result of a
// CreateMultisigAsync RPC invocation (or an applicable error).
type FutureCreateMultisigResult chan *futureResult
type FutureCreateMultisigResult chan *response
// Receive waits for the response promised by the future and returns the
// multisignature address and script needed to redeem it.
func (r FutureCreateMultisigResult) Receive() (*btcjson.CreateMultiSigResult, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
result, ok := reply.(*btcjson.CreateMultiSigResult)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"createmultisig: %T\n", reply)
// Unmarshal result as a createmultisig result object.
var multisigRes btcjson.CreateMultiSigResult
err = json.Unmarshal(res, &multisigRes)
if err != nil {
return nil, err
}
return result, nil
return &multisigRes, nil
}
// CreateMultisigAsync returns an instance of a type that can be used to get
@ -861,21 +852,21 @@ func (c *Client) CreateMultisig(requiredSigs int, addresses []btcutil.Address) (
// FutureGetNewAddressResult is a future promise to deliver the result of a
// GetNewAddressAsync RPC invocation (or an applicable error).
type FutureGetNewAddressResult chan *futureResult
type FutureGetNewAddressResult chan *response
// Receive waits for the response promised by the future and returns a new
// address.
func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
addr, ok := reply.(string)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"getnewaddress: %T\n", reply)
// Unmarshal result as a string.
var addr string
err = json.Unmarshal(res, &addr)
if err != nil {
return nil, err
}
return btcutil.DecodeAddress(addr, &btcnet.MainNetParams)
@ -903,22 +894,22 @@ func (c *Client) GetNewAddress() (btcutil.Address, error) {
// FutureGetRawChangeAddressResult is a future promise to deliver the result of
// a GetRawChangeAddressAsync RPC invocation (or an applicable error).
type FutureGetRawChangeAddressResult chan *futureResult
type FutureGetRawChangeAddressResult chan *response
// Receive waits for the response promised by the future and returns a new
// address for receiving change that will be associated with the provided
// account. Note that this is only for raw transactions and NOT for normal use.
func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
addr, ok := reply.(string)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"getrawchangeaddress: %T\n", reply)
// Unmarshal result as a string.
var addr string
err = json.Unmarshal(res, &addr)
if err != nil {
return nil, err
}
return btcutil.DecodeAddress(addr, &btcnet.MainNetParams)
@ -948,21 +939,21 @@ func (c *Client) GetRawChangeAddress(account string) (btcutil.Address, error) {
// FutureGetAccountAddressResult is a future promise to deliver the result of a
// GetAccountAddressAsync RPC invocation (or an applicable error).
type FutureGetAccountAddressResult chan *futureResult
type FutureGetAccountAddressResult chan *response
// Receive waits for the response promised by the future and returns the current
// Bitcoin address for receiving payments to the specified account.
func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
addr, ok := reply.(string)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"getaccountaddress: %T\n", reply)
// Unmarshal result as a string.
var addr string
err = json.Unmarshal(res, &addr)
if err != nil {
return nil, err
}
return btcutil.DecodeAddress(addr, &btcnet.MainNetParams)
@ -991,21 +982,21 @@ func (c *Client) GetAccountAddress(account string) (btcutil.Address, error) {
// FutureGetAccountResult is a future promise to deliver the result of a
// GetAccountAsync RPC invocation (or an applicable error).
type FutureGetAccountResult chan *futureResult
type FutureGetAccountResult chan *response
// Receive waits for the response promised by the future and returns the account
// associated with the passed address.
func (r FutureGetAccountResult) Receive() (string, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return "", err
}
// Ensure the returned data is the expected type.
account, ok := reply.(string)
if !ok {
return "", fmt.Errorf("unexpected response type for "+
"getaccount: %T\n", reply)
// Unmarshal result as a string.
var account string
err = json.Unmarshal(res, &account)
if err != nil {
return "", err
}
return account, nil
@ -1034,7 +1025,7 @@ func (c *Client) GetAccount(address btcutil.Address) (string, error) {
// FutureSetAccountResult is a future promise to deliver the result of a
// SetAccountAsync RPC invocation (or an applicable error).
type FutureSetAccountResult chan *futureResult
type FutureSetAccountResult chan *response
// Receive waits for the response promised by the future and returns the result
// of setting the account to be associated with the passed address.
@ -1070,21 +1061,21 @@ func (c *Client) SetAccount(address btcutil.Address, account string) error {
// FutureGetAddressesByAccountResult is a future promise to deliver the result
// of a GetAddressesByAccountAsync RPC invocation (or an applicable error).
type FutureGetAddressesByAccountResult chan *futureResult
type FutureGetAddressesByAccountResult chan *response
// Receive waits for the response promised by the future and returns the list of
// addresses associated with the passed account.
func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
addrStrings, ok := reply.([]string)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"getaddressesbyaccount: %T\n", reply)
// Unmashal result as an array of string.
var addrStrings []string
err = json.Unmarshal(res, &addrStrings)
if err != nil {
return nil, err
}
addrs := make([]btcutil.Address, 0, len(addrStrings))
@ -1122,24 +1113,24 @@ func (c *Client) GetAddressesByAccount(account string) ([]btcutil.Address, error
// FutureValidateAddressResult is a future promise to deliver the result of a
// ValidateAddressAsync RPC invocation (or an applicable error).
type FutureValidateAddressResult chan *futureResult
type FutureValidateAddressResult chan *response
// Receive waits for the response promised by the future and returns information
// about the given bitcoin address.
func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressResult, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
result, ok := reply.(*btcjson.ValidateAddressResult)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"validateaddress: %T\n", reply)
// Unmarshal result as a validateaddress result object.
var addrResult btcjson.ValidateAddressResult
err = json.Unmarshal(res, &addrResult)
if err != nil {
return nil, err
}
return result, nil
return &addrResult, nil
}
// ValidateAddressAsync returns an instance of a type that can be used to get
@ -1165,7 +1156,7 @@ func (c *Client) ValidateAddress(address btcutil.Address) (*btcjson.ValidateAddr
// FutureKeyPoolRefillResult is a future promise to deliver the result of a
// KeyPoolRefillAsync RPC invocation (or an applicable error).
type FutureKeyPoolRefillResult chan *futureResult
type FutureKeyPoolRefillResult chan *response
// Receive waits for the response promised by the future and returns the result
// of refilling the key pool.
@ -1228,21 +1219,21 @@ func (c *Client) KeyPoolRefillSize(newSize uint) error {
// FutureListAccountsResult is a future promise to deliver the result of a
// ListAccountsAsync or ListAccountsMinConfAsync RPC invocation (or an
// applicable error).
type FutureListAccountsResult chan *futureResult
type FutureListAccountsResult chan *response
// Receive waits for the response promised by the future and returns returns a
// map of account names and their associated balances.
func (r FutureListAccountsResult) Receive() (map[string]btcutil.Amount, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
accounts, ok := reply.(map[string]float64)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"listaccounts: %T\n", reply)
// Unmarshal result as a json object.
var accounts map[string]float64
err = json.Unmarshal(res, &accounts)
if err != nil {
return nil, err
}
accountsMap := make(map[string]btcutil.Amount)
@ -1307,21 +1298,21 @@ func (c *Client) ListAccountsMinConf(minConfirms int) (map[string]btcutil.Amount
// FutureGetBalanceResult is a future promise to deliver the result of a
// GetBalanceAsync or GetBalanceMinConfAsync RPC invocation (or an applicable
// error).
type FutureGetBalanceResult chan *futureResult
type FutureGetBalanceResult chan *response
// Receive waits for the response promised by the future and returns the
// available balance from the server for the specified account.
func (r FutureGetBalanceResult) Receive() (btcutil.Amount, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return 0, err
}
// Ensure the returned data is the expected type.
balance, ok := reply.(float64)
if !ok {
return 0, fmt.Errorf("unexpected response type for "+
"getbalance: %T\n", reply)
// Unmarshal result as a floating point number.
var balance float64
err = json.Unmarshal(res, &balance)
if err != nil {
return 0, err
}
satoshi, err := btcjson.JSONToAmount(balance)
@ -1391,21 +1382,21 @@ func (c *Client) GetBalanceMinConf(account string, minConfirms int) (btcutil.Amo
// FutureGetReceivedByAccountResult is a future promise to deliver the result of
// a GetReceivedByAccountAsync or GetReceivedByAccountMinConfAsync RPC
// invocation (or an applicable error).
type FutureGetReceivedByAccountResult chan *futureResult
type FutureGetReceivedByAccountResult chan *response
// Receive waits for the response promised by the future and returns the total
// amount received with the specified account.
func (r FutureGetReceivedByAccountResult) Receive() (btcutil.Amount, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return 0, err
}
// Ensure the returned data is the expected type.
balance, ok := reply.(float64)
if !ok {
return 0, fmt.Errorf("unexpected response type for "+
"getreceivedbyaccount: %T\n", reply)
// Unmarshal result as a floating point number.
var balance float64
err = json.Unmarshal(res, &balance)
if err != nil {
return 0, err
}
satoshi, err := btcjson.JSONToAmount(balance)
@ -1466,21 +1457,21 @@ func (c *Client) GetReceivedByAccountMinConf(account string, minConfirms int) (b
// FutureGetUnconfirmedBalanceResult is a future promise to deliver the result
// of a GetUnconfirmedBalanceAsync RPC invocation (or an applicable error).
type FutureGetUnconfirmedBalanceResult chan *futureResult
type FutureGetUnconfirmedBalanceResult chan *response
// Receive waits for the response promised by the future and returns returns the
// unconfirmed balance from the server for the specified account.
func (r FutureGetUnconfirmedBalanceResult) Receive() (btcutil.Amount, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return 0, err
}
// Ensure the returned data is the expected type.
balance, ok := reply.(float64)
if !ok {
return 0, fmt.Errorf("unexpected response type for "+
"getunconfirmedbalance: %T\n", reply)
// Unmarshal result as a floating point number.
var balance float64
err = json.Unmarshal(res, &balance)
if err != nil {
return 0, err
}
satoshi, err := btcjson.JSONToAmount(balance)
@ -1515,21 +1506,21 @@ func (c *Client) GetUnconfirmedBalance(account string) (btcutil.Amount, error) {
// FutureGetReceivedByAddressResult is a future promise to deliver the result of
// a GetReceivedByAddressAsync or GetReceivedByAddressMinConfAsync RPC
// invocation (or an applicable error).
type FutureGetReceivedByAddressResult chan *futureResult
type FutureGetReceivedByAddressResult chan *response
// Receive waits for the response promised by the future and returns the total
// amount received by the specified address.
func (r FutureGetReceivedByAddressResult) Receive() (btcutil.Amount, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return 0, err
}
// Ensure the returned data is the expected type.
balance, ok := reply.(float64)
if !ok {
return 0, fmt.Errorf("unexpected response type for "+
"getreceivedbyaddress: %T\n", reply)
// Unmarshal result as a floating point number.
var balance float64
err = json.Unmarshal(res, &balance)
if err != nil {
return 0, err
}
satoshi, err := btcjson.JSONToAmount(balance)
@ -1594,29 +1585,24 @@ func (c *Client) GetReceivedByAddressMinConf(address btcutil.Address, minConfirm
// of a ListReceivedByAccountAsync, ListReceivedByAccountMinConfAsync, or
// ListReceivedByAccountIncludeEmptyAsync RPC invocation (or an applicable
// error).
type FutureListReceivedByAccountResult chan *futureResult
type FutureListReceivedByAccountResult chan *response
// Receive waits for the response promised by the future and returns a list of
// balances by account.
func (r FutureListReceivedByAccountResult) Receive() ([]btcjson.ListReceivedByAccountResult, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// No results.
if reply == nil {
return nil, nil
// Unmarshal as an array of listreceivedbyaccount result objects.
var received []btcjson.ListReceivedByAccountResult
err = json.Unmarshal(res, &received)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
result, ok := reply.([]btcjson.ListReceivedByAccountResult)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"listreceivedbyaccount: %T\n", reply)
}
return result, nil
return received, nil
}
// ListReceivedByAccountAsync returns an instance of a type that can be used to
@ -1702,7 +1688,7 @@ func (c *Client) ListReceivedByAccountIncludeEmpty(minConfirms int, includeEmpty
// FutureWalletLockResult is a future promise to deliver the result of a
// WalletLockAsync RPC invocation (or an applicable error).
type FutureWalletLockResult chan *futureResult
type FutureWalletLockResult chan *response
// Receive waits for the response promised by the future and returns the result
// of locking the wallet.
@ -1759,7 +1745,7 @@ func (c *Client) WalletPassphrase(passphrase string, timeoutSecs int64) error {
// FutureWalletPassphraseChangeResult is a future promise to deliver the result
// of a WalletPassphraseChangeAsync RPC invocation (or an applicable error).
type FutureWalletPassphraseChangeResult chan *futureResult
type FutureWalletPassphraseChangeResult chan *response
// Receive waits for the response promised by the future and returns the result
// of changing the wallet passphrase.
@ -1799,21 +1785,21 @@ func (c *Client) WalletPassphraseChange(old, new string) error {
// FutureSignMessageResult is a future promise to deliver the result of a
// SignMessageAsync RPC invocation (or an applicable error).
type FutureSignMessageResult chan *futureResult
type FutureSignMessageResult chan *response
// Receive waits for the response promised by the future and returns the message
// signed with the private key of the specified address.
func (r FutureSignMessageResult) Receive() (string, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return "", err
}
// Ensure the returned data is the expected type.
b64, ok := reply.(string)
if !ok {
return "", fmt.Errorf("unexpected response type for "+
"signmessage: %T\n", reply)
// Unmarshal result as a string.
var b64 string
err = json.Unmarshal(res, &b64)
if err != nil {
return "", err
}
return b64, nil
@ -1845,21 +1831,21 @@ func (c *Client) SignMessage(address btcutil.Address, message string) (string, e
// FutureVerifyMessageResult is a future promise to deliver the result of a
// VerifyMessageAsync RPC invocation (or an applicable error).
type FutureVerifyMessageResult chan *futureResult
type FutureVerifyMessageResult chan *response
// Receive waits for the response promised by the future and returns whether or
// not the message was successfully verified.
func (r FutureVerifyMessageResult) Receive() (bool, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return false, err
}
// Ensure the returned data is the expected type.
verified, ok := reply.(bool)
if !ok {
return false, fmt.Errorf("unexpected response type for "+
"verifymessage: %T\n", reply)
// Unmarshal result as a boolean.
var verified bool
err = json.Unmarshal(res, &verified)
if err != nil {
return false, err
}
return verified, nil
@ -1895,22 +1881,22 @@ func (c *Client) VerifyMessage(address btcutil.Address, signature, message strin
// FutureDumpPrivKeyResult is a future promise to deliver the result of a
// DumpPrivKeyAsync RPC invocation (or an applicable error).
type FutureDumpPrivKeyResult chan *futureResult
type FutureDumpPrivKeyResult chan *response
// Receive waits for the response promised by the future and returns the private
// key corresponding to the passed address encoded in the wallet import format
// (WIF)
func (r FutureDumpPrivKeyResult) Receive() (*btcutil.WIF, error) {
reply, err := receiveFuture(r)
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Ensure the returned data is the expected type.
privKeyWIF, ok := reply.(string)
if !ok {
return nil, fmt.Errorf("unexpected response type for "+
"dumpprivkey: %T\n", reply)
// Unmarshal result as a string.
var privKeyWIF string
err = json.Unmarshal(res, &privKeyWIF)
if err != nil {
return nil, err
}
return btcutil.DecodeWIF(privKeyWIF)
@ -1943,7 +1929,7 @@ func (c *Client) DumpPrivKey(address btcutil.Address) (*btcutil.WIF, error) {
// FutureImportPrivKeyResult is a future promise to deliver the result of an
// ImportPrivKeyAsync RPC invocation (or an applicable error).
type FutureImportPrivKeyResult chan *futureResult
type FutureImportPrivKeyResult chan *response
// Receive waits for the response promised by the future and returns the result
// of importing the passed private key which must be the wallet import format