Update to make use of latest version of btcjson.

This commit contains several changes needed to update the client to use
the latest version of btcjson.  In addition, it contains a couple of other
minor changes along the way.

While the underlying changes are quite large, the public API of this
package is still the same, so caller should generally not have to update
their code due to that.  However, the underlying btcjson package API has
changed significantly.  Since this package hides the vast majority of that
from callers, it should not afffect them very much.  However, one area in
particular to watch out for is that the old btcjson.Error is now
btcjson.RPCError, so any callers doing any type assertions there will need
to update.

The following is a summary of the changes:

- The underlying btcjson significantly changed how commands work, so the
  internals of this package have been reworked to be based off of requests
  instead of the now non-existant btcjson.Cmd interface
- Update all call sites of btcjson.New<Foo>Cmd since they can no longer
  error or take varargs
- The ids for each request are part of the request now instead of the
  command to match the new btcjson model and are strict uint64s so type
  assertions are no longer needed (slightly improved efficiency)
- Remove the old temporary workaround for the getbalance command with an
  account of "*" since btcwallet has since been fixed
- Change all instances of JSONToAmount to btcutil.NewAmount since that
  function was removed in favor of btcutil.Amount
- Change all btcws invocations to btcjson since they have been combined
This commit is contained in:
Dave Collins 2015-01-01 16:34:07 -06:00
parent d45f4c47a2
commit 9ca93b30ad
10 changed files with 353 additions and 896 deletions

View file

@ -9,7 +9,7 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcjson/v2/btcjson"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
) )
@ -41,12 +41,7 @@ func (r FutureGetBestBlockHashResult) Receive() (*wire.ShaHash, error) {
// //
// See GetBestBlockHash for the blocking version and more details. // See GetBestBlockHash for the blocking version and more details.
func (c *Client) GetBestBlockHashAsync() FutureGetBestBlockHashResult { func (c *Client) GetBestBlockHashAsync() FutureGetBestBlockHashResult {
id := c.NextID() cmd := btcjson.NewGetBestBlockHashCmd()
cmd, err := btcjson.NewGetBestBlockHashCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -101,12 +96,7 @@ func (c *Client) GetBlockAsync(blockHash *wire.ShaHash) FutureGetBlockResult {
hash = blockHash.String() hash = blockHash.String()
} }
id := c.NextID() cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(false), nil)
cmd, err := btcjson.NewGetBlockCmd(id, hash, false)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -124,14 +114,14 @@ type FutureGetBlockVerboseResult chan *response
// Receive waits for the response promised by the future and returns the data // Receive waits for the response promised by the future and returns the data
// structure from the server with information about the requested block. // structure from the server with information about the requested block.
func (r FutureGetBlockVerboseResult) Receive() (*btcjson.BlockResult, error) { func (r FutureGetBlockVerboseResult) Receive() (*btcjson.GetBlockVerboseResult, error) {
res, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Unmarshal the raw result into a BlockResult. // Unmarshal the raw result into a BlockResult.
var blockResult btcjson.BlockResult var blockResult btcjson.GetBlockVerboseResult
err = json.Unmarshal(res, &blockResult) err = json.Unmarshal(res, &blockResult)
if err != nil { if err != nil {
return nil, err return nil, err
@ -150,12 +140,7 @@ func (c *Client) GetBlockVerboseAsync(blockHash *wire.ShaHash, verboseTx bool) F
hash = blockHash.String() hash = blockHash.String()
} }
id := c.NextID() cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), &verboseTx)
cmd, err := btcjson.NewGetBlockCmd(id, hash, true, verboseTx)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -163,7 +148,7 @@ func (c *Client) GetBlockVerboseAsync(blockHash *wire.ShaHash, verboseTx bool) F
// about a block given its hash. // about a block given its hash.
// //
// See GetBlock to retrieve a raw block instead. // See GetBlock to retrieve a raw block instead.
func (c *Client) GetBlockVerbose(blockHash *wire.ShaHash, verboseTx bool) (*btcjson.BlockResult, error) { func (c *Client) GetBlockVerbose(blockHash *wire.ShaHash, verboseTx bool) (*btcjson.GetBlockVerboseResult, error) {
return c.GetBlockVerboseAsync(blockHash, verboseTx).Receive() return c.GetBlockVerboseAsync(blockHash, verboseTx).Receive()
} }
@ -194,12 +179,7 @@ func (r FutureGetBlockCountResult) Receive() (int64, error) {
// //
// See GetBlockCount for the blocking version and more details. // See GetBlockCount for the blocking version and more details.
func (c *Client) GetBlockCountAsync() FutureGetBlockCountResult { func (c *Client) GetBlockCountAsync() FutureGetBlockCountResult {
id := c.NextID() cmd := btcjson.NewGetBlockCountCmd()
cmd, err := btcjson.NewGetBlockCountCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -235,12 +215,7 @@ func (r FutureGetDifficultyResult) Receive() (float64, error) {
// //
// See GetDifficulty for the blocking version and more details. // See GetDifficulty for the blocking version and more details.
func (c *Client) GetDifficultyAsync() FutureGetDifficultyResult { func (c *Client) GetDifficultyAsync() FutureGetDifficultyResult {
id := c.NextID() cmd := btcjson.NewGetDifficultyCmd()
cmd, err := btcjson.NewGetDifficultyCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -277,12 +252,7 @@ func (r FutureGetBlockHashResult) Receive() (*wire.ShaHash, error) {
// //
// See GetBlockHash for the blocking version and more details. // See GetBlockHash for the blocking version and more details.
func (c *Client) GetBlockHashAsync(blockHeight int64) FutureGetBlockHashResult { func (c *Client) GetBlockHashAsync(blockHeight int64) FutureGetBlockHashResult {
id := c.NextID() cmd := btcjson.NewGetBlockHashCmd(blockHeight)
cmd, err := btcjson.NewGetBlockHashCmd(id, blockHeight)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -330,12 +300,7 @@ func (r FutureGetRawMempoolResult) Receive() ([]*wire.ShaHash, error) {
// //
// See GetRawMempool for the blocking version and more details. // See GetRawMempool for the blocking version and more details.
func (c *Client) GetRawMempoolAsync() FutureGetRawMempoolResult { func (c *Client) GetRawMempoolAsync() FutureGetRawMempoolResult {
id := c.NextID() cmd := btcjson.NewGetRawMempoolCmd(btcjson.Bool(false))
cmd, err := btcjson.NewGetRawMempoolCmd(id, false)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -354,7 +319,7 @@ type FutureGetRawMempoolVerboseResult chan *response
// Receive waits for the response promised by the future and returns a map of // Receive waits for the response promised by the future and returns a map of
// transaction hashes to an associated data structure with information about the // transaction hashes to an associated data structure with information about the
// transaction for all transactions in the memory pool. // transaction for all transactions in the memory pool.
func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMempoolResult, error) { func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMempoolVerboseResult, error) {
res, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
@ -362,7 +327,7 @@ func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMe
// Unmarshal the result as a map of strings (tx shas) to their detailed // Unmarshal the result as a map of strings (tx shas) to their detailed
// results. // results.
var mempoolItems map[string]btcjson.GetRawMempoolResult var mempoolItems map[string]btcjson.GetRawMempoolVerboseResult
err = json.Unmarshal(res, &mempoolItems) err = json.Unmarshal(res, &mempoolItems)
if err != nil { if err != nil {
return nil, err return nil, err
@ -376,12 +341,7 @@ func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMe
// //
// See GetRawMempoolVerbose for the blocking version and more details. // See GetRawMempoolVerbose for the blocking version and more details.
func (c *Client) GetRawMempoolVerboseAsync() FutureGetRawMempoolVerboseResult { func (c *Client) GetRawMempoolVerboseAsync() FutureGetRawMempoolVerboseResult {
id := c.NextID() cmd := btcjson.NewGetRawMempoolCmd(btcjson.Bool(true))
cmd, err := btcjson.NewGetRawMempoolCmd(id, true)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -390,7 +350,7 @@ func (c *Client) GetRawMempoolVerboseAsync() FutureGetRawMempoolVerboseResult {
// the memory pool. // the memory pool.
// //
// See GetRawMempool to retrieve only the transaction hashes instead. // See GetRawMempool to retrieve only the transaction hashes instead.
func (c *Client) GetRawMempoolVerbose() (map[string]btcjson.GetRawMempoolResult, error) { func (c *Client) GetRawMempoolVerbose() (map[string]btcjson.GetRawMempoolVerboseResult, error) {
return c.GetRawMempoolVerboseAsync().Receive() return c.GetRawMempoolVerboseAsync().Receive()
} }
@ -423,12 +383,7 @@ func (r FutureVerifyChainResult) Receive() (bool, error) {
// //
// See VerifyChain for the blocking version and more details. // See VerifyChain for the blocking version and more details.
func (c *Client) VerifyChainAsync() FutureVerifyChainResult { func (c *Client) VerifyChainAsync() FutureVerifyChainResult {
id := c.NextID() cmd := btcjson.NewVerifyChainCmd(nil, nil)
cmd, err := btcjson.NewVerifyChainCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -446,12 +401,7 @@ func (c *Client) VerifyChain() (bool, error) {
// //
// See VerifyChainLevel for the blocking version and more details. // See VerifyChainLevel for the blocking version and more details.
func (c *Client) VerifyChainLevelAsync(checkLevel int32) FutureVerifyChainResult { func (c *Client) VerifyChainLevelAsync(checkLevel int32) FutureVerifyChainResult {
id := c.NextID() cmd := btcjson.NewVerifyChainCmd(&checkLevel, nil)
cmd, err := btcjson.NewVerifyChainCmd(id, checkLevel)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -474,12 +424,7 @@ func (c *Client) VerifyChainLevel(checkLevel int32) (bool, error) {
// //
// See VerifyChainBlocks for the blocking version and more details. // See VerifyChainBlocks for the blocking version and more details.
func (c *Client) VerifyChainBlocksAsync(checkLevel, numBlocks int32) FutureVerifyChainResult { func (c *Client) VerifyChainBlocksAsync(checkLevel, numBlocks int32) FutureVerifyChainResult {
id := c.NextID() cmd := btcjson.NewVerifyChainCmd(&checkLevel, &numBlocks)
cmd, err := btcjson.NewVerifyChainCmd(id, checkLevel, numBlocks)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -537,11 +482,7 @@ func (c *Client) GetTxOutAsync(txHash *wire.ShaHash, index int, mempool bool) Fu
hash = txHash.String() hash = txHash.String()
} }
id := c.NextID() cmd := btcjson.NewGetTxOutCmd(hash, index, &mempool)
cmd, err := btcjson.NewGetTxOutCmd(id, hash, index, mempool)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }

6
doc.go
View file

@ -141,14 +141,14 @@ the type can vary, but usually will be best handled by simply showing/logging
it. it.
The third category of errors, that is errors returned by the server, can be The third category of errors, that is errors returned by the server, can be
detected by type asserting the error in a *btcjson.Error. For example, to detected by type asserting the error in a *btcjson.RPCError. For example, to
detect if a command is unimplemented by the remote RPC server: detect if a command is unimplemented by the remote RPC server:
amount, err := client.GetBalance("") amount, err := client.GetBalance("")
if err != nil { if err != nil {
if jerr, ok := err.(*btcjson.Error); ok { if jerr, ok := err.(*btcjson.RPCError); ok {
switch jerr.Code { switch jerr.Code {
case btcjson.ErrUnimplemented.Code: case btcjson.ErrRPCUnimplemented:
// Handle not implemented error // Handle not implemented error
// Handle other specific errors you care about // Handle other specific errors you care about

View file

@ -9,8 +9,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcjson/v2/btcjson"
"github.com/btcsuite/btcd/btcjson/btcws"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
) )
@ -45,12 +44,7 @@ func (r FutureDebugLevelResult) Receive() (string, error) {
// //
// NOTE: This is a btcd extension. // NOTE: This is a btcd extension.
func (c *Client) DebugLevelAsync(levelSpec string) FutureDebugLevelResult { func (c *Client) DebugLevelAsync(levelSpec string) FutureDebugLevelResult {
id := c.NextID() cmd := btcjson.NewDebugLevelCmd(levelSpec)
cmd, err := btcjson.NewDebugLevelCmd(id, levelSpec)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -86,8 +80,7 @@ func (r FutureCreateEncryptedWalletResult) Receive() error {
// //
// NOTE: This is a btcwallet extension. // NOTE: This is a btcwallet extension.
func (c *Client) CreateEncryptedWalletAsync(passphrase string) FutureCreateEncryptedWalletResult { func (c *Client) CreateEncryptedWalletAsync(passphrase string) FutureCreateEncryptedWalletResult {
id := c.NextID() cmd := btcjson.NewCreateEncryptedWalletCmd(passphrase)
cmd := btcws.NewCreateEncryptedWalletCmd(id, passphrase)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -137,12 +130,7 @@ func (c *Client) ListAddressTransactionsAsync(addresses []btcutil.Address, accou
for _, addr := range addresses { for _, addr := range addresses {
addrs = append(addrs, addr.EncodeAddress()) addrs = append(addrs, addr.EncodeAddress())
} }
id := c.NextID() cmd := btcjson.NewListAddressTransactionsCmd(addrs, &account)
cmd, err := btcws.NewListAddressTransactionsCmd(id, addrs, account)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -167,7 +155,7 @@ func (r FutureGetBestBlockResult) Receive() (*wire.ShaHash, int32, error) {
} }
// Unmarsal result as a getbestblock result object. // Unmarsal result as a getbestblock result object.
var bestBlock btcws.GetBestBlockResult var bestBlock btcjson.GetBestBlockResult
err = json.Unmarshal(res, &bestBlock) err = json.Unmarshal(res, &bestBlock)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -190,9 +178,7 @@ func (r FutureGetBestBlockResult) Receive() (*wire.ShaHash, int32, error) {
// //
// NOTE: This is a btcd extension. // NOTE: This is a btcd extension.
func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult { func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult {
id := c.NextID() cmd := btcjson.NewGetBestBlockCmd()
cmd := btcws.NewGetBestBlockCmd(id)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -234,9 +220,7 @@ func (r FutureGetCurrentNetResult) Receive() (wire.BitcoinNet, error) {
// //
// NOTE: This is a btcd extension. // NOTE: This is a btcd extension.
func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult { func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult {
id := c.NextID() cmd := btcjson.NewGetCurrentNetCmd()
cmd := btcws.NewGetCurrentNetCmd(id)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -302,12 +286,7 @@ func (r FutureExportWatchingWalletResult) Receive() ([]byte, []byte, error) {
// //
// NOTE: This is a btcwallet extension. // NOTE: This is a btcwallet extension.
func (c *Client) ExportWatchingWalletAsync(account string) FutureExportWatchingWalletResult { func (c *Client) ExportWatchingWalletAsync(account string) FutureExportWatchingWalletResult {
id := c.NextID() cmd := btcjson.NewExportWatchingWalletCmd(&account, btcjson.Bool(true))
cmd, err := btcws.NewExportWatchingWalletCmd(id, account, true)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }

View file

@ -13,6 +13,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -20,8 +21,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcjson/v2/btcjson"
"github.com/btcsuite/btcd/btcjson/btcws"
"github.com/btcsuite/go-socks/socks" "github.com/btcsuite/go-socks/socks"
"github.com/btcsuite/websocket" "github.com/btcsuite/websocket"
) )
@ -87,16 +87,18 @@ const (
// as the original JSON-RPC command and a channel to reply on when the server // as the original JSON-RPC command and a channel to reply on when the server
// responds with the result. // responds with the result.
type sendPostDetails struct { type sendPostDetails struct {
command btcjson.Cmd httpRequest *http.Request
request *http.Request jsonRequest *jsonRequest
responseChan chan *response
} }
// jsonRequest holds information about a json request that is used to properly // jsonRequest holds information about a json request that is used to properly
// detect, interpret, and deliver a reply to it. // detect, interpret, and deliver a reply to it.
type jsonRequest struct { type jsonRequest struct {
cmd btcjson.Cmd id uint64
responseChan chan *response method string
cmd interface{}
marshalledJSON []byte
responseChan chan *response
} }
// Client represents a Bitcoin RPC client which allows easy access to the // Client represents a Bitcoin RPC client which allows easy access to the
@ -163,15 +165,15 @@ func (c *Client) NextID() uint64 {
return atomic.AddUint64(&c.id, 1) return atomic.AddUint64(&c.id, 1)
} }
// addRequest associates the passed jsonRequest with the passed id. This allows // addRequest associates the passed jsonRequest with its id. This allows the
// the response from the remote server to be unmarshalled to the appropriate // response from the remote server to be unmarshalled to the appropriate type
// type and sent to the specified channel when it is received. // and sent to the specified channel when it is received.
// //
// If the client has already begun shutting down, ErrClientShutdown is returned // If the client has already begun shutting down, ErrClientShutdown is returned
// and the request is not added. // and the request is not added.
// //
// This function is safe for concurrent access. // This function is safe for concurrent access.
func (c *Client) addRequest(id uint64, request *jsonRequest) error { func (c *Client) addRequest(jReq *jsonRequest) error {
c.requestLock.Lock() c.requestLock.Lock()
defer c.requestLock.Unlock() defer c.requestLock.Unlock()
@ -187,9 +189,8 @@ func (c *Client) addRequest(id uint64, request *jsonRequest) error {
default: default:
} }
// TODO(davec): Already there? element := c.requestList.PushBack(jReq)
element := c.requestList.PushBack(request) c.requestMap[jReq.id] = element
c.requestMap[id] = element
return nil return nil
} }
@ -224,7 +225,7 @@ func (c *Client) removeAllRequests() {
// trackRegisteredNtfns examines the passed command to see if it is one of // trackRegisteredNtfns examines the passed command to see if it is one of
// the notification commands and updates the notification state that is used // the notification commands and updates the notification state that is used
// to automatically re-establish registered notifications on reconnects. // to automatically re-establish registered notifications on reconnects.
func (c *Client) trackRegisteredNtfns(cmd btcjson.Cmd) { func (c *Client) trackRegisteredNtfns(cmd interface{}) {
// Nothing to do if the caller is not interested in notifications. // Nothing to do if the caller is not interested in notifications.
if c.ntfnHandlers == nil { if c.ntfnHandlers == nil {
return return
@ -234,23 +235,23 @@ func (c *Client) trackRegisteredNtfns(cmd btcjson.Cmd) {
defer c.ntfnState.Unlock() defer c.ntfnState.Unlock()
switch bcmd := cmd.(type) { switch bcmd := cmd.(type) {
case *btcws.NotifyBlocksCmd: case *btcjson.NotifyBlocksCmd:
c.ntfnState.notifyBlocks = true c.ntfnState.notifyBlocks = true
case *btcws.NotifyNewTransactionsCmd: case *btcjson.NotifyNewTransactionsCmd:
if bcmd.Verbose { if bcmd.Verbose != nil && *bcmd.Verbose {
c.ntfnState.notifyNewTxVerbose = true c.ntfnState.notifyNewTxVerbose = true
} else { } else {
c.ntfnState.notifyNewTx = true c.ntfnState.notifyNewTx = true
} }
case *btcws.NotifySpentCmd: case *btcjson.NotifySpentCmd:
for _, op := range bcmd.OutPoints { for _, op := range bcmd.OutPoints {
c.ntfnState.notifySpent[op] = struct{}{} c.ntfnState.notifySpent[op] = struct{}{}
} }
case *btcws.NotifyReceivedCmd: case *btcjson.NotifyReceivedCmd:
for _, addr := range bcmd.Addresses { for _, addr := range bcmd.Addresses {
c.ntfnState.notifyReceived[addr] = struct{}{} c.ntfnState.notifyReceived[addr] = struct{}{}
} }
@ -278,8 +279,8 @@ type (
// rawResponse is a partially-unmarshaled JSON-RPC response. For this // 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. // to be valid (according to JSON-RPC 1.0 spec), ID may not be nil.
rawResponse struct { rawResponse struct {
Result json.RawMessage `json:"result"` Result json.RawMessage `json:"result"`
Error *btcjson.Error `json:"error"` Error *btcjson.RPCError `json:"error"`
} }
) )
@ -291,7 +292,7 @@ type response struct {
} }
// result checks whether the unmarshaled response contains a non-nil error, // result checks whether the unmarshaled response contains a non-nil error,
// returning an unmarshaled btcjson.Error (or an unmarshaling error) if so. // returning an unmarshaled btcjson.RPCError (or an unmarshaling error) if so.
// If the response is not an error, the raw bytes of the request are // If the response is not an error, the raw bytes of the request are
// returned for further unmashaling into specific result types. // returned for further unmashaling into specific result types.
func (r rawResponse) result() (result []byte, err error) { func (r rawResponse) result() (result []byte, err error) {
@ -303,7 +304,8 @@ func (r rawResponse) result() (result []byte, err error) {
// handleMessage is the main handler for incoming notifications and responses. // handleMessage is the main handler for incoming notifications and responses.
func (c *Client) handleMessage(msg []byte) { func (c *Client) handleMessage(msg []byte) {
// Attempt to unmarshal the message as either a notifiation or response. // Attempt to unmarshal the message as either a notification or
// response.
var in inMessage var in inMessage
err := json.Unmarshal(msg, &in) err := json.Unmarshal(msg, &in)
if err != nil { if err != nil {
@ -441,7 +443,7 @@ func (c *Client) sendMessage(marshalledJSON []byte) {
// reregisterNtfns creates and sends commands needed to re-establish the current // reregisterNtfns creates and sends commands needed to re-establish the current
// notification state associated with the client. It should only be called on // notification state associated with the client. It should only be called on
// on reconnect by the resendCmds function. // on reconnect by the resendRequests function.
func (c *Client) reregisterNtfns() error { func (c *Client) reregisterNtfns() error {
// Nothing to do if the caller is not interested in notifications. // Nothing to do if the caller is not interested in notifications.
if c.ntfnHandlers == nil { if c.ntfnHandlers == nil {
@ -480,7 +482,7 @@ func (c *Client) reregisterNtfns() error {
// outpoints in one command if needed. // outpoints in one command if needed.
nslen := len(stateCopy.notifySpent) nslen := len(stateCopy.notifySpent)
if nslen > 0 { if nslen > 0 {
outpoints := make([]btcws.OutPoint, 0, nslen) outpoints := make([]btcjson.OutPoint, 0, nslen)
for op := range stateCopy.notifySpent { for op := range stateCopy.notifySpent {
outpoints = append(outpoints, op) outpoints = append(outpoints, op)
} }
@ -513,10 +515,10 @@ var ignoreResends = map[string]struct{}{
"rescan": struct{}{}, "rescan": struct{}{},
} }
// resendCmds resends any commands that had not completed when the client // resendRequests resends any requests that had not completed when the client
// disconnected. It is intended to be called once the client has reconnected as // disconnected. It is intended to be called once the client has reconnected as
// a separate goroutine. // a separate goroutine.
func (c *Client) resendCmds() { func (c *Client) resendRequests() {
// Set the notification state back up. If anything goes wrong, // Set the notification state back up. If anything goes wrong,
// disconnect the client. // disconnect the client.
if err := c.reregisterNtfns(); err != nil { if err := c.reregisterNtfns(); err != nil {
@ -525,37 +527,39 @@ func (c *Client) resendCmds() {
return return
} }
// Since it's possible to block on send and more commands might be // Since it's possible to block on send and more requests might be
// added by the caller while resending, make a copy of all of the // added by the caller while resending, make a copy of all of the
// commands that need to be resent now and work from the copy. This // requests that need to be resent now and work from the copy. This
// also allows the lock to be released quickly. // also allows the lock to be released quickly.
c.requestLock.Lock() c.requestLock.Lock()
resendCmds := make([]*jsonRequest, 0, c.requestList.Len()) resendReqs := make([]*jsonRequest, 0, c.requestList.Len())
var nextElem *list.Element var nextElem *list.Element
for e := c.requestList.Front(); e != nil; e = nextElem { for e := c.requestList.Front(); e != nil; e = nextElem {
nextElem = e.Next() nextElem = e.Next()
req := e.Value.(*jsonRequest) jReq := e.Value.(*jsonRequest)
if _, ok := ignoreResends[req.cmd.Method()]; ok { if _, ok := ignoreResends[jReq.method]; ok {
// If a request is not sent on reconnect, remove it // If a request is not sent on reconnect, remove it
// from the request structures, since no reply is // from the request structures, since no reply is
// expected. // expected.
delete(c.requestMap, req.cmd.Id().(uint64)) delete(c.requestMap, jReq.id)
c.requestList.Remove(e) c.requestList.Remove(e)
} else { } else {
resendCmds = append(resendCmds, req) resendReqs = append(resendReqs, jReq)
} }
} }
c.requestLock.Unlock() c.requestLock.Unlock()
for _, req := range resendCmds { for _, jReq := range resendReqs {
// Stop resending commands if the client disconnected again // Stop resending commands if the client disconnected again
// since the next reconnect will handle them. // since the next reconnect will handle them.
if c.Disconnected() { if c.Disconnected() {
return return
} }
c.marshalAndSend(req.cmd, req.responseChan) log.Tracef("Sending command [%s] with id %d", jReq.method,
jReq.id)
c.sendMessage(jReq.marshalledJSON)
} }
} }
@ -624,9 +628,9 @@ out:
// new connection. // new connection.
c.start() c.start()
// Reissue pending commands in another goroutine since // Reissue pending requests in another goroutine since
// the send can block. // the send can block.
go c.resendCmds() go c.resendRequests()
// Break out of the reconnect loop back to wait for // Break out of the reconnect loop back to wait for
// disconnect again. // disconnect again.
@ -638,40 +642,41 @@ out:
} }
// handleSendPostMessage handles performing the passed HTTP request, reading the // handleSendPostMessage handles performing the passed HTTP request, reading the
// result, unmarshalling it, and delivering the unmarhsalled result to the // result, unmarshalling it, and delivering the unmarshalled result to the
// provided response channel. // provided response channel.
func (c *Client) handleSendPostMessage(details *sendPostDetails) { func (c *Client) handleSendPostMessage(details *sendPostDetails) {
// Post the request. jReq := details.jsonRequest
cmd := details.command log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id)
log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) httpResponse, err := c.httpClient.Do(details.httpRequest)
httpResponse, err := c.httpClient.Do(details.request)
if err != nil { if err != nil {
details.responseChan <- &response{err: err} jReq.responseChan <- &response{err: err}
return return
} }
// Read the raw bytes and close the response. // Read the raw bytes and close the response.
respBytes, err := btcjson.GetRaw(httpResponse.Body) respBytes, err := ioutil.ReadAll(httpResponse.Body)
httpResponse.Body.Close()
if err != nil { if err != nil {
details.responseChan <- &response{err: err} err = fmt.Errorf("error reading json reply: %v", err)
jReq.responseChan <- &response{err: err}
return return
} }
// Handle unsuccessful HTTP responses // Handle unsuccessful HTTP responses
if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 { if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 {
details.responseChan <- &response{err: errors.New(string(respBytes))} jReq.responseChan <- &response{err: errors.New(string(respBytes))}
return return
} }
var resp rawResponse var resp rawResponse
err = json.Unmarshal(respBytes, &resp) err = json.Unmarshal(respBytes, &resp)
if err != nil { if err != nil {
details.responseChan <- &response{err: err} jReq.responseChan <- &response{err: err}
return return
} }
res, err := resp.result() res, err := resp.result()
details.responseChan <- &response{result: res, err: err} jReq.responseChan <- &response{result: res, err: err}
} }
// sendPostHandler handles all outgoing messages when the client is running // sendPostHandler handles all outgoing messages when the client is running
@ -698,7 +703,7 @@ cleanup:
for { for {
select { select {
case details := <-c.sendPostChan: case details := <-c.sendPostChan:
details.responseChan <- &response{ details.jsonRequest.responseChan <- &response{
result: nil, result: nil,
err: ErrClientShutdown, err: ErrClientShutdown,
} }
@ -715,18 +720,17 @@ cleanup:
// sendPostRequest sends the passed HTTP request to the RPC server using the // sendPostRequest sends the passed HTTP request to the RPC server using the
// HTTP client associated with the client. It is backed by a buffered channel, // HTTP client associated with the client. It is backed by a buffered channel,
// so it will not block until the send channel is full. // so it will not block until the send channel is full.
func (c *Client) sendPostRequest(req *http.Request, command btcjson.Cmd, responseChan chan *response) { func (c *Client) sendPostRequest(httpReq *http.Request, jReq *jsonRequest) {
// Don't send the message if shutting down. // Don't send the message if shutting down.
select { select {
case <-c.shutdown: case <-c.shutdown:
responseChan <- &response{result: nil, err: ErrClientShutdown} jReq.responseChan <- &response{result: nil, err: ErrClientShutdown}
default: default:
} }
c.sendPostChan <- &sendPostDetails{ c.sendPostChan <- &sendPostDetails{
command: command, jsonRequest: jReq,
request: req, httpRequest: httpReq,
responseChan: responseChan,
} }
} }
@ -749,66 +753,45 @@ func receiveFuture(f chan *response) ([]byte, error) {
return r.result, r.err return r.result, r.err
} }
// marshalAndSendPost marshals the passed command to JSON-RPC and sends it to // sendPost sends the passed request to the server by issuing an HTTP POST
// the server by issuing an HTTP POST request and returns a response channel // request using the provided response channel for the reply. Typically a new
// on which the reply will be delivered. Typically a new connection is opened // connection is opened and closed for each command when using this method,
// and closed for each command when using this method, however, the underlying // however, the underlying HTTP client might coalesce multiple commands
// HTTP client might coalesce multiple commands depending on several factors // depending on several factors including the remote server configuration.
// including the remote server configuration. func (c *Client) sendPost(jReq *jsonRequest) {
func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *response) {
marshalledJSON, err := json.Marshal(cmd)
if err != nil {
responseChan <- &response{result: nil, err: err}
return
}
// Generate a request to the configured RPC server. // Generate a request to the configured RPC server.
protocol := "http" protocol := "http"
if !c.config.DisableTLS { if !c.config.DisableTLS {
protocol = "https" protocol = "https"
} }
url := protocol + "://" + c.config.Host url := protocol + "://" + c.config.Host
req, err := http.NewRequest("POST", url, bytes.NewReader(marshalledJSON)) bodyReader := bytes.NewReader(jReq.marshalledJSON)
httpReq, err := http.NewRequest("POST", url, bodyReader)
if err != nil { if err != nil {
responseChan <- &response{result: nil, err: err} jReq.responseChan <- &response{result: nil, err: err}
return return
} }
req.Close = true httpReq.Close = true
req.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Content-Type", "application/json")
// Configure basic access authorization. // Configure basic access authorization.
req.SetBasicAuth(c.config.User, c.config.Pass) httpReq.SetBasicAuth(c.config.User, c.config.Pass)
log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id)
c.sendPostRequest(req, cmd, responseChan) c.sendPostRequest(httpReq, jReq)
} }
// marshalAndSend marshals the passed command to JSON-RPC and sends it to the // sendRequest sends the passed json request to the associated server using the
// server. It returns a response channel on which the reply will be delivered. // provided response channel for the reply. It handles both websocket and HTTP
func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *response) { // POST mode depending on the configuration of the client.
marshalledJSON, err := cmd.MarshalJSON() func (c *Client) sendRequest(jReq *jsonRequest) {
if err != nil {
responseChan <- &response{result: nil, err: err}
return
}
log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id())
c.sendMessage(marshalledJSON)
}
// sendCmd sends the passed command to the associated server and returns a
// response channel on which the reply will be deliver at some point in the
// future. It handles both websocket and HTTP POST mode depending on the
// configuration of the client.
func (c *Client) sendCmd(cmd btcjson.Cmd) chan *response {
// Choose which marshal and send function to use depending on whether // Choose which marshal and send function to use depending on whether
// the client running in HTTP POST mode or not. When running in HTTP // the client running in HTTP POST mode or not. When running in HTTP
// POST mode, the command is issued via an HTTP client. Otherwise, // POST mode, the command is issued via an HTTP client. Otherwise,
// the command is issued via the asynchronous websocket channels. // the command is issued via the asynchronous websocket channels.
responseChan := make(chan *response, 1)
if c.config.HttpPostMode { if c.config.HttpPostMode {
c.marshalAndSendPost(cmd, responseChan) c.sendPost(jReq)
return responseChan return
} }
// Check whether the websocket connection has never been established, // Check whether the websocket connection has never been established,
@ -816,26 +799,58 @@ func (c *Client) sendCmd(cmd btcjson.Cmd) chan *response {
select { select {
case <-c.connEstablished: case <-c.connEstablished:
default: default:
responseChan <- &response{err: ErrClientNotConnected} jReq.responseChan <- &response{err: ErrClientNotConnected}
return responseChan return
} }
err := c.addRequest(cmd.Id().(uint64), &jsonRequest{ // Add the request to the internal tracking map so the response from the
cmd: cmd, // remote server can be properly detected and routed to the response
responseChan: responseChan, // channel. Then send the marshalled request via the websocket
}) // connection.
if err != nil { if err := c.addRequest(jReq); err != nil {
responseChan <- &response{err: err} jReq.responseChan <- &response{err: err}
return responseChan return
} }
c.marshalAndSend(cmd, responseChan) log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id)
c.sendMessage(jReq.marshalledJSON)
}
// sendCmd sends the passed command to the associated server and returns a
// response channel on which the reply will be delivered at some point in the
// future. It handles both websocket and HTTP POST mode depending on the
// configuration of the client.
func (c *Client) sendCmd(cmd interface{}) chan *response {
// Get the method associated with the command.
method, err := btcjson.CmdMethod(cmd)
if err != nil {
return newFutureError(err)
}
// Marshal the command.
id := c.NextID()
marshalledJSON, err := btcjson.MarshalCmd(id, cmd)
if err != nil {
return newFutureError(err)
}
// Generate the request and send it along with a channel to respond on.
responseChan := make(chan *response, 1)
jReq := &jsonRequest{
id: id,
method: method,
cmd: cmd,
marshalledJSON: marshalledJSON,
responseChan: responseChan,
}
c.sendRequest(jReq)
return responseChan return responseChan
} }
// sendCmdAndWait sends the passed command to the associated server, waits // sendCmdAndWait sends the passed command to the associated server, waits
// for the reply, and returns the result from it. It will return the error // for the reply, and returns the result from it. It will return the error
// field in the reply if there is one. // field in the reply if there is one.
func (c *Client) sendCmdAndWait(cmd btcjson.Cmd) (interface{}, error) { func (c *Client) sendCmdAndWait(cmd interface{}) (interface{}, error) {
// Marshal the command to JSON-RPC, send it to the connected server, and // Marshal the command to JSON-RPC, send it to the connected server, and
// wait for a response on the returned channel. // wait for a response on the returned channel.
return receiveFuture(c.sendCmd(cmd)) return receiveFuture(c.sendCmd(cmd))

View file

@ -9,7 +9,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcjson/v2/btcjson"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
) )
@ -41,12 +41,7 @@ func (r FutureGetGenerateResult) Receive() (bool, error) {
// //
// See GetGenerate for the blocking version and more details. // See GetGenerate for the blocking version and more details.
func (c *Client) GetGenerateAsync() FutureGetGenerateResult { func (c *Client) GetGenerateAsync() FutureGetGenerateResult {
id := c.NextID() cmd := btcjson.NewGetGenerateCmd()
cmd, err := btcjson.NewGetGenerateCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -76,12 +71,7 @@ func (r FutureSetGenerateResult) Receive() error {
// //
// See SetGenerate for the blocking version and more details. // See SetGenerate for the blocking version and more details.
func (c *Client) SetGenerateAsync(enable bool, numCPUs int) FutureSetGenerateResult { func (c *Client) SetGenerateAsync(enable bool, numCPUs int) FutureSetGenerateResult {
id := c.NextID() cmd := btcjson.NewSetGenerateCmd(enable, &numCPUs)
cmd, err := btcjson.NewSetGenerateCmd(id, enable, numCPUs)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -119,12 +109,7 @@ func (r FutureGetHashesPerSecResult) Receive() (int64, error) {
// //
// See GetHashesPerSec for the blocking version and more details. // See GetHashesPerSec for the blocking version and more details.
func (c *Client) GetHashesPerSecAsync() FutureGetHashesPerSecResult { func (c *Client) GetHashesPerSecAsync() FutureGetHashesPerSecResult {
id := c.NextID() cmd := btcjson.NewGetHashesPerSecCmd()
cmd, err := btcjson.NewGetHashesPerSecCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -163,12 +148,7 @@ func (r FutureGetMiningInfoResult) Receive() (*btcjson.GetMiningInfoResult, erro
// //
// See GetMiningInfo for the blocking version and more details. // See GetMiningInfo for the blocking version and more details.
func (c *Client) GetMiningInfoAsync() FutureGetMiningInfoResult { func (c *Client) GetMiningInfoAsync() FutureGetMiningInfoResult {
id := c.NextID() cmd := btcjson.NewGetMiningInfoCmd()
cmd, err := btcjson.NewGetMiningInfoCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -206,12 +186,7 @@ func (r FutureGetNetworkHashPS) Receive() (int64, error) {
// //
// See GetNetworkHashPS for the blocking version and more details. // See GetNetworkHashPS for the blocking version and more details.
func (c *Client) GetNetworkHashPSAsync() FutureGetNetworkHashPS { func (c *Client) GetNetworkHashPSAsync() FutureGetNetworkHashPS {
id := c.NextID() cmd := btcjson.NewGetNetworkHashPSCmd(nil, nil)
cmd, err := btcjson.NewGetNetworkHashPSCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -230,12 +205,7 @@ func (c *Client) GetNetworkHashPS() (int64, error) {
// //
// See GetNetworkHashPS2 for the blocking version and more details. // See GetNetworkHashPS2 for the blocking version and more details.
func (c *Client) GetNetworkHashPS2Async(blocks int) FutureGetNetworkHashPS { func (c *Client) GetNetworkHashPS2Async(blocks int) FutureGetNetworkHashPS {
id := c.NextID() cmd := btcjson.NewGetNetworkHashPSCmd(&blocks, nil)
cmd, err := btcjson.NewGetNetworkHashPSCmd(id, blocks)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -256,12 +226,7 @@ func (c *Client) GetNetworkHashPS2(blocks int) (int64, error) {
// //
// See GetNetworkHashPS3 for the blocking version and more details. // See GetNetworkHashPS3 for the blocking version and more details.
func (c *Client) GetNetworkHashPS3Async(blocks, height int) FutureGetNetworkHashPS { func (c *Client) GetNetworkHashPS3Async(blocks, height int) FutureGetNetworkHashPS {
id := c.NextID() cmd := btcjson.NewGetNetworkHashPSCmd(&blocks, &height)
cmd, err := btcjson.NewGetNetworkHashPSCmd(id, blocks, height)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -303,12 +268,7 @@ func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) {
// //
// See GetWork for the blocking version and more details. // See GetWork for the blocking version and more details.
func (c *Client) GetWorkAsync() FutureGetWork { func (c *Client) GetWorkAsync() FutureGetWork {
id := c.NextID() cmd := btcjson.NewGetWorkCmd(nil)
cmd, err := btcjson.NewGetWorkCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -347,12 +307,7 @@ func (r FutureGetWorkSubmit) Receive() (bool, error) {
// //
// See GetWorkSubmit for the blocking version and more details. // See GetWorkSubmit for the blocking version and more details.
func (c *Client) GetWorkSubmitAsync(data string) FutureGetWorkSubmit { func (c *Client) GetWorkSubmitAsync(data string) FutureGetWorkSubmit {
id := c.NextID() cmd := btcjson.NewGetWorkCmd(&data)
cmd, err := btcjson.NewGetWorkCmd(id, data)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -406,12 +361,7 @@ func (c *Client) SubmitBlockAsync(block *btcutil.Block, options *btcjson.SubmitB
blockHex = hex.EncodeToString(blockBytes) blockHex = hex.EncodeToString(blockBytes)
} }
id := c.NextID() cmd := btcjson.NewSubmitBlockCmd(blockHex, options)
cmd, err := btcjson.NewSubmitBlockCmd(id, blockHex, options)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }

51
net.go
View file

@ -7,7 +7,7 @@ package btcrpcclient
import ( import (
"encoding/json" "encoding/json"
"github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcjson/v2/btcjson"
) )
// AddNodeCommand enumerates the available commands that the AddNode function // AddNodeCommand enumerates the available commands that the AddNode function
@ -54,12 +54,7 @@ func (r FutureAddNodeResult) Receive() error {
// //
// See AddNode for the blocking version and more details. // See AddNode for the blocking version and more details.
func (c *Client) AddNodeAsync(host string, command AddNodeCommand) FutureAddNodeResult { func (c *Client) AddNodeAsync(host string, command AddNodeCommand) FutureAddNodeResult {
id := c.NextID() cmd := btcjson.NewAddNodeCmd(host, btcjson.AddNodeSubCmd(command))
cmd, err := btcjson.NewAddNodeCmd(id, host, string(command))
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -100,12 +95,7 @@ func (r FutureGetAddedNodeInfoResult) Receive() ([]btcjson.GetAddedNodeInfoResul
// //
// See GetAddedNodeInfo for the blocking version and more details. // See GetAddedNodeInfo for the blocking version and more details.
func (c *Client) GetAddedNodeInfoAsync(peer string) FutureGetAddedNodeInfoResult { func (c *Client) GetAddedNodeInfoAsync(peer string) FutureGetAddedNodeInfoResult {
id := c.NextID() cmd := btcjson.NewGetAddedNodeInfoCmd(true, &peer)
cmd, err := btcjson.NewGetAddedNodeInfoCmd(id, true, peer)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -145,12 +135,7 @@ func (r FutureGetAddedNodeInfoNoDNSResult) Receive() ([]string, error) {
// //
// See GetAddedNodeInfoNoDNS for the blocking version and more details. // See GetAddedNodeInfoNoDNS for the blocking version and more details.
func (c *Client) GetAddedNodeInfoNoDNSAsync(peer string) FutureGetAddedNodeInfoNoDNSResult { func (c *Client) GetAddedNodeInfoNoDNSAsync(peer string) FutureGetAddedNodeInfoNoDNSResult {
id := c.NextID() cmd := btcjson.NewGetAddedNodeInfoCmd(false, &peer)
cmd, err := btcjson.NewGetAddedNodeInfoCmd(id, false, peer)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -191,12 +176,7 @@ func (r FutureGetConnectionCountResult) Receive() (int64, error) {
// //
// See GetConnectionCount for the blocking version and more details. // See GetConnectionCount for the blocking version and more details.
func (c *Client) GetConnectionCountAsync() FutureGetConnectionCountResult { func (c *Client) GetConnectionCountAsync() FutureGetConnectionCountResult {
id := c.NextID() cmd := btcjson.NewGetConnectionCountCmd()
cmd, err := btcjson.NewGetConnectionCountCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -226,12 +206,7 @@ func (r FuturePingResult) Receive() error {
// //
// See Ping for the blocking version and more details. // See Ping for the blocking version and more details.
func (c *Client) PingAsync() FuturePingResult { func (c *Client) PingAsync() FuturePingResult {
id := c.NextID() cmd := btcjson.NewPingCmd()
cmd, err := btcjson.NewPingCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -271,12 +246,7 @@ func (r FutureGetPeerInfoResult) Receive() ([]btcjson.GetPeerInfoResult, error)
// //
// See GetPeerInfo for the blocking version and more details. // See GetPeerInfo for the blocking version and more details.
func (c *Client) GetPeerInfoAsync() FutureGetPeerInfoResult { func (c *Client) GetPeerInfoAsync() FutureGetPeerInfoResult {
id := c.NextID() cmd := btcjson.NewGetPeerInfoCmd()
cmd, err := btcjson.NewGetPeerInfoCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -313,12 +283,7 @@ func (r FutureGetNetTotalsResult) Receive() (*btcjson.GetNetTotalsResult, error)
// //
// See GetNetTotals for the blocking version and more details. // See GetNetTotals for the blocking version and more details.
func (c *Client) GetNetTotalsAsync() FutureGetNetTotalsResult { func (c *Client) GetNetTotalsAsync() FutureGetNetTotalsResult {
id := c.NextID() cmd := btcjson.NewGetNetTotalsCmd()
cmd, err := btcjson.NewGetNetTotalsCmd(id)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }

100
notify.go
View file

@ -13,8 +13,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcjson/v2/btcjson"
"github.com/btcsuite/btcd/btcjson/btcws"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
) )
@ -37,7 +36,7 @@ type notificationState struct {
notifyNewTx bool notifyNewTx bool
notifyNewTxVerbose bool notifyNewTxVerbose bool
notifyReceived map[string]struct{} notifyReceived map[string]struct{}
notifySpent map[btcws.OutPoint]struct{} notifySpent map[btcjson.OutPoint]struct{}
} }
// Copy returns a deep copy of the receiver. // Copy returns a deep copy of the receiver.
@ -52,7 +51,7 @@ func (s *notificationState) Copy() *notificationState {
for addr := range s.notifyReceived { for addr := range s.notifyReceived {
stateCopy.notifyReceived[addr] = struct{}{} stateCopy.notifyReceived[addr] = struct{}{}
} }
stateCopy.notifySpent = make(map[btcws.OutPoint]struct{}) stateCopy.notifySpent = make(map[btcjson.OutPoint]struct{})
for op := range s.notifySpent { for op := range s.notifySpent {
stateCopy.notifySpent[op] = struct{}{} stateCopy.notifySpent[op] = struct{}{}
} }
@ -64,7 +63,7 @@ func (s *notificationState) Copy() *notificationState {
func newNotificationState() *notificationState { func newNotificationState() *notificationState {
return &notificationState{ return &notificationState{
notifyReceived: make(map[string]struct{}), notifyReceived: make(map[string]struct{}),
notifySpent: make(map[btcws.OutPoint]struct{}), notifySpent: make(map[btcjson.OutPoint]struct{}),
} }
} }
@ -110,7 +109,7 @@ type NotificationHandlers struct {
// connected to the longest (best) chain. It will only be invoked if a // connected to the longest (best) chain. It will only be invoked if a
// preceding call to NotifyReceived, Rescan, or RescanEndHeight has been // preceding call to NotifyReceived, Rescan, or RescanEndHeight has been
// made to register for the notification and the function is non-nil. // made to register for the notification and the function is non-nil.
OnRecvTx func(transaction *btcutil.Tx, details *btcws.BlockDetails) OnRecvTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails)
// OnRedeemingTx is invoked when a transaction that spends a registered // OnRedeemingTx is invoked when a transaction that spends a registered
// outpoint is received into the memory pool and also connected to the // outpoint is received into the memory pool and also connected to the
@ -122,7 +121,7 @@ type NotificationHandlers struct {
// for the outpoints that are now "owned" as a result of receiving // for the outpoints that are now "owned" as a result of receiving
// funds to the registered addresses. This means it is possible for // funds to the registered addresses. This means it is possible for
// this to invoked indirectly as the result of a NotifyReceived call. // this to invoked indirectly as the result of a NotifyReceived call.
OnRedeemingTx func(transaction *btcutil.Tx, details *btcws.BlockDetails) OnRedeemingTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails)
// OnRescanFinished is invoked after a rescan finishes due to a previous // OnRescanFinished is invoked after a rescan finishes due to a previous
// call to Rescan or RescanEndHeight. Finished rescans should be // call to Rescan or RescanEndHeight. Finished rescans should be
@ -188,7 +187,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
switch ntfn.Method { switch ntfn.Method {
// OnBlockConnected // OnBlockConnected
case btcws.BlockConnectedNtfnMethod: case btcjson.BlockConnectedNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnBlockConnected == nil { if c.ntfnHandlers.OnBlockConnected == nil {
@ -205,7 +204,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnBlockConnected(blockSha, blockHeight) c.ntfnHandlers.OnBlockConnected(blockSha, blockHeight)
// OnBlockDisconnected // OnBlockDisconnected
case btcws.BlockDisconnectedNtfnMethod: case btcjson.BlockDisconnectedNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnBlockDisconnected == nil { if c.ntfnHandlers.OnBlockDisconnected == nil {
@ -222,7 +221,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnBlockDisconnected(blockSha, blockHeight) c.ntfnHandlers.OnBlockDisconnected(blockSha, blockHeight)
// OnRecvTx // OnRecvTx
case btcws.RecvTxNtfnMethod: case btcjson.RecvTxNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnRecvTx == nil { if c.ntfnHandlers.OnRecvTx == nil {
@ -239,7 +238,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnRecvTx(tx, block) c.ntfnHandlers.OnRecvTx(tx, block)
// OnRedeemingTx // OnRedeemingTx
case btcws.RedeemingTxNtfnMethod: case btcjson.RedeemingTxNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnRedeemingTx == nil { if c.ntfnHandlers.OnRedeemingTx == nil {
@ -256,7 +255,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnRedeemingTx(tx, block) c.ntfnHandlers.OnRedeemingTx(tx, block)
// OnRescanFinished // OnRescanFinished
case btcws.RescanFinishedNtfnMethod: case btcjson.RescanFinishedNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnRescanFinished == nil { if c.ntfnHandlers.OnRescanFinished == nil {
@ -273,7 +272,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnRescanFinished(hash, height, blkTime) c.ntfnHandlers.OnRescanFinished(hash, height, blkTime)
// OnRescanProgress // OnRescanProgress
case btcws.RescanProgressNtfnMethod: case btcjson.RescanProgressNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnRescanProgress == nil { if c.ntfnHandlers.OnRescanProgress == nil {
@ -290,7 +289,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnRescanProgress(hash, height, blkTime) c.ntfnHandlers.OnRescanProgress(hash, height, blkTime)
// OnTxAccepted // OnTxAccepted
case btcws.TxAcceptedNtfnMethod: case btcjson.TxAcceptedNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnTxAccepted == nil { if c.ntfnHandlers.OnTxAccepted == nil {
@ -307,7 +306,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnTxAccepted(hash, amt) c.ntfnHandlers.OnTxAccepted(hash, amt)
// OnTxAcceptedVerbose // OnTxAcceptedVerbose
case btcws.TxAcceptedVerboseNtfnMethod: case btcjson.TxAcceptedVerboseNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnTxAcceptedVerbose == nil { if c.ntfnHandlers.OnTxAcceptedVerbose == nil {
@ -324,7 +323,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnTxAcceptedVerbose(rawTx) c.ntfnHandlers.OnTxAcceptedVerbose(rawTx)
// OnBtcdConnected // OnBtcdConnected
case btcws.BtcdConnectedNtfnMethod: case btcjson.BtcdConnectedNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnBtcdConnected == nil { if c.ntfnHandlers.OnBtcdConnected == nil {
@ -341,7 +340,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnBtcdConnected(connected) c.ntfnHandlers.OnBtcdConnected(connected)
// OnAccountBalance // OnAccountBalance
case btcws.AccountBalanceNtfnMethod: case btcjson.AccountBalanceNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnAccountBalance == nil { if c.ntfnHandlers.OnAccountBalance == nil {
@ -358,7 +357,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnAccountBalance(account, bal, conf) c.ntfnHandlers.OnAccountBalance(account, bal, conf)
// OnWalletLockState // OnWalletLockState
case btcws.WalletLockStateNtfnMethod: case btcjson.WalletLockStateNtfnMethod:
// Ignore the notification if the client is not interested in // Ignore the notification if the client is not interested in
// it. // it.
if c.ntfnHandlers.OnWalletLockState == nil { if c.ntfnHandlers.OnWalletLockState == nil {
@ -433,7 +432,7 @@ func parseChainNtfnParams(params []json.RawMessage) (*wire.ShaHash,
// the block it's mined in from the parameters of recvtx and redeemingtx // the block it's mined in from the parameters of recvtx and redeemingtx
// notifications. // notifications.
func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx,
*btcws.BlockDetails, error) { *btcjson.BlockDetails, error) {
if len(params) == 0 || len(params) > 2 { if len(params) == 0 || len(params) > 2 {
return nil, nil, wrongNumParams(len(params)) return nil, nil, wrongNumParams(len(params))
@ -448,7 +447,7 @@ func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx,
// If present, unmarshal second optional parameter as the block details // If present, unmarshal second optional parameter as the block details
// JSON object. // JSON object.
var block *btcws.BlockDetails var block *btcjson.BlockDetails
if len(params) > 1 { if len(params) > 1 {
err = json.Unmarshal(params[1], &block) err = json.Unmarshal(params[1], &block)
if err != nil { if err != nil {
@ -558,7 +557,7 @@ func parseTxAcceptedVerboseNtfnParams(params []json.RawMessage) (*btcjson.TxRawR
return nil, err return nil, err
} }
// TODO: change txacceptedverbose notifiation callbacks to use nicer // TODO: change txacceptedverbose notification callbacks to use nicer
// types for all details about the transaction (i.e. decoding hashes // types for all details about the transaction (i.e. decoding hashes
// from their string encoding). // from their string encoding).
return &rawTx, nil return &rawTx, nil
@ -611,7 +610,7 @@ func parseAccountBalanceNtfnParams(params []json.RawMessage) (account string,
} }
// Bounds check amount. // Bounds check amount.
bal, err := btcjson.JSONToAmount(fbal) bal, err := btcutil.NewAmount(fbal)
if err != nil { if err != nil {
return "", 0, false, err return "", 0, false, err
} }
@ -677,9 +676,7 @@ func (c *Client) NotifyBlocksAsync() FutureNotifyBlocksResult {
return newNilFutureResult() return newNilFutureResult()
} }
id := c.NextID() cmd := btcjson.NewNotifyBlocksCmd()
cmd := btcws.NewNotifyBlocksCmd(id)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -715,7 +712,7 @@ func (r FutureNotifySpentResult) Receive() error {
// notifySpentInternal is the same as notifySpentAsync except it accepts // notifySpentInternal is the same as notifySpentAsync except it accepts
// the converted outpoints as a parameter so the client can more efficiently // the converted outpoints as a parameter so the client can more efficiently
// recreate the previous notification state on reconnect. // recreate the previous notification state on reconnect.
func (c *Client) notifySpentInternal(outpoints []btcws.OutPoint) FutureNotifySpentResult { func (c *Client) notifySpentInternal(outpoints []btcjson.OutPoint) FutureNotifySpentResult {
// Not supported in HTTP POST mode. // Not supported in HTTP POST mode.
if c.config.HttpPostMode { if c.config.HttpPostMode {
return newFutureError(ErrNotificationsNotSupported) return newFutureError(ErrNotificationsNotSupported)
@ -727,9 +724,7 @@ func (c *Client) notifySpentInternal(outpoints []btcws.OutPoint) FutureNotifySpe
return newNilFutureResult() return newNilFutureResult()
} }
id := c.NextID() cmd := btcjson.NewNotifySpentCmd(outpoints)
cmd := btcws.NewNotifySpentCmd(id, outpoints)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -752,13 +747,11 @@ func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentR
return newNilFutureResult() return newNilFutureResult()
} }
id := c.NextID() ops := make([]btcjson.OutPoint, 0, len(outpoints))
ops := make([]btcws.OutPoint, 0, len(outpoints))
for _, outpoint := range outpoints { for _, outpoint := range outpoints {
ops = append(ops, *btcws.NewOutPointFromWire(outpoint)) ops = append(ops, *btcjson.NewOutPointFromWire(outpoint))
} }
cmd := btcws.NewNotifySpentCmd(id, ops) cmd := btcjson.NewNotifySpentCmd(ops)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -810,12 +803,7 @@ func (c *Client) NotifyNewTransactionsAsync(verbose bool) FutureNotifyNewTransac
return newNilFutureResult() return newNilFutureResult()
} }
id := c.NextID() cmd := btcjson.NewNotifyNewTransactionsCmd(&verbose)
cmd, err := btcws.NewNotifyNewTransactionsCmd(id, verbose)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -865,9 +853,7 @@ func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceived
} }
// Convert addresses to strings. // Convert addresses to strings.
id := c.NextID() cmd := btcjson.NewNotifyReceivedCmd(addresses)
cmd := btcws.NewNotifyReceivedCmd(id, addresses)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -895,9 +881,7 @@ func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyRe
for _, addr := range addresses { for _, addr := range addresses {
addrs = append(addrs, addr.String()) addrs = append(addrs, addr.String())
} }
id := c.NextID() cmd := btcjson.NewNotifyReceivedCmd(addrs)
cmd := btcws.NewNotifyReceivedCmd(id, addrs)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -978,17 +962,12 @@ func (c *Client) RescanAsync(startBlock *wire.ShaHash,
} }
// Convert outpoints. // Convert outpoints.
ops := make([]btcws.OutPoint, 0, len(outpoints)) ops := make([]btcjson.OutPoint, 0, len(outpoints))
for _, op := range outpoints { for _, op := range outpoints {
ops = append(ops, *btcws.NewOutPointFromWire(op)) ops = append(ops, *btcjson.NewOutPointFromWire(op))
}
id := c.NextID()
cmd, err := btcws.NewRescanCmd(id, startBlockShaStr, addrs, ops)
if err != nil {
return newFutureError(err)
} }
cmd := btcjson.NewRescanCmd(startBlockShaStr, addrs, ops, nil)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -1063,18 +1042,13 @@ func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash,
} }
// Convert outpoints. // Convert outpoints.
ops := make([]btcws.OutPoint, 0, len(outpoints)) ops := make([]btcjson.OutPoint, 0, len(outpoints))
for _, op := range outpoints { for _, op := range outpoints {
ops = append(ops, *btcws.NewOutPointFromWire(op)) ops = append(ops, *btcjson.NewOutPointFromWire(op))
}
id := c.NextID()
cmd, err := btcws.NewRescanCmd(id, startBlockShaStr, addrs, ops,
endBlockShaStr)
if err != nil {
return newFutureError(err)
} }
cmd := btcjson.NewRescanCmd(startBlockShaStr, addrs, ops,
&endBlockShaStr)
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }

View file

@ -8,40 +8,9 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcjson/v2/btcjson"
) )
// rawRequest satisifies the btcjson.Cmd interface for btcjson raw commands.
// This type exists here rather than making btcjson.RawCmd satisify the Cmd
// interface due to conflict between the Id and Method field names vs method
// names.
type rawRequest struct {
btcjson.RawCmd
}
// Enforce that rawRequest is a btcjson.Cmd.
var _ btcjson.Cmd = &rawRequest{}
// Id returns the JSON-RPC id of the request.
func (r *rawRequest) Id() interface{} {
return r.RawCmd.Id
}
// Method returns the method string of the request.
func (r *rawRequest) Method() string {
return r.RawCmd.Method
}
// MarshalJSON marshals the raw request as a JSON-RPC 1.0 object.
func (r *rawRequest) MarshalJSON() ([]byte, error) {
return json.Marshal(r.RawCmd)
}
// UnmarshalJSON unmarshals a JSON-RPC 1.0 object into the raw request.
func (r *rawRequest) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, &r.RawCmd)
}
// FutureRawResult is a future promise to deliver the result of a RawRequest RPC // FutureRawResult is a future promise to deliver the result of a RawRequest RPC
// invocation (or an applicable error). // invocation (or an applicable error).
type FutureRawResult chan *response type FutureRawResult chan *response
@ -69,16 +38,34 @@ func (c *Client) RawRequestAsync(method string, params []json.RawMessage) Future
params = []json.RawMessage{} params = []json.RawMessage{}
} }
cmd := &rawRequest{ // Create a raw JSON-RPC request using the provided method and params
RawCmd: btcjson.RawCmd{ // and marshal it. This is done rather than using the sendCmd function
Jsonrpc: "1.0", // since that relies on marshalling registered btcjson commands rather
Id: c.NextID(), // than custom commands.
Method: method, id := c.NextID()
Params: params, rawRequest := &btcjson.Request{
}, Jsonrpc: "1.0",
ID: id,
Method: method,
Params: params,
}
marshalledJSON, err := json.Marshal(rawRequest)
if err != nil {
return newFutureError(err)
} }
return c.sendCmd(cmd) // Generate the request and send it along with a channel to respond on.
responseChan := make(chan *response, 1)
jReq := &jsonRequest{
id: id,
method: method,
cmd: nil,
marshalledJSON: marshalledJSON,
responseChan: responseChan,
}
c.sendRequest(jReq)
return responseChan
} }
// RawRequest allows the caller to send a raw or custom request to the server. // RawRequest allows the caller to send a raw or custom request to the server.

View file

@ -9,7 +9,7 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/btcjson/v2/btcjson"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
) )
@ -101,12 +101,7 @@ func (c *Client) GetRawTransactionAsync(txHash *wire.ShaHash) FutureGetRawTransa
hash = txHash.String() hash = txHash.String()
} }
id := c.NextID() cmd := btcjson.NewGetRawTransactionCmd(hash, btcjson.Int(0))
cmd, err := btcjson.NewGetRawTransactionCmd(id, hash, 0)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -152,12 +147,7 @@ func (c *Client) GetRawTransactionVerboseAsync(txHash *wire.ShaHash) FutureGetRa
hash = txHash.String() hash = txHash.String()
} }
id := c.NextID() cmd := btcjson.NewGetRawTransactionCmd(hash, btcjson.Int(1))
cmd, err := btcjson.NewGetRawTransactionCmd(id, hash, 1)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -197,13 +187,8 @@ func (r FutureDecodeRawTransactionResult) Receive() (*btcjson.TxRawResult, error
// //
// See DecodeRawTransaction for the blocking version and more details. // See DecodeRawTransaction for the blocking version and more details.
func (c *Client) DecodeRawTransactionAsync(serializedTx []byte) FutureDecodeRawTransactionResult { func (c *Client) DecodeRawTransactionAsync(serializedTx []byte) FutureDecodeRawTransactionResult {
id := c.NextID()
txHex := hex.EncodeToString(serializedTx) txHex := hex.EncodeToString(serializedTx)
cmd, err := btcjson.NewDecodeRawTransactionCmd(id, txHex) cmd := btcjson.NewDecodeRawTransactionCmd(txHex)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -255,16 +240,11 @@ func (r FutureCreateRawTransactionResult) Receive() (*wire.MsgTx, error) {
func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput,
amounts map[btcutil.Address]btcutil.Amount) FutureCreateRawTransactionResult { amounts map[btcutil.Address]btcutil.Amount) FutureCreateRawTransactionResult {
id := c.NextID() convertedAmts := make(map[string]float64, len(amounts))
convertedAmts := make(map[string]int64, len(amounts))
for addr, amount := range amounts { for addr, amount := range amounts {
convertedAmts[addr.String()] = int64(amount) convertedAmts[addr.String()] = amount.ToBTC()
} }
cmd, err := btcjson.NewCreateRawTransactionCmd(id, inputs, convertedAmts) cmd := btcjson.NewCreateRawTransactionCmd(inputs, convertedAmts)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -315,12 +295,7 @@ func (c *Client) SendRawTransactionAsync(tx *wire.MsgTx, allowHighFees bool) Fut
txHex = hex.EncodeToString(buf.Bytes()) txHex = hex.EncodeToString(buf.Bytes())
} }
id := c.NextID() cmd := btcjson.NewSendRawTransactionCmd(txHex, &allowHighFees)
cmd, err := btcjson.NewSendRawTransactionCmd(id, txHex, allowHighFees)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -381,12 +356,7 @@ func (c *Client) SignRawTransactionAsync(tx *wire.MsgTx) FutureSignRawTransactio
txHex = hex.EncodeToString(buf.Bytes()) txHex = hex.EncodeToString(buf.Bytes())
} }
id := c.NextID() cmd := btcjson.NewSignRawTransactionCmd(txHex, nil, nil, nil)
cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -417,12 +387,7 @@ func (c *Client) SignRawTransaction2Async(tx *wire.MsgTx, inputs []btcjson.RawTx
txHex = hex.EncodeToString(buf.Bytes()) txHex = hex.EncodeToString(buf.Bytes())
} }
id := c.NextID() cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, nil, nil)
cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -459,13 +424,8 @@ func (c *Client) SignRawTransaction3Async(tx *wire.MsgTx,
txHex = hex.EncodeToString(buf.Bytes()) txHex = hex.EncodeToString(buf.Bytes())
} }
id := c.NextID() cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, &privKeysWIF,
cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, nil)
privKeysWIF)
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }
@ -512,13 +472,8 @@ func (c *Client) SignRawTransaction4Async(tx *wire.MsgTx,
txHex = hex.EncodeToString(buf.Bytes()) txHex = hex.EncodeToString(buf.Bytes())
} }
id := c.NextID() cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, &privKeysWIF,
cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, btcjson.String(string(hashType)))
privKeysWIF, string(hashType))
if err != nil {
return newFutureError(err)
}
return c.sendCmd(cmd) return c.sendCmd(cmd)
} }

515
wallet.go

File diff suppressed because it is too large Load diff