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

6
doc.go
View file

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

View file

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

View file

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

View file

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

51
net.go
View file

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

100
notify.go
View file

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

View file

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

View file

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

515
wallet.go

File diff suppressed because it is too large Load diff