diff --git a/glide.lock b/glide.lock index 5430e676..a74b30ac 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,8 @@ -hash: 9e111ebb6989c8b23db0ef5c0523e8dde1ccaa964fa7a42faccbb7a6a60b2860 -updated: 2017-06-19T16:45:22.5258215-04:00 +hash: 976decfaf173d97d2e4572399490637aa12e217312a7d8b28813780a738e1151 +updated: 2017-08-15T20:07:19.2012575-05:00 imports: - name: github.com/btcsuite/btclog version: 96c2a91a67da03552a5e6554fe3ccbfbc7f860be -- name: github.com/btcsuite/btcrpcclient - version: 45b9cb481d2aead4e80aab32d7aa86db386430ab - name: github.com/btcsuite/btcutil version: 501929d3d046174c3d39f0ea54ece471aa17238c subpackages: diff --git a/glide.yaml b/glide.yaml index c4c0c425..f42ad703 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,7 +1,6 @@ package: github.com/btcsuite/btcd import: - package: github.com/btcsuite/btclog -- package: github.com/btcsuite/btcrpcclient - package: github.com/btcsuite/btcutil subpackages: - bloom diff --git a/integration/rpctest/memwallet.go b/integration/rpctest/memwallet.go index 10471d38..9b2999f3 100644 --- a/integration/rpctest/memwallet.go +++ b/integration/rpctest/memwallet.go @@ -14,9 +14,9 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcrpcclient" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil/hdkeychain" ) @@ -102,7 +102,7 @@ type memWallet struct { net *chaincfg.Params - rpc *btcrpcclient.Client + rpc *rpcclient.Client sync.RWMutex } @@ -171,7 +171,7 @@ func (m *memWallet) SyncedHeight() int32 { // SetRPCClient saves the passed rpc connection to btcd as the wallet's // personal rpc connection. -func (m *memWallet) SetRPCClient(rpcClient *btcrpcclient.Client) { +func (m *memWallet) SetRPCClient(rpcClient *rpcclient.Client) { m.rpc = rpcClient } diff --git a/integration/rpctest/node.go b/integration/rpctest/node.go index 6554824c..f56a088a 100644 --- a/integration/rpctest/node.go +++ b/integration/rpctest/node.go @@ -14,7 +14,7 @@ import ( "runtime" "time" - rpc "github.com/btcsuite/btcrpcclient" + rpc "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcutil" ) diff --git a/integration/rpctest/rpc_harness.go b/integration/rpctest/rpc_harness.go index 16789731..145c2938 100644 --- a/integration/rpctest/rpc_harness.go +++ b/integration/rpctest/rpc_harness.go @@ -17,8 +17,8 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcrpcclient" "github.com/btcsuite/btcutil" ) @@ -78,9 +78,9 @@ type Harness struct { // to. ActiveNet *chaincfg.Params - Node *btcrpcclient.Client + Node *rpcclient.Client node *node - handlers *btcrpcclient.NotificationHandlers + handlers *rpcclient.NotificationHandlers wallet *memWallet @@ -97,7 +97,7 @@ type Harness struct { // used. // // NOTE: This function is safe for concurrent access. -func New(activeNet *chaincfg.Params, handlers *btcrpcclient.NotificationHandlers, +func New(activeNet *chaincfg.Params, handlers *rpcclient.NotificationHandlers, extraArgs []string) (*Harness, error) { harnessStateMtx.Lock() @@ -157,7 +157,7 @@ func New(activeNet *chaincfg.Params, handlers *btcrpcclient.NotificationHandlers numTestInstances++ if handlers == nil { - handlers = &btcrpcclient.NotificationHandlers{} + handlers = &rpcclient.NotificationHandlers{} } // If a handler for the OnFilteredBlock{Connected,Disconnected} callback @@ -303,12 +303,12 @@ func (h *Harness) TearDown() error { // we're not able to establish a connection, this function returns with an // error. func (h *Harness) connectRPCClient() error { - var client *btcrpcclient.Client + var client *rpcclient.Client var err error rpcConf := h.node.config.rpcConnConfig() for i := 0; i < h.maxConnRetries; i++ { - if client, err = btcrpcclient.New(&rpcConf, h.handlers); err != nil { + if client, err = rpcclient.New(&rpcConf, h.handlers); err != nil { time.Sleep(time.Duration(i) * 50 * time.Millisecond) continue } @@ -379,7 +379,7 @@ func (h *Harness) UnlockOutputs(inputs []*wire.TxIn) { // RPCConfig returns the harnesses current rpc configuration. This allows other // potential RPC clients created within tests to connect to a given test // harness instance. -func (h *Harness) RPCConfig() btcrpcclient.ConnConfig { +func (h *Harness) RPCConfig() rpcclient.ConnConfig { return h.node.config.rpcConnConfig() } diff --git a/integration/rpctest/utils.go b/integration/rpctest/utils.go index 50cbb998..fc7d938d 100644 --- a/integration/rpctest/utils.go +++ b/integration/rpctest/utils.go @@ -9,7 +9,7 @@ import ( "time" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcrpcclient" + "github.com/btcsuite/btcd/rpcclient" ) // JoinType is an enum representing a particular type of "node join". A node @@ -115,7 +115,7 @@ func ConnectNode(from *Harness, to *Harness) error { numPeers := len(peerInfo) targetAddr := to.node.config.listen - if err := from.Node.AddNode(targetAddr, btcrpcclient.ANAdd); err != nil { + if err := from.Node.AddNode(targetAddr, rpcclient.ANAdd); err != nil { return err } diff --git a/rpcclient/CONTRIBUTORS b/rpcclient/CONTRIBUTORS new file mode 100644 index 00000000..86c2b993 --- /dev/null +++ b/rpcclient/CONTRIBUTORS @@ -0,0 +1,13 @@ +# This is the list of people who have contributed code to the repository. +# +# Names should be added to this file only after verifying that the individual +# or the individual's organization has agreed to the LICENSE. +# +# Names should be added to this file like so: +# Name + +Dave Collins +Geert-Johan Riemer +Josh Rickmar +Michalis Kargakis +Ruben de Vries 0 { + log.Printf("First utxo:\n%v", spew.Sdump(unspent[0])) + } + + // For this example gracefully shutdown the client after 10 seconds. + // Ordinarily when to shutdown the client is highly application + // specific. + log.Println("Client shutdown in 10 seconds...") + time.AfterFunc(time.Second*10, func() { + log.Println("Client shutting down...") + client.Shutdown() + log.Println("Client shutdown complete.") + }) + + // Wait until the client either shuts down gracefully (or the user + // terminates the process with Ctrl+C). + client.WaitForShutdown() +} diff --git a/rpcclient/extensions.go b/rpcclient/extensions.go new file mode 100644 index 00000000..916ddb5f --- /dev/null +++ b/rpcclient/extensions.go @@ -0,0 +1,473 @@ +// Copyright (c) 2014-2017 The btcsuite developers +// Copyright (c) 2015-2017 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpcclient + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// FutureDebugLevelResult is a future promise to deliver the result of a +// DebugLevelAsync RPC invocation (or an applicable error). +type FutureDebugLevelResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of setting the debug logging level to the passed level specification or the +// list of of the available subsystems for the special keyword 'show'. +func (r FutureDebugLevelResult) Receive() (string, error) { + res, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Unmashal the result as a string. + var result string + err = json.Unmarshal(res, &result) + if err != nil { + return "", err + } + return result, nil +} + +// DebugLevelAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See DebugLevel for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) DebugLevelAsync(levelSpec string) FutureDebugLevelResult { + cmd := btcjson.NewDebugLevelCmd(levelSpec) + return c.sendCmd(cmd) +} + +// DebugLevel dynamically sets the debug logging level to the passed level +// specification. +// +// The levelspec can be either a debug level or of the form: +// =,=,... +// +// Additionally, the special keyword 'show' can be used to get a list of the +// available subsystems. +// +// NOTE: This is a btcd extension. +func (c *Client) DebugLevel(levelSpec string) (string, error) { + return c.DebugLevelAsync(levelSpec).Receive() +} + +// FutureCreateEncryptedWalletResult is a future promise to deliver the error +// result of a CreateEncryptedWalletAsync RPC invocation. +type FutureCreateEncryptedWalletResult chan *response + +// Receive waits for and returns the error response promised by the future. +func (r FutureCreateEncryptedWalletResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// CreateEncryptedWalletAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See CreateEncryptedWallet for the blocking version and more details. +// +// NOTE: This is a btcwallet extension. +func (c *Client) CreateEncryptedWalletAsync(passphrase string) FutureCreateEncryptedWalletResult { + cmd := btcjson.NewCreateEncryptedWalletCmd(passphrase) + return c.sendCmd(cmd) +} + +// CreateEncryptedWallet requests the creation of an encrypted wallet. Wallets +// managed by btcwallet are only written to disk with encrypted private keys, +// and generating wallets on the fly is impossible as it requires user input for +// the encryption passphrase. This RPC specifies the passphrase and instructs +// the wallet creation. This may error if a wallet is already opened, or the +// new wallet cannot be written to disk. +// +// NOTE: This is a btcwallet extension. +func (c *Client) CreateEncryptedWallet(passphrase string) error { + return c.CreateEncryptedWalletAsync(passphrase).Receive() +} + +// FutureListAddressTransactionsResult is a future promise to deliver the result +// of a ListAddressTransactionsAsync RPC invocation (or an applicable error). +type FutureListAddressTransactionsResult chan *response + +// Receive waits for the response promised by the future and returns information +// about all transactions associated with the provided addresses. +func (r FutureListAddressTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal the result as an array of listtransactions objects. + var transactions []btcjson.ListTransactionsResult + err = json.Unmarshal(res, &transactions) + if err != nil { + return nil, err + } + return transactions, nil +} + +// ListAddressTransactionsAsync returns an instance of a type that can be used +// to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListAddressTransactions for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) ListAddressTransactionsAsync(addresses []btcutil.Address, account string) FutureListAddressTransactionsResult { + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + cmd := btcjson.NewListAddressTransactionsCmd(addrs, &account) + return c.sendCmd(cmd) +} + +// ListAddressTransactions returns information about all transactions associated +// with the provided addresses. +// +// NOTE: This is a btcwallet extension. +func (c *Client) ListAddressTransactions(addresses []btcutil.Address, account string) ([]btcjson.ListTransactionsResult, error) { + return c.ListAddressTransactionsAsync(addresses, account).Receive() +} + +// FutureGetBestBlockResult is a future promise to deliver the result of a +// GetBestBlockAsync RPC invocation (or an applicable error). +type FutureGetBestBlockResult chan *response + +// Receive waits for the response promised by the future and returns the hash +// and height of the block in the longest (best) chain. +func (r FutureGetBestBlockResult) Receive() (*chainhash.Hash, int32, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, 0, err + } + + // Unmarshal result as a getbestblock result object. + var bestBlock btcjson.GetBestBlockResult + err = json.Unmarshal(res, &bestBlock) + if err != nil { + return nil, 0, err + } + + // Convert to hash from string. + hash, err := chainhash.NewHashFromStr(bestBlock.Hash) + if err != nil { + return nil, 0, err + } + + return hash, bestBlock.Height, nil +} + +// GetBestBlockAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetBestBlock for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult { + cmd := btcjson.NewGetBestBlockCmd() + return c.sendCmd(cmd) +} + +// GetBestBlock returns the hash and height of the block in the longest (best) +// chain. +// +// NOTE: This is a btcd extension. +func (c *Client) GetBestBlock() (*chainhash.Hash, int32, error) { + return c.GetBestBlockAsync().Receive() +} + +// FutureGetCurrentNetResult is a future promise to deliver the result of a +// GetCurrentNetAsync RPC invocation (or an applicable error). +type FutureGetCurrentNetResult chan *response + +// Receive waits for the response promised by the future and returns the network +// the server is running on. +func (r FutureGetCurrentNetResult) Receive() (wire.BitcoinNet, error) { + res, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Unmarshal result as an int64. + var net int64 + err = json.Unmarshal(res, &net) + if err != nil { + return 0, err + } + + return wire.BitcoinNet(net), nil +} + +// GetCurrentNetAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetCurrentNet for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult { + cmd := btcjson.NewGetCurrentNetCmd() + return c.sendCmd(cmd) +} + +// GetCurrentNet returns the network the server is running on. +// +// NOTE: This is a btcd extension. +func (c *Client) GetCurrentNet() (wire.BitcoinNet, error) { + return c.GetCurrentNetAsync().Receive() +} + +// FutureGetHeadersResult is a future promise to deliver the result of a +// getheaders RPC invocation (or an applicable error). +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +type FutureGetHeadersResult chan *response + +// Receive waits for the response promised by the future and returns the +// getheaders result. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (r FutureGetHeadersResult) Receive() ([]wire.BlockHeader, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a slice of strings. + var result []string + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + // Deserialize the []string into []wire.BlockHeader. + headers := make([]wire.BlockHeader, len(result)) + for i, headerHex := range result { + serialized, err := hex.DecodeString(headerHex) + if err != nil { + return nil, err + } + err = headers[i].Deserialize(bytes.NewReader(serialized)) + if err != nil { + return nil, err + } + } + return headers, nil +} + +// GetHeadersAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the returned instance. +// +// See GetHeaders for the blocking version and more details. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) GetHeadersAsync(blockLocators []chainhash.Hash, hashStop *chainhash.Hash) FutureGetHeadersResult { + locators := make([]string, len(blockLocators)) + for i := range blockLocators { + locators[i] = blockLocators[i].String() + } + hash := "" + if hashStop != nil { + hash = hashStop.String() + } + cmd := btcjson.NewGetHeadersCmd(locators, hash) + return c.sendCmd(cmd) +} + +// GetHeaders mimics the wire protocol getheaders and headers messages by +// returning all headers on the main chain after the first known block in the +// locators, up until a block hash matches hashStop. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) GetHeaders(blockLocators []chainhash.Hash, hashStop *chainhash.Hash) ([]wire.BlockHeader, error) { + return c.GetHeadersAsync(blockLocators, hashStop).Receive() +} + +// FutureExportWatchingWalletResult is a future promise to deliver the result of +// an ExportWatchingWalletAsync RPC invocation (or an applicable error). +type FutureExportWatchingWalletResult chan *response + +// Receive waits for the response promised by the future and returns the +// exported wallet. +func (r FutureExportWatchingWalletResult) Receive() ([]byte, []byte, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, nil, err + } + + // Unmarshal result as a JSON object. + var obj map[string]interface{} + err = json.Unmarshal(res, &obj) + if err != nil { + return nil, nil, err + } + + // Check for the wallet and tx string fields in the object. + base64Wallet, ok := obj["wallet"].(string) + if !ok { + return nil, nil, fmt.Errorf("unexpected response type for "+ + "exportwatchingwallet 'wallet' field: %T\n", + obj["wallet"]) + } + base64TxStore, ok := obj["tx"].(string) + if !ok { + return nil, nil, fmt.Errorf("unexpected response type for "+ + "exportwatchingwallet 'tx' field: %T\n", + obj["tx"]) + } + + walletBytes, err := base64.StdEncoding.DecodeString(base64Wallet) + if err != nil { + return nil, nil, err + } + + txStoreBytes, err := base64.StdEncoding.DecodeString(base64TxStore) + if err != nil { + return nil, nil, err + } + + return walletBytes, txStoreBytes, nil + +} + +// ExportWatchingWalletAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ExportWatchingWallet for the blocking version and more details. +// +// NOTE: This is a btcwallet extension. +func (c *Client) ExportWatchingWalletAsync(account string) FutureExportWatchingWalletResult { + cmd := btcjson.NewExportWatchingWalletCmd(&account, btcjson.Bool(true)) + return c.sendCmd(cmd) +} + +// ExportWatchingWallet returns the raw bytes for a watching-only version of +// wallet.bin and tx.bin, respectively, for the specified account that can be +// used by btcwallet to enable a wallet which does not have the private keys +// necessary to spend funds. +// +// NOTE: This is a btcwallet extension. +func (c *Client) ExportWatchingWallet(account string) ([]byte, []byte, error) { + return c.ExportWatchingWalletAsync(account).Receive() +} + +// FutureSessionResult is a future promise to deliver the result of a +// SessionAsync RPC invocation (or an applicable error). +type FutureSessionResult chan *response + +// Receive waits for the response promised by the future and returns the +// session result. +func (r FutureSessionResult) Receive() (*btcjson.SessionResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a session result object. + var session btcjson.SessionResult + err = json.Unmarshal(res, &session) + if err != nil { + return nil, err + } + + return &session, nil +} + +// SessionAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See Session for the blocking version and more details. +// +// NOTE: This is a btcsuite extension. +func (c *Client) SessionAsync() FutureSessionResult { + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + cmd := btcjson.NewSessionCmd() + return c.sendCmd(cmd) +} + +// Session returns details regarding a websocket client's current connection. +// +// This RPC requires the client to be running in websocket mode. +// +// NOTE: This is a btcsuite extension. +func (c *Client) Session() (*btcjson.SessionResult, error) { + return c.SessionAsync().Receive() +} + +// FutureVersionResult is a future promise to delivere the result of a version +// RPC invocation (or an applicable error). +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +type FutureVersionResult chan *response + +// Receive waits for the response promised by the future and returns the version +// result. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (r FutureVersionResult) Receive() (map[string]btcjson.VersionResult, + error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a version result object. + var vr map[string]btcjson.VersionResult + err = json.Unmarshal(res, &vr) + if err != nil { + return nil, err + } + + return vr, nil +} + +// VersionAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See Version for the blocking version and more details. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) VersionAsync() FutureVersionResult { + cmd := btcjson.NewVersionCmd() + return c.sendCmd(cmd) +} + +// Version returns information about the server's JSON-RPC API versions. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) Version() (map[string]btcjson.VersionResult, error) { + return c.VersionAsync().Receive() +} diff --git a/rpcclient/infrastructure.go b/rpcclient/infrastructure.go new file mode 100644 index 00000000..24f0ae99 --- /dev/null +++ b/rpcclient/infrastructure.go @@ -0,0 +1,1332 @@ +// Copyright (c) 2014-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpcclient + +import ( + "bytes" + "container/list" + "crypto/tls" + "crypto/x509" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "math" + "net" + "net/http" + "net/url" + "sync" + "sync/atomic" + "time" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/go-socks/socks" + "github.com/btcsuite/websocket" +) + +var ( + // ErrInvalidAuth is an error to describe the condition where the client + // is either unable to authenticate or the specified endpoint is + // incorrect. + ErrInvalidAuth = errors.New("authentication failure") + + // ErrInvalidEndpoint is an error to describe the condition where the + // websocket handshake failed with the specified endpoint. + ErrInvalidEndpoint = errors.New("the endpoint either does not support " + + "websockets or does not exist") + + // ErrClientNotConnected is an error to describe the condition where a + // websocket client has been created, but the connection was never + // established. This condition differs from ErrClientDisconnect, which + // represents an established connection that was lost. + ErrClientNotConnected = errors.New("the client was never connected") + + // ErrClientDisconnect is an error to describe the condition where the + // client has been disconnected from the RPC server. When the + // DisableAutoReconnect option is not set, any outstanding futures + // when a client disconnect occurs will return this error as will + // any new requests. + ErrClientDisconnect = errors.New("the client has been disconnected") + + // ErrClientShutdown is an error to describe the condition where the + // client is either already shutdown, or in the process of shutting + // down. Any outstanding futures when a client shutdown occurs will + // return this error as will any new requests. + ErrClientShutdown = errors.New("the client has been shutdown") + + // ErrNotWebsocketClient is an error to describe the condition of + // calling a Client method intended for a websocket client when the + // client has been configured to run in HTTP POST mode instead. + ErrNotWebsocketClient = errors.New("client is not configured for " + + "websockets") + + // ErrClientAlreadyConnected is an error to describe the condition where + // a new client connection cannot be established due to a websocket + // client having already connected to the RPC server. + ErrClientAlreadyConnected = errors.New("websocket client has already " + + "connected") +) + +const ( + // sendBufferSize is the number of elements the websocket send channel + // can queue before blocking. + sendBufferSize = 50 + + // sendPostBufferSize is the number of elements the HTTP POST send + // channel can queue before blocking. + sendPostBufferSize = 100 + + // connectionRetryInterval is the amount of time to wait in between + // retries when automatically reconnecting to an RPC server. + connectionRetryInterval = time.Second * 5 +) + +// sendPostDetails houses an HTTP POST request to send to an RPC server as well +// as the original JSON-RPC command and a channel to reply on when the server +// responds with the result. +type sendPostDetails struct { + 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 { + id uint64 + method string + cmd interface{} + marshalledJSON []byte + responseChan chan *response +} + +// Client represents a Bitcoin RPC client which allows easy access to the +// various RPC methods available on a Bitcoin RPC server. Each of the wrapper +// functions handle the details of converting the passed and return types to and +// from the underlying JSON types which are required for the JSON-RPC +// invocations +// +// The client provides each RPC in both synchronous (blocking) and asynchronous +// (non-blocking) forms. The asynchronous forms are based on the concept of +// futures where they return an instance of a type that promises to deliver the +// result of the invocation at some future time. Invoking the Receive method on +// the returned future will block until the result is available if it's not +// already. +type Client struct { + id uint64 // atomic, so must stay 64-bit aligned + + // config holds the connection configuration assoiated with this client. + config *ConnConfig + + // wsConn is the underlying websocket connection when not in HTTP POST + // mode. + wsConn *websocket.Conn + + // httpClient is the underlying HTTP client to use when running in HTTP + // POST mode. + httpClient *http.Client + + // mtx is a mutex to protect access to connection related fields. + mtx sync.Mutex + + // disconnected indicated whether or not the server is disconnected. + disconnected bool + + // retryCount holds the number of times the client has tried to + // reconnect to the RPC server. + retryCount int64 + + // Track command and their response channels by ID. + requestLock sync.Mutex + requestMap map[uint64]*list.Element + requestList *list.List + + // Notifications. + ntfnHandlers *NotificationHandlers + ntfnStateLock sync.Mutex + ntfnState *notificationState + + // Networking infrastructure. + sendChan chan []byte + sendPostChan chan *sendPostDetails + connEstablished chan struct{} + disconnect chan struct{} + shutdown chan struct{} + wg sync.WaitGroup +} + +// NextID returns the next id to be used when sending a JSON-RPC message. This +// ID allows responses to be associated with particular requests per the +// JSON-RPC specification. Typically the consumer of the client does not need +// to call this function, however, if a custom request is being created and used +// this function should be used to ensure the ID is unique amongst all requests +// being made. +func (c *Client) NextID() uint64 { + return atomic.AddUint64(&c.id, 1) +} + +// 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(jReq *jsonRequest) error { + c.requestLock.Lock() + defer c.requestLock.Unlock() + + // A non-blocking read of the shutdown channel with the request lock + // held avoids adding the request to the client's internal data + // structures if the client is in the process of shutting down (and + // has not yet grabbed the request lock), or has finished shutdown + // already (responding to each outstanding request with + // ErrClientShutdown). + select { + case <-c.shutdown: + return ErrClientShutdown + default: + } + + element := c.requestList.PushBack(jReq) + c.requestMap[jReq.id] = element + return nil +} + +// removeRequest returns and removes the jsonRequest which contains the response +// channel and original method associated with the passed id or nil if there is +// no association. +// +// This function is safe for concurrent access. +func (c *Client) removeRequest(id uint64) *jsonRequest { + c.requestLock.Lock() + defer c.requestLock.Unlock() + + element := c.requestMap[id] + if element != nil { + delete(c.requestMap, id) + request := c.requestList.Remove(element).(*jsonRequest) + return request + } + + return nil +} + +// removeAllRequests removes all the jsonRequests which contain the response +// channels for outstanding requests. +// +// This function MUST be called with the request lock held. +func (c *Client) removeAllRequests() { + c.requestMap = make(map[uint64]*list.Element) + c.requestList.Init() +} + +// trackRegisteredNtfns examines the passed command to see if it is one of +// the notification commands and updates the notification state that is used +// to automatically re-establish registered notifications on reconnects. +func (c *Client) trackRegisteredNtfns(cmd interface{}) { + // Nothing to do if the caller is not interested in notifications. + if c.ntfnHandlers == nil { + return + } + + c.ntfnStateLock.Lock() + defer c.ntfnStateLock.Unlock() + + switch bcmd := cmd.(type) { + case *btcjson.NotifyBlocksCmd: + c.ntfnState.notifyBlocks = true + + case *btcjson.NotifyNewTransactionsCmd: + if bcmd.Verbose != nil && *bcmd.Verbose { + c.ntfnState.notifyNewTxVerbose = true + } else { + c.ntfnState.notifyNewTx = true + + } + + case *btcjson.NotifySpentCmd: + for _, op := range bcmd.OutPoints { + c.ntfnState.notifySpent[op] = struct{}{} + } + + case *btcjson.NotifyReceivedCmd: + for _, addr := range bcmd.Addresses { + c.ntfnState.notifyReceived[addr] = struct{}{} + } + } +} + +type ( + // inMessage is the first type that an incoming message is unmarshaled + // into. It supports both requests (for notification support) and + // responses. The partially-unmarshaled message is a notification if + // the embedded ID (from the response) is nil. Otherwise, it is a + // response. + inMessage struct { + ID *float64 `json:"id"` + *rawNotification + *rawResponse + } + + // rawNotification is a partially-unmarshaled JSON-RPC notification. + rawNotification struct { + Method string `json:"method"` + Params []json.RawMessage `json:"params"` + } + + // rawResponse is a partially-unmarshaled JSON-RPC response. For this + // to be valid (according to JSON-RPC 1.0 spec), ID may not be nil. + rawResponse struct { + Result json.RawMessage `json:"result"` + Error *btcjson.RPCError `json:"error"` + } +) + +// response is the raw bytes of a JSON-RPC result, or the error if the response +// error object was non-null. +type response struct { + result []byte + err error +} + +// result checks whether the unmarshaled response contains a non-nil error, +// returning an unmarshaled btcjson.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) { + if r.Error != nil { + return nil, r.Error + } + return r.Result, nil +} + +// handleMessage is the main handler for incoming notifications and responses. +func (c *Client) handleMessage(msg []byte) { + // Attempt to unmarshal the message as either a notification or + // response. + var in inMessage + err := json.Unmarshal(msg, &in) + if err != nil { + log.Warnf("Remote server sent invalid message: %v", err) + return + } + + // JSON-RPC 1.0 notifications are requests with a null id. + if in.ID == nil { + ntfn := in.rawNotification + if ntfn == nil { + log.Warn("Malformed notification: missing " + + "method and parameters") + return + } + if ntfn.Method == "" { + log.Warn("Malformed notification: missing method") + return + } + // params are not optional: nil isn't valid (but len == 0 is) + if ntfn.Params == nil { + log.Warn("Malformed notification: missing params") + return + } + // Deliver the notification. + log.Tracef("Received notification [%s]", in.Method) + c.handleNotification(in.rawNotification) + return + } + + // ensure that in.ID can be converted to an integer without loss of precision + if *in.ID < 0 || *in.ID != math.Trunc(*in.ID) { + log.Warn("Malformed response: invalid identifier") + return + } + + if in.rawResponse == nil { + log.Warn("Malformed response: missing result and error") + return + } + + id := uint64(*in.ID) + log.Tracef("Received response for id %d (result %s)", id, in.Result) + request := c.removeRequest(id) + + // Nothing more to do if there is no request associated with this reply. + if request == nil || request.responseChan == nil { + log.Warnf("Received unexpected reply: %s (id %d)", in.Result, + id) + return + } + + // Since the command was successful, examine it to see if it's a + // notification, and if is, add it to the notification state so it + // can automatically be re-established on reconnect. + c.trackRegisteredNtfns(request.cmd) + + // Deliver the response. + result, err := in.rawResponse.result() + request.responseChan <- &response{result: result, err: err} +} + +// shouldLogReadError returns whether or not the passed error, which is expected +// to have come from reading from the websocket connection in wsInHandler, +// should be logged. +func (c *Client) shouldLogReadError(err error) bool { + // No logging when the connetion is being forcibly disconnected. + select { + case <-c.shutdown: + return false + default: + } + + // No logging when the connection has been disconnected. + if err == io.EOF { + return false + } + if opErr, ok := err.(*net.OpError); ok && !opErr.Temporary() { + return false + } + + return true +} + +// wsInHandler handles all incoming messages for the websocket connection +// associated with the client. It must be run as a goroutine. +func (c *Client) wsInHandler() { +out: + for { + // Break out of the loop once the shutdown channel has been + // closed. Use a non-blocking select here so we fall through + // otherwise. + select { + case <-c.shutdown: + break out + default: + } + + _, msg, err := c.wsConn.ReadMessage() + if err != nil { + // Log the error if it's not due to disconnecting. + if c.shouldLogReadError(err) { + log.Errorf("Websocket receive error from "+ + "%s: %v", c.config.Host, err) + } + break out + } + c.handleMessage(msg) + } + + // Ensure the connection is closed. + c.Disconnect() + c.wg.Done() + log.Tracef("RPC client input handler done for %s", c.config.Host) +} + +// disconnectChan returns a copy of the current disconnect channel. The channel +// is read protected by the client mutex, and is safe to call while the channel +// is being reassigned during a reconnect. +func (c *Client) disconnectChan() <-chan struct{} { + c.mtx.Lock() + ch := c.disconnect + c.mtx.Unlock() + return ch +} + +// wsOutHandler handles all outgoing messages for the websocket connection. It +// uses a buffered channel to serialize output messages while allowing the +// sender to continue running asynchronously. It must be run as a goroutine. +func (c *Client) wsOutHandler() { +out: + for { + // Send any messages ready for send until the client is + // disconnected closed. + select { + case msg := <-c.sendChan: + err := c.wsConn.WriteMessage(websocket.TextMessage, msg) + if err != nil { + c.Disconnect() + break out + } + + case <-c.disconnectChan(): + break out + } + } + + // Drain any channels before exiting so nothing is left waiting around + // to send. +cleanup: + for { + select { + case <-c.sendChan: + default: + break cleanup + } + } + c.wg.Done() + log.Tracef("RPC client output handler done for %s", c.config.Host) +} + +// sendMessage sends the passed JSON to the connected server using the +// websocket connection. It is backed by a buffered channel, so it will not +// block until the send channel is full. +func (c *Client) sendMessage(marshalledJSON []byte) { + // Don't send the message if disconnected. + select { + case c.sendChan <- marshalledJSON: + case <-c.disconnectChan(): + return + } +} + +// 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 resendRequests function. +func (c *Client) reregisterNtfns() error { + // Nothing to do if the caller is not interested in notifications. + if c.ntfnHandlers == nil { + return nil + } + + // In order to avoid holding the lock on the notification state for the + // entire time of the potentially long running RPCs issued below, make a + // copy of it and work from that. + // + // Also, other commands will be running concurrently which could modify + // the notification state (while not under the lock of course) which + // also register it with the remote RPC server, so this prevents double + // registrations. + c.ntfnStateLock.Lock() + stateCopy := c.ntfnState.Copy() + c.ntfnStateLock.Unlock() + + // Reregister notifyblocks if needed. + if stateCopy.notifyBlocks { + log.Debugf("Reregistering [notifyblocks]") + if err := c.NotifyBlocks(); err != nil { + return err + } + } + + // Reregister notifynewtransactions if needed. + if stateCopy.notifyNewTx || stateCopy.notifyNewTxVerbose { + log.Debugf("Reregistering [notifynewtransactions] (verbose=%v)", + stateCopy.notifyNewTxVerbose) + err := c.NotifyNewTransactions(stateCopy.notifyNewTxVerbose) + if err != nil { + return err + } + } + + // Reregister the combination of all previously registered notifyspent + // outpoints in one command if needed. + nslen := len(stateCopy.notifySpent) + if nslen > 0 { + outpoints := make([]btcjson.OutPoint, 0, nslen) + for op := range stateCopy.notifySpent { + outpoints = append(outpoints, op) + } + log.Debugf("Reregistering [notifyspent] outpoints: %v", outpoints) + if err := c.notifySpentInternal(outpoints).Receive(); err != nil { + return err + } + } + + // Reregister the combination of all previously registered + // notifyreceived addresses in one command if needed. + nrlen := len(stateCopy.notifyReceived) + if nrlen > 0 { + addresses := make([]string, 0, nrlen) + for addr := range stateCopy.notifyReceived { + addresses = append(addresses, addr) + } + log.Debugf("Reregistering [notifyreceived] addresses: %v", addresses) + if err := c.notifyReceivedInternal(addresses).Receive(); err != nil { + return err + } + } + + return nil +} + +// ignoreResends is a set of all methods for requests that are "long running" +// are not be reissued by the client on reconnect. +var ignoreResends = map[string]struct{}{ + "rescan": {}, +} + +// 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) resendRequests() { + // Set the notification state back up. If anything goes wrong, + // disconnect the client. + if err := c.reregisterNtfns(); err != nil { + log.Warnf("Unable to re-establish notification state: %v", err) + c.Disconnect() + return + } + + // Since it's possible to block on send and more requests might be + // added by the caller while resending, make a copy of all of the + // requests that need to be resent now and work from the copy. This + // also allows the lock to be released quickly. + c.requestLock.Lock() + resendReqs := make([]*jsonRequest, 0, c.requestList.Len()) + var nextElem *list.Element + for e := c.requestList.Front(); e != nil; e = nextElem { + nextElem = e.Next() + + 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, jReq.id) + c.requestList.Remove(e) + } else { + resendReqs = append(resendReqs, jReq) + } + } + c.requestLock.Unlock() + + for _, jReq := range resendReqs { + // Stop resending commands if the client disconnected again + // since the next reconnect will handle them. + if c.Disconnected() { + return + } + + log.Tracef("Sending command [%s] with id %d", jReq.method, + jReq.id) + c.sendMessage(jReq.marshalledJSON) + } +} + +// wsReconnectHandler listens for client disconnects and automatically tries +// to reconnect with retry interval that scales based on the number of retries. +// It also resends any commands that had not completed when the client +// disconnected so the disconnect/reconnect process is largely transparent to +// the caller. This function is not run when the DisableAutoReconnect config +// options is set. +// +// This function must be run as a goroutine. +func (c *Client) wsReconnectHandler() { +out: + for { + select { + case <-c.disconnect: + // On disconnect, fallthrough to reestablish the + // connection. + + case <-c.shutdown: + break out + } + + reconnect: + for { + select { + case <-c.shutdown: + break out + default: + } + + wsConn, err := dial(c.config) + if err != nil { + c.retryCount++ + log.Infof("Failed to connect to %s: %v", + c.config.Host, err) + + // Scale the retry interval by the number of + // retries so there is a backoff up to a max + // of 1 minute. + scaledInterval := connectionRetryInterval.Nanoseconds() * c.retryCount + scaledDuration := time.Duration(scaledInterval) + if scaledDuration > time.Minute { + scaledDuration = time.Minute + } + log.Infof("Retrying connection to %s in "+ + "%s", c.config.Host, scaledDuration) + time.Sleep(scaledDuration) + continue reconnect + } + + log.Infof("Reestablished connection to RPC server %s", + c.config.Host) + + // Reset the connection state and signal the reconnect + // has happened. + c.wsConn = wsConn + c.retryCount = 0 + + c.mtx.Lock() + c.disconnect = make(chan struct{}) + c.disconnected = false + c.mtx.Unlock() + + // Start processing input and output for the + // new connection. + c.start() + + // Reissue pending requests in another goroutine since + // the send can block. + go c.resendRequests() + + // Break out of the reconnect loop back to wait for + // disconnect again. + break reconnect + } + } + c.wg.Done() + log.Tracef("RPC client reconnect handler done for %s", c.config.Host) +} + +// handleSendPostMessage handles performing the passed HTTP request, reading the +// result, unmarshalling it, and delivering the unmarshalled result to the +// provided response channel. +func (c *Client) handleSendPostMessage(details *sendPostDetails) { + 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 { + jReq.responseChan <- &response{err: err} + return + } + + // Read the raw bytes and close the response. + respBytes, err := ioutil.ReadAll(httpResponse.Body) + httpResponse.Body.Close() + if err != nil { + err = fmt.Errorf("error reading json reply: %v", err) + jReq.responseChan <- &response{err: err} + return + } + + // Try to unmarshal the response as a regular JSON-RPC response. + var resp rawResponse + err = json.Unmarshal(respBytes, &resp) + if err != nil { + // When the response itself isn't a valid JSON-RPC response + // return an error which includes the HTTP status code and raw + // response bytes. + err = fmt.Errorf("status code: %d, response: %q", + httpResponse.StatusCode, string(respBytes)) + jReq.responseChan <- &response{err: err} + return + } + + res, err := resp.result() + jReq.responseChan <- &response{result: res, err: err} +} + +// sendPostHandler handles all outgoing messages when the client is running +// in HTTP POST mode. It uses a buffered channel to serialize output messages +// while allowing the sender to continue running asynchronously. It must be run +// as a goroutine. +func (c *Client) sendPostHandler() { +out: + for { + // Send any messages ready for send until the shutdown channel + // is closed. + select { + case details := <-c.sendPostChan: + c.handleSendPostMessage(details) + + case <-c.shutdown: + break out + } + } + + // Drain any wait channels before exiting so nothing is left waiting + // around to send. +cleanup: + for { + select { + case details := <-c.sendPostChan: + details.jsonRequest.responseChan <- &response{ + result: nil, + err: ErrClientShutdown, + } + + default: + break cleanup + } + } + c.wg.Done() + log.Tracef("RPC client send handler done for %s", c.config.Host) + +} + +// sendPostRequest sends the passed HTTP request to the RPC server using the +// HTTP client associated with the client. It is backed by a buffered channel, +// so it will not block until the send channel is full. +func (c *Client) sendPostRequest(httpReq *http.Request, jReq *jsonRequest) { + // Don't send the message if shutting down. + select { + case <-c.shutdown: + jReq.responseChan <- &response{result: nil, err: ErrClientShutdown} + default: + } + + c.sendPostChan <- &sendPostDetails{ + jsonRequest: jReq, + httpRequest: httpReq, + } +} + +// newFutureError returns a new future result channel that already has the +// passed error waitin on the channel with the reply set to nil. This is useful +// to easily return errors from the various Async functions. +func newFutureError(err error) chan *response { + responseChan := make(chan *response, 1) + responseChan <- &response{err: err} + return responseChan +} + +// receiveFuture receives from the passed futureResult channel to extract a +// reply or any errors. The examined errors include an error in the +// futureResult and the error in the reply from the server. This will block +// until the result is available on the passed channel. +func receiveFuture(f chan *response) ([]byte, error) { + // Wait for a response on the returned channel. + r := <-f + return r.result, r.err +} + +// 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 + bodyReader := bytes.NewReader(jReq.marshalledJSON) + httpReq, err := http.NewRequest("POST", url, bodyReader) + if err != nil { + jReq.responseChan <- &response{result: nil, err: err} + return + } + httpReq.Close = true + httpReq.Header.Set("Content-Type", "application/json") + + // Configure basic access authorization. + httpReq.SetBasicAuth(c.config.User, c.config.Pass) + + log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id) + c.sendPostRequest(httpReq, jReq) +} + +// 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. + if c.config.HTTPPostMode { + c.sendPost(jReq) + return + } + + // Check whether the websocket connection has never been established, + // in which case the handler goroutines are not running. + select { + case <-c.connEstablished: + default: + jReq.responseChan <- &response{err: ErrClientNotConnected} + return + } + + // 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 + } + 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 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)) +} + +// Disconnected returns whether or not the server is disconnected. If a +// websocket client was created but never connected, this also returns false. +func (c *Client) Disconnected() bool { + c.mtx.Lock() + defer c.mtx.Unlock() + + select { + case <-c.connEstablished: + return c.disconnected + default: + return false + } +} + +// doDisconnect disconnects the websocket associated with the client if it +// hasn't already been disconnected. It will return false if the disconnect is +// not needed or the client is running in HTTP POST mode. +// +// This function is safe for concurrent access. +func (c *Client) doDisconnect() bool { + if c.config.HTTPPostMode { + return false + } + + c.mtx.Lock() + defer c.mtx.Unlock() + + // Nothing to do if already disconnected. + if c.disconnected { + return false + } + + log.Tracef("Disconnecting RPC client %s", c.config.Host) + close(c.disconnect) + if c.wsConn != nil { + c.wsConn.Close() + } + c.disconnected = true + return true +} + +// doShutdown closes the shutdown channel and logs the shutdown unless shutdown +// is already in progress. It will return false if the shutdown is not needed. +// +// This function is safe for concurrent access. +func (c *Client) doShutdown() bool { + // Ignore the shutdown request if the client is already in the process + // of shutting down or already shutdown. + select { + case <-c.shutdown: + return false + default: + } + + log.Tracef("Shutting down RPC client %s", c.config.Host) + close(c.shutdown) + return true +} + +// Disconnect disconnects the current websocket associated with the client. The +// connection will automatically be re-established unless the client was +// created with the DisableAutoReconnect flag. +// +// This function has no effect when the client is running in HTTP POST mode. +func (c *Client) Disconnect() { + // Nothing to do if already disconnected or running in HTTP POST mode. + if !c.doDisconnect() { + return + } + + c.requestLock.Lock() + defer c.requestLock.Unlock() + + // When operating without auto reconnect, send errors to any pending + // requests and shutdown the client. + if c.config.DisableAutoReconnect { + for e := c.requestList.Front(); e != nil; e = e.Next() { + req := e.Value.(*jsonRequest) + req.responseChan <- &response{ + result: nil, + err: ErrClientDisconnect, + } + } + c.removeAllRequests() + c.doShutdown() + } +} + +// Shutdown shuts down the client by disconnecting any connections associated +// with the client and, when automatic reconnect is enabled, preventing future +// attempts to reconnect. It also stops all goroutines. +func (c *Client) Shutdown() { + // Do the shutdown under the request lock to prevent clients from + // adding new requests while the client shutdown process is initiated. + c.requestLock.Lock() + defer c.requestLock.Unlock() + + // Ignore the shutdown request if the client is already in the process + // of shutting down or already shutdown. + if !c.doShutdown() { + return + } + + // Send the ErrClientShutdown error to any pending requests. + for e := c.requestList.Front(); e != nil; e = e.Next() { + req := e.Value.(*jsonRequest) + req.responseChan <- &response{ + result: nil, + err: ErrClientShutdown, + } + } + c.removeAllRequests() + + // Disconnect the client if needed. + c.doDisconnect() +} + +// start begins processing input and output messages. +func (c *Client) start() { + log.Tracef("Starting RPC client %s", c.config.Host) + + // Start the I/O processing handlers depending on whether the client is + // in HTTP POST mode or the default websocket mode. + if c.config.HTTPPostMode { + c.wg.Add(1) + go c.sendPostHandler() + } else { + c.wg.Add(3) + go func() { + if c.ntfnHandlers != nil { + if c.ntfnHandlers.OnClientConnected != nil { + c.ntfnHandlers.OnClientConnected() + } + } + c.wg.Done() + }() + go c.wsInHandler() + go c.wsOutHandler() + } +} + +// WaitForShutdown blocks until the client goroutines are stopped and the +// connection is closed. +func (c *Client) WaitForShutdown() { + c.wg.Wait() +} + +// ConnConfig describes the connection configuration parameters for the client. +// This +type ConnConfig struct { + // Host is the IP address and port of the RPC server you want to connect + // to. + Host string + + // Endpoint is the websocket endpoint on the RPC server. This is + // typically "ws". + Endpoint string + + // User is the username to use to authenticate to the RPC server. + User string + + // Pass is the passphrase to use to authenticate to the RPC server. + Pass string + + // DisableTLS specifies whether transport layer security should be + // disabled. It is recommended to always use TLS if the RPC server + // supports it as otherwise your username and password is sent across + // the wire in cleartext. + DisableTLS bool + + // Certificates are the bytes for a PEM-encoded certificate chain used + // for the TLS connection. It has no effect if the DisableTLS parameter + // is true. + Certificates []byte + + // Proxy specifies to connect through a SOCKS 5 proxy server. It may + // be an empty string if a proxy is not required. + Proxy string + + // ProxyUser is an optional username to use for the proxy server if it + // requires authentication. It has no effect if the Proxy parameter + // is not set. + ProxyUser string + + // ProxyPass is an optional password to use for the proxy server if it + // requires authentication. It has no effect if the Proxy parameter + // is not set. + ProxyPass string + + // DisableAutoReconnect specifies the client should not automatically + // try to reconnect to the server when it has been disconnected. + DisableAutoReconnect bool + + // DisableConnectOnNew specifies that a websocket client connection + // should not be tried when creating the client with New. Instead, the + // client is created and returned unconnected, and Connect must be + // called manually. + DisableConnectOnNew bool + + // HTTPPostMode instructs the client to run using multiple independent + // connections issuing HTTP POST requests instead of using the default + // of websockets. Websockets are generally preferred as some of the + // features of the client such notifications only work with websockets, + // however, not all servers support the websocket extensions, so this + // flag can be set to true to use basic HTTP POST requests instead. + HTTPPostMode bool + + // EnableBCInfoHacks is an option provided to enable compatiblity hacks + // when connecting to blockchain.info RPC server + EnableBCInfoHacks bool +} + +// newHTTPClient returns a new http client that is configured according to the +// proxy and TLS settings in the associated connection configuration. +func newHTTPClient(config *ConnConfig) (*http.Client, error) { + // Set proxy function if there is a proxy configured. + var proxyFunc func(*http.Request) (*url.URL, error) + if config.Proxy != "" { + proxyURL, err := url.Parse(config.Proxy) + if err != nil { + return nil, err + } + proxyFunc = http.ProxyURL(proxyURL) + } + + // Configure TLS if needed. + var tlsConfig *tls.Config + if !config.DisableTLS { + if len(config.Certificates) > 0 { + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(config.Certificates) + tlsConfig = &tls.Config{ + RootCAs: pool, + } + } + } + + client := http.Client{ + Transport: &http.Transport{ + Proxy: proxyFunc, + TLSClientConfig: tlsConfig, + }, + } + + return &client, nil +} + +// dial opens a websocket connection using the passed connection configuration +// details. +func dial(config *ConnConfig) (*websocket.Conn, error) { + // Setup TLS if not disabled. + var tlsConfig *tls.Config + var scheme = "ws" + if !config.DisableTLS { + tlsConfig = &tls.Config{ + MinVersion: tls.VersionTLS12, + } + if len(config.Certificates) > 0 { + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(config.Certificates) + tlsConfig.RootCAs = pool + } + scheme = "wss" + } + + // Create a websocket dialer that will be used to make the connection. + // It is modified by the proxy setting below as needed. + dialer := websocket.Dialer{TLSClientConfig: tlsConfig} + + // Setup the proxy if one is configured. + if config.Proxy != "" { + proxy := &socks.Proxy{ + Addr: config.Proxy, + Username: config.ProxyUser, + Password: config.ProxyPass, + } + dialer.NetDial = proxy.Dial + } + + // The RPC server requires basic authorization, so create a custom + // request header with the Authorization header set. + login := config.User + ":" + config.Pass + auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) + requestHeader := make(http.Header) + requestHeader.Add("Authorization", auth) + + // Dial the connection. + url := fmt.Sprintf("%s://%s/%s", scheme, config.Host, config.Endpoint) + wsConn, resp, err := dialer.Dial(url, requestHeader) + if err != nil { + if err != websocket.ErrBadHandshake || resp == nil { + return nil, err + } + + // Detect HTTP authentication error status codes. + if resp.StatusCode == http.StatusUnauthorized || + resp.StatusCode == http.StatusForbidden { + return nil, ErrInvalidAuth + } + + // The connection was authenticated and the status response was + // ok, but the websocket handshake still failed, so the endpoint + // is invalid in some way. + if resp.StatusCode == http.StatusOK { + return nil, ErrInvalidEndpoint + } + + // Return the status text from the server if none of the special + // cases above apply. + return nil, errors.New(resp.Status) + } + return wsConn, nil +} + +// New creates a new RPC client based on the provided connection configuration +// details. The notification handlers parameter may be nil if you are not +// interested in receiving notifications and will be ignored if the +// configuration is set to run in HTTP POST mode. +func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error) { + // Either open a websocket connection or create an HTTP client depending + // on the HTTP POST mode. Also, set the notification handlers to nil + // when running in HTTP POST mode. + var wsConn *websocket.Conn + var httpClient *http.Client + connEstablished := make(chan struct{}) + var start bool + if config.HTTPPostMode { + ntfnHandlers = nil + start = true + + var err error + httpClient, err = newHTTPClient(config) + if err != nil { + return nil, err + } + } else { + if !config.DisableConnectOnNew { + var err error + wsConn, err = dial(config) + if err != nil { + return nil, err + } + start = true + } + } + + client := &Client{ + config: config, + wsConn: wsConn, + httpClient: httpClient, + requestMap: make(map[uint64]*list.Element), + requestList: list.New(), + ntfnHandlers: ntfnHandlers, + ntfnState: newNotificationState(), + sendChan: make(chan []byte, sendBufferSize), + sendPostChan: make(chan *sendPostDetails, sendPostBufferSize), + connEstablished: connEstablished, + disconnect: make(chan struct{}), + shutdown: make(chan struct{}), + } + + if start { + log.Infof("Established connection to RPC server %s", + config.Host) + close(connEstablished) + client.start() + if !client.config.HTTPPostMode && !client.config.DisableAutoReconnect { + client.wg.Add(1) + go client.wsReconnectHandler() + } + } + + return client, nil +} + +// Connect establishes the initial websocket connection. This is necessary when +// a client was created after setting the DisableConnectOnNew field of the +// Config struct. +// +// Up to tries number of connections (each after an increasing backoff) will +// be tried if the connection can not be established. The special value of 0 +// indicates an unlimited number of connection attempts. +// +// This method will error if the client is not configured for websockets, if the +// connection has already been established, or if none of the connection +// attempts were successful. +func (c *Client) Connect(tries int) error { + c.mtx.Lock() + defer c.mtx.Unlock() + + if c.config.HTTPPostMode { + return ErrNotWebsocketClient + } + if c.wsConn != nil { + return ErrClientAlreadyConnected + } + + // Begin connection attempts. Increase the backoff after each failed + // attempt, up to a maximum of one minute. + var err error + var backoff time.Duration + for i := 0; tries == 0 || i < tries; i++ { + var wsConn *websocket.Conn + wsConn, err = dial(c.config) + if err != nil { + backoff = connectionRetryInterval * time.Duration(i+1) + if backoff > time.Minute { + backoff = time.Minute + } + time.Sleep(backoff) + continue + } + + // Connection was established. Set the websocket connection + // member of the client and start the goroutines necessary + // to run the client. + log.Infof("Established connection to RPC server %s", + c.config.Host) + c.wsConn = wsConn + close(c.connEstablished) + c.start() + if !c.config.DisableAutoReconnect { + c.wg.Add(1) + go c.wsReconnectHandler() + } + return nil + } + + // All connection attempts failed, so return the last error. + return err +} diff --git a/rpcclient/log.go b/rpcclient/log.go new file mode 100644 index 00000000..30226187 --- /dev/null +++ b/rpcclient/log.go @@ -0,0 +1,47 @@ +// Copyright (c) 2014-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpcclient + +import ( + "github.com/btcsuite/btclog" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until UseLogger is called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +func UseLogger(logger btclog.Logger) { + log = logger +} + +// LogClosure is a closure that can be printed with %v to be used to +// generate expensive-to-create data for a detailed log level and avoid doing +// the work if the data isn't printed. +type logClosure func() string + +// String invokes the log closure and returns the results string. +func (c logClosure) String() string { + return c() +} + +// newLogClosure returns a new closure over the passed function which allows +// it to be used as a parameter in a logging function that is only invoked when +// the logging level is such that the message will actually be logged. +func newLogClosure(c func() string) logClosure { + return logClosure(c) +} diff --git a/rpcclient/mining.go b/rpcclient/mining.go new file mode 100644 index 00000000..76a9d305 --- /dev/null +++ b/rpcclient/mining.go @@ -0,0 +1,417 @@ +// Copyright (c) 2014-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpcclient + +import ( + "encoding/hex" + "encoding/json" + "errors" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcutil" +) + +// FutureGenerateResult is a future promise to deliver the result of a +// GenerateAsync RPC invocation (or an applicable error). +type FutureGenerateResult chan *response + +// Receive waits for the response promised by the future and returns a list of +// block hashes generated by the call. +func (r FutureGenerateResult) Receive() ([]*chainhash.Hash, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a list of strings. + var result []string + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + // Convert each block hash to a chainhash.Hash and store a pointer to + // each. + convertedResult := make([]*chainhash.Hash, len(result)) + for i, hashString := range result { + convertedResult[i], err = chainhash.NewHashFromStr(hashString) + if err != nil { + return nil, err + } + } + + return convertedResult, nil +} + +// GenerateAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See Generate for the blocking version and more details. +func (c *Client) GenerateAsync(numBlocks uint32) FutureGenerateResult { + cmd := btcjson.NewGenerateCmd(numBlocks) + return c.sendCmd(cmd) +} + +// Generate generates numBlocks blocks and returns their hashes. +func (c *Client) Generate(numBlocks uint32) ([]*chainhash.Hash, error) { + return c.GenerateAsync(numBlocks).Receive() +} + +// FutureGetGenerateResult is a future promise to deliver the result of a +// GetGenerateAsync RPC invocation (or an applicable error). +type FutureGetGenerateResult chan *response + +// Receive waits for the response promised by the future and returns true if the +// server is set to mine, otherwise false. +func (r FutureGetGenerateResult) Receive() (bool, error) { + res, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Unmarshal result as a boolean. + var result bool + err = json.Unmarshal(res, &result) + if err != nil { + return false, err + } + + return result, nil +} + +// GetGenerateAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetGenerate for the blocking version and more details. +func (c *Client) GetGenerateAsync() FutureGetGenerateResult { + cmd := btcjson.NewGetGenerateCmd() + return c.sendCmd(cmd) +} + +// GetGenerate returns true if the server is set to mine, otherwise false. +func (c *Client) GetGenerate() (bool, error) { + return c.GetGenerateAsync().Receive() +} + +// FutureSetGenerateResult is a future promise to deliver the result of a +// SetGenerateAsync RPC invocation (or an applicable error). +type FutureSetGenerateResult chan *response + +// Receive waits for the response promised by the future and returns an error if +// any occurred when setting the server to generate coins (mine) or not. +func (r FutureSetGenerateResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// SetGenerateAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SetGenerate for the blocking version and more details. +func (c *Client) SetGenerateAsync(enable bool, numCPUs int) FutureSetGenerateResult { + cmd := btcjson.NewSetGenerateCmd(enable, &numCPUs) + return c.sendCmd(cmd) +} + +// SetGenerate sets the server to generate coins (mine) or not. +func (c *Client) SetGenerate(enable bool, numCPUs int) error { + return c.SetGenerateAsync(enable, numCPUs).Receive() +} + +// FutureGetHashesPerSecResult is a future promise to deliver the result of a +// GetHashesPerSecAsync RPC invocation (or an applicable error). +type FutureGetHashesPerSecResult chan *response + +// Receive waits for the response promised by the future and returns a recent +// hashes per second performance measurement while generating coins (mining). +// Zero is returned if the server is not mining. +func (r FutureGetHashesPerSecResult) Receive() (int64, error) { + res, err := receiveFuture(r) + if err != nil { + return -1, err + } + + // Unmarshal result as an int64. + var result int64 + err = json.Unmarshal(res, &result) + if err != nil { + return 0, err + } + + return result, nil +} + +// GetHashesPerSecAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetHashesPerSec for the blocking version and more details. +func (c *Client) GetHashesPerSecAsync() FutureGetHashesPerSecResult { + cmd := btcjson.NewGetHashesPerSecCmd() + return c.sendCmd(cmd) +} + +// GetHashesPerSec returns a recent hashes per second performance measurement +// while generating coins (mining). Zero is returned if the server is not +// mining. +func (c *Client) GetHashesPerSec() (int64, error) { + return c.GetHashesPerSecAsync().Receive() +} + +// FutureGetMiningInfoResult is a future promise to deliver the result of a +// GetMiningInfoAsync RPC invocation (or an applicable error). +type FutureGetMiningInfoResult chan *response + +// Receive waits for the response promised by the future and returns the mining +// information. +func (r FutureGetMiningInfoResult) Receive() (*btcjson.GetMiningInfoResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a getmininginfo result object. + var infoResult btcjson.GetMiningInfoResult + err = json.Unmarshal(res, &infoResult) + if err != nil { + return nil, err + } + + return &infoResult, nil +} + +// GetMiningInfoAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetMiningInfo for the blocking version and more details. +func (c *Client) GetMiningInfoAsync() FutureGetMiningInfoResult { + cmd := btcjson.NewGetMiningInfoCmd() + return c.sendCmd(cmd) +} + +// GetMiningInfo returns mining information. +func (c *Client) GetMiningInfo() (*btcjson.GetMiningInfoResult, error) { + return c.GetMiningInfoAsync().Receive() +} + +// FutureGetNetworkHashPS is a future promise to deliver the result of a +// GetNetworkHashPSAsync RPC invocation (or an applicable error). +type FutureGetNetworkHashPS chan *response + +// Receive waits for the response promised by the future and returns the +// estimated network hashes per second for the block heights provided by the +// parameters. +func (r FutureGetNetworkHashPS) Receive() (int64, error) { + res, err := receiveFuture(r) + if err != nil { + return -1, err + } + + // Unmarshal result as an int64. + var result int64 + err = json.Unmarshal(res, &result) + if err != nil { + return 0, err + } + + return result, nil +} + +// GetNetworkHashPSAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetNetworkHashPS for the blocking version and more details. +func (c *Client) GetNetworkHashPSAsync() FutureGetNetworkHashPS { + cmd := btcjson.NewGetNetworkHashPSCmd(nil, nil) + return c.sendCmd(cmd) +} + +// GetNetworkHashPS returns the estimated network hashes per second using the +// default number of blocks and the most recent block height. +// +// See GetNetworkHashPS2 to override the number of blocks to use and +// GetNetworkHashPS3 to override the height at which to calculate the estimate. +func (c *Client) GetNetworkHashPS() (int64, error) { + return c.GetNetworkHashPSAsync().Receive() +} + +// GetNetworkHashPS2Async returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetNetworkHashPS2 for the blocking version and more details. +func (c *Client) GetNetworkHashPS2Async(blocks int) FutureGetNetworkHashPS { + cmd := btcjson.NewGetNetworkHashPSCmd(&blocks, nil) + return c.sendCmd(cmd) +} + +// GetNetworkHashPS2 returns the estimated network hashes per second for the +// specified previous number of blocks working backwards from the most recent +// block height. The blocks parameter can also be -1 in which case the number +// of blocks since the last difficulty change will be used. +// +// See GetNetworkHashPS to use defaults and GetNetworkHashPS3 to override the +// height at which to calculate the estimate. +func (c *Client) GetNetworkHashPS2(blocks int) (int64, error) { + return c.GetNetworkHashPS2Async(blocks).Receive() +} + +// GetNetworkHashPS3Async returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetNetworkHashPS3 for the blocking version and more details. +func (c *Client) GetNetworkHashPS3Async(blocks, height int) FutureGetNetworkHashPS { + cmd := btcjson.NewGetNetworkHashPSCmd(&blocks, &height) + return c.sendCmd(cmd) +} + +// GetNetworkHashPS3 returns the estimated network hashes per second for the +// specified previous number of blocks working backwards from the specified +// block height. The blocks parameter can also be -1 in which case the number +// of blocks since the last difficulty change will be used. +// +// See GetNetworkHashPS and GetNetworkHashPS2 to use defaults. +func (c *Client) GetNetworkHashPS3(blocks, height int) (int64, error) { + return c.GetNetworkHashPS3Async(blocks, height).Receive() +} + +// FutureGetWork is a future promise to deliver the result of a +// GetWorkAsync RPC invocation (or an applicable error). +type FutureGetWork chan *response + +// Receive waits for the response promised by the future and returns the hash +// data to work on. +func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a getwork result object. + var result btcjson.GetWorkResult + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return &result, nil +} + +// GetWorkAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetWork for the blocking version and more details. +func (c *Client) GetWorkAsync() FutureGetWork { + cmd := btcjson.NewGetWorkCmd(nil) + return c.sendCmd(cmd) +} + +// GetWork returns hash data to work on. +// +// See GetWorkSubmit to submit the found solution. +func (c *Client) GetWork() (*btcjson.GetWorkResult, error) { + return c.GetWorkAsync().Receive() +} + +// FutureGetWorkSubmit is a future promise to deliver the result of a +// GetWorkSubmitAsync RPC invocation (or an applicable error). +type FutureGetWorkSubmit chan *response + +// Receive waits for the response promised by the future and returns whether +// or not the submitted block header was accepted. +func (r FutureGetWorkSubmit) Receive() (bool, error) { + res, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Unmarshal result as a boolean. + var accepted bool + err = json.Unmarshal(res, &accepted) + if err != nil { + return false, err + } + + return accepted, nil +} + +// GetWorkSubmitAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetWorkSubmit for the blocking version and more details. +func (c *Client) GetWorkSubmitAsync(data string) FutureGetWorkSubmit { + cmd := btcjson.NewGetWorkCmd(&data) + return c.sendCmd(cmd) +} + +// GetWorkSubmit submits a block header which is a solution to previously +// requested data and returns whether or not the solution was accepted. +// +// See GetWork to request data to work on. +func (c *Client) GetWorkSubmit(data string) (bool, error) { + return c.GetWorkSubmitAsync(data).Receive() +} + +// FutureSubmitBlockResult is a future promise to deliver the result of a +// SubmitBlockAsync RPC invocation (or an applicable error). +type FutureSubmitBlockResult chan *response + +// Receive waits for the response promised by the future and returns an error if +// any occurred when submitting the block. +func (r FutureSubmitBlockResult) Receive() error { + res, err := receiveFuture(r) + if err != nil { + return err + } + + if string(res) != "null" { + var result string + err = json.Unmarshal(res, &result) + if err != nil { + return err + } + + return errors.New(result) + } + + return nil + +} + +// SubmitBlockAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SubmitBlock for the blocking version and more details. +func (c *Client) SubmitBlockAsync(block *btcutil.Block, options *btcjson.SubmitBlockOptions) FutureSubmitBlockResult { + blockHex := "" + if block != nil { + blockBytes, err := block.Bytes() + if err != nil { + return newFutureError(err) + } + + blockHex = hex.EncodeToString(blockBytes) + } + + cmd := btcjson.NewSubmitBlockCmd(blockHex, options) + return c.sendCmd(cmd) +} + +// SubmitBlock attempts to submit a new block into the bitcoin network. +func (c *Client) SubmitBlock(block *btcutil.Block, options *btcjson.SubmitBlockOptions) error { + return c.SubmitBlockAsync(block, options).Receive() +} + +// TODO(davec): Implement GetBlockTemplate diff --git a/rpcclient/net.go b/rpcclient/net.go new file mode 100644 index 00000000..aeda8b3c --- /dev/null +++ b/rpcclient/net.go @@ -0,0 +1,285 @@ +// Copyright (c) 2014-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpcclient + +import ( + "encoding/json" + + "github.com/btcsuite/btcd/btcjson" +) + +// AddNodeCommand enumerates the available commands that the AddNode function +// accepts. +type AddNodeCommand string + +// Constants used to indicate the command for the AddNode function. +const ( + // ANAdd indicates the specified host should be added as a persistent + // peer. + ANAdd AddNodeCommand = "add" + + // ANRemove indicates the specified peer should be removed. + ANRemove AddNodeCommand = "remove" + + // ANOneTry indicates the specified host should try to connect once, + // but it should not be made persistent. + ANOneTry AddNodeCommand = "onetry" +) + +// String returns the AddNodeCommand in human-readable form. +func (cmd AddNodeCommand) String() string { + return string(cmd) +} + +// FutureAddNodeResult is a future promise to deliver the result of an +// AddNodeAsync RPC invocation (or an applicable error). +type FutureAddNodeResult chan *response + +// Receive waits for the response promised by the future and returns an error if +// any occurred when performing the specified command. +func (r FutureAddNodeResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// AddNodeAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See AddNode for the blocking version and more details. +func (c *Client) AddNodeAsync(host string, command AddNodeCommand) FutureAddNodeResult { + cmd := btcjson.NewAddNodeCmd(host, btcjson.AddNodeSubCmd(command)) + return c.sendCmd(cmd) +} + +// AddNode attempts to perform the passed command on the passed persistent peer. +// For example, it can be used to add or a remove a persistent peer, or to do +// a one time connection to a peer. +// +// It may not be used to remove non-persistent peers. +func (c *Client) AddNode(host string, command AddNodeCommand) error { + return c.AddNodeAsync(host, command).Receive() +} + +// FutureGetAddedNodeInfoResult is a future promise to deliver the result of a +// GetAddedNodeInfoAsync RPC invocation (or an applicable error). +type FutureGetAddedNodeInfoResult chan *response + +// Receive waits for the response promised by the future and returns information +// about manually added (persistent) peers. +func (r FutureGetAddedNodeInfoResult) Receive() ([]btcjson.GetAddedNodeInfoResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of getaddednodeinfo result objects. + var nodeInfo []btcjson.GetAddedNodeInfoResult + err = json.Unmarshal(res, &nodeInfo) + if err != nil { + return nil, err + } + + return nodeInfo, nil +} + +// GetAddedNodeInfoAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetAddedNodeInfo for the blocking version and more details. +func (c *Client) GetAddedNodeInfoAsync(peer string) FutureGetAddedNodeInfoResult { + cmd := btcjson.NewGetAddedNodeInfoCmd(true, &peer) + return c.sendCmd(cmd) +} + +// GetAddedNodeInfo returns information about manually added (persistent) peers. +// +// See GetAddedNodeInfoNoDNS to retrieve only a list of the added (persistent) +// peers. +func (c *Client) GetAddedNodeInfo(peer string) ([]btcjson.GetAddedNodeInfoResult, error) { + return c.GetAddedNodeInfoAsync(peer).Receive() +} + +// FutureGetAddedNodeInfoNoDNSResult is a future promise to deliver the result +// of a GetAddedNodeInfoNoDNSAsync RPC invocation (or an applicable error). +type FutureGetAddedNodeInfoNoDNSResult chan *response + +// Receive waits for the response promised by the future and returns a list of +// manually added (persistent) peers. +func (r FutureGetAddedNodeInfoNoDNSResult) Receive() ([]string, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as an array of strings. + var nodes []string + err = json.Unmarshal(res, &nodes) + if err != nil { + return nil, err + } + + return nodes, nil +} + +// GetAddedNodeInfoNoDNSAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetAddedNodeInfoNoDNS for the blocking version and more details. +func (c *Client) GetAddedNodeInfoNoDNSAsync(peer string) FutureGetAddedNodeInfoNoDNSResult { + cmd := btcjson.NewGetAddedNodeInfoCmd(false, &peer) + return c.sendCmd(cmd) +} + +// GetAddedNodeInfoNoDNS returns a list of manually added (persistent) peers. +// This works by setting the dns flag to false in the underlying RPC. +// +// See GetAddedNodeInfo to obtain more information about each added (persistent) +// peer. +func (c *Client) GetAddedNodeInfoNoDNS(peer string) ([]string, error) { + return c.GetAddedNodeInfoNoDNSAsync(peer).Receive() +} + +// FutureGetConnectionCountResult is a future promise to deliver the result +// of a GetConnectionCountAsync RPC invocation (or an applicable error). +type FutureGetConnectionCountResult chan *response + +// Receive waits for the response promised by the future and returns the number +// of active connections to other peers. +func (r FutureGetConnectionCountResult) Receive() (int64, error) { + res, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Unmarshal result as an int64. + var count int64 + err = json.Unmarshal(res, &count) + if err != nil { + return 0, err + } + + return count, nil +} + +// GetConnectionCountAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetConnectionCount for the blocking version and more details. +func (c *Client) GetConnectionCountAsync() FutureGetConnectionCountResult { + cmd := btcjson.NewGetConnectionCountCmd() + return c.sendCmd(cmd) +} + +// GetConnectionCount returns the number of active connections to other peers. +func (c *Client) GetConnectionCount() (int64, error) { + return c.GetConnectionCountAsync().Receive() +} + +// FuturePingResult is a future promise to deliver the result of a PingAsync RPC +// invocation (or an applicable error). +type FuturePingResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of queueing a ping to be sent to each connected peer. +func (r FuturePingResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// PingAsync returns an instance of a type that can be used to get the result of +// the RPC at some future time by invoking the Receive function on the returned +// instance. +// +// See Ping for the blocking version and more details. +func (c *Client) PingAsync() FuturePingResult { + cmd := btcjson.NewPingCmd() + return c.sendCmd(cmd) +} + +// Ping queues a ping to be sent to each connected peer. +// +// Use the GetPeerInfo function and examine the PingTime and PingWait fields to +// access the ping times. +func (c *Client) Ping() error { + return c.PingAsync().Receive() +} + +// FutureGetPeerInfoResult is a future promise to deliver the result of a +// GetPeerInfoAsync RPC invocation (or an applicable error). +type FutureGetPeerInfoResult chan *response + +// Receive waits for the response promised by the future and returns data about +// each connected network peer. +func (r FutureGetPeerInfoResult) Receive() ([]btcjson.GetPeerInfoResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as an array of getpeerinfo result objects. + var peerInfo []btcjson.GetPeerInfoResult + err = json.Unmarshal(res, &peerInfo) + if err != nil { + return nil, err + } + + return peerInfo, nil +} + +// GetPeerInfoAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetPeerInfo for the blocking version and more details. +func (c *Client) GetPeerInfoAsync() FutureGetPeerInfoResult { + cmd := btcjson.NewGetPeerInfoCmd() + return c.sendCmd(cmd) +} + +// GetPeerInfo returns data about each connected network peer. +func (c *Client) GetPeerInfo() ([]btcjson.GetPeerInfoResult, error) { + return c.GetPeerInfoAsync().Receive() +} + +// FutureGetNetTotalsResult is a future promise to deliver the result of a +// GetNetTotalsAsync RPC invocation (or an applicable error). +type FutureGetNetTotalsResult chan *response + +// Receive waits for the response promised by the future and returns network +// traffic statistics. +func (r FutureGetNetTotalsResult) Receive() (*btcjson.GetNetTotalsResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a getnettotals result object. + var totals btcjson.GetNetTotalsResult + err = json.Unmarshal(res, &totals) + if err != nil { + return nil, err + } + + return &totals, nil +} + +// GetNetTotalsAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetNetTotals for the blocking version and more details. +func (c *Client) GetNetTotalsAsync() FutureGetNetTotalsResult { + cmd := btcjson.NewGetNetTotalsCmd() + return c.sendCmd(cmd) +} + +// GetNetTotals returns network traffic statistics. +func (c *Client) GetNetTotals() (*btcjson.GetNetTotalsResult, error) { + return c.GetNetTotalsAsync().Receive() +} diff --git a/rpcclient/notify.go b/rpcclient/notify.go new file mode 100644 index 00000000..9a0a3568 --- /dev/null +++ b/rpcclient/notify.go @@ -0,0 +1,1357 @@ +// Copyright (c) 2014-2017 The btcsuite developers +// Copyright (c) 2015-2017 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpcclient + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +var ( + // ErrWebsocketsRequired is an error to describe the condition where the + // caller is trying to use a websocket-only feature, such as requesting + // notifications or other websocket requests when the client is + // configured to run in HTTP POST mode. + ErrWebsocketsRequired = errors.New("a websocket connection is required " + + "to use this feature") +) + +// notificationState is used to track the current state of successfuly +// registered notification so the state can be automatically re-established on +// reconnect. +type notificationState struct { + notifyBlocks bool + notifyNewTx bool + notifyNewTxVerbose bool + notifyReceived map[string]struct{} + notifySpent map[btcjson.OutPoint]struct{} +} + +// Copy returns a deep copy of the receiver. +func (s *notificationState) Copy() *notificationState { + var stateCopy notificationState + stateCopy.notifyBlocks = s.notifyBlocks + stateCopy.notifyNewTx = s.notifyNewTx + stateCopy.notifyNewTxVerbose = s.notifyNewTxVerbose + stateCopy.notifyReceived = make(map[string]struct{}) + for addr := range s.notifyReceived { + stateCopy.notifyReceived[addr] = struct{}{} + } + stateCopy.notifySpent = make(map[btcjson.OutPoint]struct{}) + for op := range s.notifySpent { + stateCopy.notifySpent[op] = struct{}{} + } + + return &stateCopy +} + +// newNotificationState returns a new notification state ready to be populated. +func newNotificationState() *notificationState { + return ¬ificationState{ + notifyReceived: make(map[string]struct{}), + notifySpent: make(map[btcjson.OutPoint]struct{}), + } +} + +// newNilFutureResult returns a new future result channel that already has the +// result waiting on the channel with the reply set to nil. This is useful +// to ignore things such as notifications when the caller didn't specify any +// notification handlers. +func newNilFutureResult() chan *response { + responseChan := make(chan *response, 1) + responseChan <- &response{result: nil, err: nil} + return responseChan +} + +// NotificationHandlers defines callback function pointers to invoke with +// notifications. Since all of the functions are nil by default, all +// notifications are effectively ignored until their handlers are set to a +// concrete callback. +// +// NOTE: Unless otherwise documented, these handlers must NOT directly call any +// blocking calls on the client instance since the input reader goroutine blocks +// until the callback has completed. Doing so will result in a deadlock +// situation. +type NotificationHandlers struct { + // OnClientConnected is invoked when the client connects or reconnects + // to the RPC server. This callback is run async with the rest of the + // notification handlers, and is safe for blocking client requests. + OnClientConnected func() + + // OnBlockConnected is invoked when a block is connected to the longest + // (best) chain. It will only be invoked if a preceding call to + // NotifyBlocks has been made to register for the notification and the + // function is non-nil. + // + // NOTE: Deprecated. Use OnFilteredBlockConnected instead. + OnBlockConnected func(hash *chainhash.Hash, height int32, t time.Time) + + // OnFilteredBlockConnected is invoked when a block is connected to the + // longest (best) chain. It will only be invoked if a preceding call to + // NotifyBlocks has been made to register for the notification and the + // function is non-nil. Its parameters differ from OnBlockConnected: it + // receives the block's height, header, and relevant transactions. + OnFilteredBlockConnected func(height int32, header *wire.BlockHeader, + txs []*btcutil.Tx) + + // OnBlockDisconnected is invoked when a block is disconnected from the + // longest (best) chain. It will only be invoked if a preceding call to + // NotifyBlocks has been made to register for the notification and the + // function is non-nil. + // + // NOTE: Deprecated. Use OnFilteredBlockDisconnected instead. + OnBlockDisconnected func(hash *chainhash.Hash, height int32, t time.Time) + + // OnFilteredBlockDisconnected is invoked when a block is disconnected + // from the longest (best) chain. It will only be invoked if a + // preceding NotifyBlocks has been made to register for the notification + // and the call to function is non-nil. Its parameters differ from + // OnBlockDisconnected: it receives the block's height and header. + OnFilteredBlockDisconnected func(height int32, header *wire.BlockHeader) + + // OnRecvTx is invoked when a transaction that receives funds to a + // registered address is received into the memory pool and also + // connected to the longest (best) chain. It will only be invoked if a + // preceding call to NotifyReceived, Rescan, or RescanEndHeight has been + // made to register for the notification and the function is non-nil. + // + // NOTE: Deprecated. Use OnRelevantTxAccepted instead. + OnRecvTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails) + + // OnRedeemingTx is invoked when a transaction that spends a registered + // outpoint is received into the memory pool and also connected to the + // longest (best) chain. It will only be invoked if a preceding call to + // NotifySpent, Rescan, or RescanEndHeight has been made to register for + // the notification and the function is non-nil. + // + // NOTE: The NotifyReceived will automatically register notifications + // for the outpoints that are now "owned" as a result of receiving + // funds to the registered addresses. This means it is possible for + // this to invoked indirectly as the result of a NotifyReceived call. + // + // NOTE: Deprecated. Use OnRelevantTxAccepted instead. + OnRedeemingTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails) + + // OnRelevantTxAccepted is invoked when an unmined transaction passes + // the client's transaction filter. + // + // NOTE: This is a btcsuite extension ported from + // github.com/decred/dcrrpcclient. + OnRelevantTxAccepted func(transaction []byte) + + // OnRescanFinished is invoked after a rescan finishes due to a previous + // call to Rescan or RescanEndHeight. Finished rescans should be + // signaled on this notification, rather than relying on the return + // result of a rescan request, due to how btcd may send various rescan + // notifications after the rescan request has already returned. + // + // NOTE: Deprecated. Not used with RescanBlocks. + OnRescanFinished func(hash *chainhash.Hash, height int32, blkTime time.Time) + + // OnRescanProgress is invoked periodically when a rescan is underway. + // It will only be invoked if a preceding call to Rescan or + // RescanEndHeight has been made and the function is non-nil. + // + // NOTE: Deprecated. Not used with RescanBlocks. + OnRescanProgress func(hash *chainhash.Hash, height int32, blkTime time.Time) + + // OnTxAccepted is invoked when a transaction is accepted into the + // memory pool. It will only be invoked if a preceding call to + // NotifyNewTransactions with the verbose flag set to false has been + // made to register for the notification and the function is non-nil. + OnTxAccepted func(hash *chainhash.Hash, amount btcutil.Amount) + + // OnTxAccepted is invoked when a transaction is accepted into the + // memory pool. It will only be invoked if a preceding call to + // NotifyNewTransactions with the verbose flag set to true has been + // made to register for the notification and the function is non-nil. + OnTxAcceptedVerbose func(txDetails *btcjson.TxRawResult) + + // OnBtcdConnected is invoked when a wallet connects or disconnects from + // btcd. + // + // This will only be available when client is connected to a wallet + // server such as btcwallet. + OnBtcdConnected func(connected bool) + + // OnAccountBalance is invoked with account balance updates. + // + // This will only be available when speaking to a wallet server + // such as btcwallet. + OnAccountBalance func(account string, balance btcutil.Amount, confirmed bool) + + // OnWalletLockState is invoked when a wallet is locked or unlocked. + // + // This will only be available when client is connected to a wallet + // server such as btcwallet. + OnWalletLockState func(locked bool) + + // OnUnknownNotification is invoked when an unrecognized notification + // is received. This typically means the notification handling code + // for this package needs to be updated for a new notification type or + // the caller is using a custom notification this package does not know + // about. + OnUnknownNotification func(method string, params []json.RawMessage) +} + +// handleNotification examines the passed notification type, performs +// conversions to get the raw notification types into higher level types and +// delivers the notification to the appropriate On handler registered with +// the client. +func (c *Client) handleNotification(ntfn *rawNotification) { + // Ignore the notification if the client is not interested in any + // notifications. + if c.ntfnHandlers == nil { + return + } + + switch ntfn.Method { + // OnBlockConnected + case btcjson.BlockConnectedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnBlockConnected == nil { + return + } + + blockHash, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid block connected "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnBlockConnected(blockHash, blockHeight, blockTime) + + // OnFilteredBlockConnected + case btcjson.FilteredBlockConnectedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnFilteredBlockConnected == nil { + return + } + + blockHeight, blockHeader, transactions, err := + parseFilteredBlockConnectedParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid filtered block "+ + "connected notification: %v", err) + return + } + + c.ntfnHandlers.OnFilteredBlockConnected(blockHeight, + blockHeader, transactions) + + // OnBlockDisconnected + case btcjson.BlockDisconnectedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnBlockDisconnected == nil { + return + } + + blockHash, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid block connected "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnBlockDisconnected(blockHash, blockHeight, blockTime) + + // OnFilteredBlockDisconnected + case btcjson.FilteredBlockDisconnectedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnFilteredBlockDisconnected == nil { + return + } + + blockHeight, blockHeader, err := + parseFilteredBlockDisconnectedParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid filtered block "+ + "disconnected notification: %v", err) + return + } + + c.ntfnHandlers.OnFilteredBlockDisconnected(blockHeight, + blockHeader) + + // OnRecvTx + case btcjson.RecvTxNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnRecvTx == nil { + return + } + + tx, block, err := parseChainTxNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid recvtx notification: %v", + err) + return + } + + c.ntfnHandlers.OnRecvTx(tx, block) + + // OnRedeemingTx + case btcjson.RedeemingTxNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnRedeemingTx == nil { + return + } + + tx, block, err := parseChainTxNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid redeemingtx "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnRedeemingTx(tx, block) + + // OnRelevantTxAccepted + case btcjson.RelevantTxAcceptedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnRelevantTxAccepted == nil { + return + } + + transaction, err := parseRelevantTxAcceptedParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid relevanttxaccepted "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnRelevantTxAccepted(transaction) + + // OnRescanFinished + case btcjson.RescanFinishedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnRescanFinished == nil { + return + } + + hash, height, blkTime, err := parseRescanProgressParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid rescanfinished "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnRescanFinished(hash, height, blkTime) + + // OnRescanProgress + case btcjson.RescanProgressNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnRescanProgress == nil { + return + } + + hash, height, blkTime, err := parseRescanProgressParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid rescanprogress "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnRescanProgress(hash, height, blkTime) + + // OnTxAccepted + case btcjson.TxAcceptedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnTxAccepted == nil { + return + } + + hash, amt, err := parseTxAcceptedNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid tx accepted "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnTxAccepted(hash, amt) + + // OnTxAcceptedVerbose + case btcjson.TxAcceptedVerboseNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnTxAcceptedVerbose == nil { + return + } + + rawTx, err := parseTxAcceptedVerboseNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid tx accepted verbose "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnTxAcceptedVerbose(rawTx) + + // OnBtcdConnected + case btcjson.BtcdConnectedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnBtcdConnected == nil { + return + } + + connected, err := parseBtcdConnectedNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid btcd connected "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnBtcdConnected(connected) + + // OnAccountBalance + case btcjson.AccountBalanceNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnAccountBalance == nil { + return + } + + account, bal, conf, err := parseAccountBalanceNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid account balance "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnAccountBalance(account, bal, conf) + + // OnWalletLockState + case btcjson.WalletLockStateNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnWalletLockState == nil { + return + } + + // The account name is not notified, so the return value is + // discarded. + _, locked, err := parseWalletLockStateNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid wallet lock state "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnWalletLockState(locked) + + // OnUnknownNotification + default: + if c.ntfnHandlers.OnUnknownNotification == nil { + return + } + + c.ntfnHandlers.OnUnknownNotification(ntfn.Method, ntfn.Params) + } +} + +// wrongNumParams is an error type describing an unparseable JSON-RPC +// notificiation due to an incorrect number of parameters for the +// expected notification type. The value is the number of parameters +// of the invalid notification. +type wrongNumParams int + +// Error satisifies the builtin error interface. +func (e wrongNumParams) Error() string { + return fmt.Sprintf("wrong number of parameters (%d)", e) +} + +// parseChainNtfnParams parses out the block hash and height from the parameters +// of blockconnected and blockdisconnected notifications. +func parseChainNtfnParams(params []json.RawMessage) (*chainhash.Hash, + int32, time.Time, error) { + + if len(params) != 3 { + return nil, 0, time.Time{}, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + var blockHashStr string + err := json.Unmarshal(params[0], &blockHashStr) + if err != nil { + return nil, 0, time.Time{}, err + } + + // Unmarshal second parameter as an integer. + var blockHeight int32 + err = json.Unmarshal(params[1], &blockHeight) + if err != nil { + return nil, 0, time.Time{}, err + } + + // Unmarshal third parameter as unix time. + var blockTimeUnix int64 + err = json.Unmarshal(params[2], &blockTimeUnix) + if err != nil { + return nil, 0, time.Time{}, err + } + + // Create hash from block hash string. + blockHash, err := chainhash.NewHashFromStr(blockHashStr) + if err != nil { + return nil, 0, time.Time{}, err + } + + // Create time.Time from unix time. + blockTime := time.Unix(blockTimeUnix, 0) + + return blockHash, blockHeight, blockTime, nil +} + +// parseFilteredBlockConnectedParams parses out the parameters included in a +// filteredblockconnected notification. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func parseFilteredBlockConnectedParams(params []json.RawMessage) (int32, + *wire.BlockHeader, []*btcutil.Tx, error) { + + if len(params) < 3 { + return 0, nil, nil, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as an integer. + var blockHeight int32 + err := json.Unmarshal(params[0], &blockHeight) + if err != nil { + return 0, nil, nil, err + } + + // Unmarshal second parameter as a slice of bytes. + blockHeaderBytes, err := parseHexParam(params[1]) + if err != nil { + return 0, nil, nil, err + } + + // Deserialize block header from slice of bytes. + var blockHeader wire.BlockHeader + err = blockHeader.Deserialize(bytes.NewReader(blockHeaderBytes)) + if err != nil { + return 0, nil, nil, err + } + + // Unmarshal third parameter as a slice of hex-encoded strings. + var hexTransactions []string + err = json.Unmarshal(params[2], &hexTransactions) + if err != nil { + return 0, nil, nil, err + } + + // Create slice of transactions from slice of strings by hex-decoding. + transactions := make([]*btcutil.Tx, len(hexTransactions)) + for i, hexTx := range hexTransactions { + transaction, err := hex.DecodeString(hexTx) + if err != nil { + return 0, nil, nil, err + } + + transactions[i], err = btcutil.NewTxFromBytes(transaction) + if err != nil { + return 0, nil, nil, err + } + } + + return blockHeight, &blockHeader, transactions, nil +} + +// parseFilteredBlockDisconnectedParams parses out the parameters included in a +// filteredblockdisconnected notification. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func parseFilteredBlockDisconnectedParams(params []json.RawMessage) (int32, + *wire.BlockHeader, error) { + if len(params) < 2 { + return 0, nil, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as an integer. + var blockHeight int32 + err := json.Unmarshal(params[0], &blockHeight) + if err != nil { + return 0, nil, err + } + + // Unmarshal second parmeter as a slice of bytes. + blockHeaderBytes, err := parseHexParam(params[1]) + if err != nil { + return 0, nil, err + } + + // Deserialize block header from slice of bytes. + var blockHeader wire.BlockHeader + err = blockHeader.Deserialize(bytes.NewReader(blockHeaderBytes)) + if err != nil { + return 0, nil, err + } + + return blockHeight, &blockHeader, nil +} + +func parseHexParam(param json.RawMessage) ([]byte, error) { + var s string + err := json.Unmarshal(param, &s) + if err != nil { + return nil, err + } + return hex.DecodeString(s) +} + +// parseRelevantTxAcceptedParams parses out the parameter included in a +// relevanttxaccepted notification. +func parseRelevantTxAcceptedParams(params []json.RawMessage) (transaction []byte, err error) { + if len(params) < 1 { + return nil, wrongNumParams(len(params)) + } + + return parseHexParam(params[0]) +} + +// parseChainTxNtfnParams parses out the transaction and optional details about +// the block it's mined in from the parameters of recvtx and redeemingtx +// notifications. +func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, + *btcjson.BlockDetails, error) { + + if len(params) == 0 || len(params) > 2 { + return nil, nil, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + var txHex string + err := json.Unmarshal(params[0], &txHex) + if err != nil { + return nil, nil, err + } + + // If present, unmarshal second optional parameter as the block details + // JSON object. + var block *btcjson.BlockDetails + if len(params) > 1 { + err = json.Unmarshal(params[1], &block) + if err != nil { + return nil, nil, err + } + } + + // Hex decode and deserialize the transaction. + serializedTx, err := hex.DecodeString(txHex) + if err != nil { + return nil, nil, err + } + var msgTx wire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + return nil, nil, err + } + + // TODO: Change recvtx and redeemingtx callback signatures to use + // nicer types for details about the block (block hash as a + // chainhash.Hash, block time as a time.Time, etc.). + return btcutil.NewTx(&msgTx), block, nil +} + +// parseRescanProgressParams parses out the height of the last rescanned block +// from the parameters of rescanfinished and rescanprogress notifications. +func parseRescanProgressParams(params []json.RawMessage) (*chainhash.Hash, int32, time.Time, error) { + if len(params) != 3 { + return nil, 0, time.Time{}, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as an string. + var hashStr string + err := json.Unmarshal(params[0], &hashStr) + if err != nil { + return nil, 0, time.Time{}, err + } + + // Unmarshal second parameter as an integer. + var height int32 + err = json.Unmarshal(params[1], &height) + if err != nil { + return nil, 0, time.Time{}, err + } + + // Unmarshal third parameter as an integer. + var blkTime int64 + err = json.Unmarshal(params[2], &blkTime) + if err != nil { + return nil, 0, time.Time{}, err + } + + // Decode string encoding of block hash. + hash, err := chainhash.NewHashFromStr(hashStr) + if err != nil { + return nil, 0, time.Time{}, err + } + + return hash, height, time.Unix(blkTime, 0), nil +} + +// parseTxAcceptedNtfnParams parses out the transaction hash and total amount +// from the parameters of a txaccepted notification. +func parseTxAcceptedNtfnParams(params []json.RawMessage) (*chainhash.Hash, + btcutil.Amount, error) { + + if len(params) != 2 { + return nil, 0, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + var txHashStr string + err := json.Unmarshal(params[0], &txHashStr) + if err != nil { + return nil, 0, err + } + + // Unmarshal second parameter as a floating point number. + var famt float64 + err = json.Unmarshal(params[1], &famt) + if err != nil { + return nil, 0, err + } + + // Bounds check amount. + amt, err := btcutil.NewAmount(famt) + if err != nil { + return nil, 0, err + } + + // Decode string encoding of transaction sha. + txHash, err := chainhash.NewHashFromStr(txHashStr) + if err != nil { + return nil, 0, err + } + + return txHash, amt, nil +} + +// parseTxAcceptedVerboseNtfnParams parses out details about a raw transaction +// from the parameters of a txacceptedverbose notification. +func parseTxAcceptedVerboseNtfnParams(params []json.RawMessage) (*btcjson.TxRawResult, + error) { + + if len(params) != 1 { + return nil, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a raw transaction result object. + var rawTx btcjson.TxRawResult + err := json.Unmarshal(params[0], &rawTx) + if err != nil { + return nil, err + } + + // TODO: change txacceptedverbose notification callbacks to use nicer + // types for all details about the transaction (i.e. decoding hashes + // from their string encoding). + return &rawTx, nil +} + +// parseBtcdConnectedNtfnParams parses out the connection status of btcd +// and btcwallet from the parameters of a btcdconnected notification. +func parseBtcdConnectedNtfnParams(params []json.RawMessage) (bool, error) { + if len(params) != 1 { + return false, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a boolean. + var connected bool + err := json.Unmarshal(params[0], &connected) + if err != nil { + return false, err + } + + return connected, nil +} + +// parseAccountBalanceNtfnParams parses out the account name, total balance, +// and whether or not the balance is confirmed or unconfirmed from the +// parameters of an accountbalance notification. +func parseAccountBalanceNtfnParams(params []json.RawMessage) (account string, + balance btcutil.Amount, confirmed bool, err error) { + + if len(params) != 3 { + return "", 0, false, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + err = json.Unmarshal(params[0], &account) + if err != nil { + return "", 0, false, err + } + + // Unmarshal second parameter as a floating point number. + var fbal float64 + err = json.Unmarshal(params[1], &fbal) + if err != nil { + return "", 0, false, err + } + + // Unmarshal third parameter as a boolean. + err = json.Unmarshal(params[2], &confirmed) + if err != nil { + return "", 0, false, err + } + + // Bounds check amount. + bal, err := btcutil.NewAmount(fbal) + if err != nil { + return "", 0, false, err + } + + return account, bal, confirmed, nil +} + +// parseWalletLockStateNtfnParams parses out the account name and locked +// state of an account from the parameters of a walletlockstate notification. +func parseWalletLockStateNtfnParams(params []json.RawMessage) (account string, + locked bool, err error) { + + if len(params) != 2 { + return "", false, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + err = json.Unmarshal(params[0], &account) + if err != nil { + return "", false, err + } + + // Unmarshal second parameter as a boolean. + err = json.Unmarshal(params[1], &locked) + if err != nil { + return "", false, err + } + + return account, locked, nil +} + +// FutureNotifyBlocksResult is a future promise to deliver the result of a +// NotifyBlocksAsync RPC invocation (or an applicable error). +type FutureNotifyBlocksResult chan *response + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifyBlocksResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// NotifyBlocksAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See NotifyBlocks for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyBlocksAsync() FutureNotifyBlocksResult { + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + cmd := btcjson.NewNotifyBlocksCmd() + return c.sendCmd(cmd) +} + +// NotifyBlocks registers the client to receive notifications when blocks are +// connected and disconnected from the main chain. The notifications are +// delivered to the notification handlers associated with the client. Calling +// this function has no effect if there are no notification handlers and will +// result in an error if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnBlockConnected or OnBlockDisconnected. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyBlocks() error { + return c.NotifyBlocksAsync().Receive() +} + +// FutureNotifySpentResult is a future promise to deliver the result of a +// NotifySpentAsync RPC invocation (or an applicable error). +// +// NOTE: Deprecated. Use FutureLoadTxFilterResult instead. +type FutureNotifySpentResult chan *response + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifySpentResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// 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 []btcjson.OutPoint) FutureNotifySpentResult { + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + cmd := btcjson.NewNotifySpentCmd(outpoints) + return c.sendCmd(cmd) +} + +// newOutPointFromWire constructs the btcjson representation of a transaction +// outpoint from the wire type. +func newOutPointFromWire(op *wire.OutPoint) btcjson.OutPoint { + return btcjson.OutPoint{ + Hash: op.Hash.String(), + Index: op.Index, + } +} + +// NotifySpentAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See NotifySpent for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use LoadTxFilterAsync instead. +func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentResult { + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + ops := make([]btcjson.OutPoint, 0, len(outpoints)) + for _, outpoint := range outpoints { + ops = append(ops, newOutPointFromWire(outpoint)) + } + cmd := btcjson.NewNotifySpentCmd(ops) + return c.sendCmd(cmd) +} + +// NotifySpent registers the client to receive notifications when the passed +// transaction outputs are spent. The notifications are delivered to the +// notification handlers associated with the client. Calling this function has +// no effect if there are no notification handlers and will result in an error +// if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via +// OnRedeemingTx. +// +// NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use LoadTxFilter instead. +func (c *Client) NotifySpent(outpoints []*wire.OutPoint) error { + return c.NotifySpentAsync(outpoints).Receive() +} + +// FutureNotifyNewTransactionsResult is a future promise to deliver the result +// of a NotifyNewTransactionsAsync RPC invocation (or an applicable error). +type FutureNotifyNewTransactionsResult chan *response + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifyNewTransactionsResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// NotifyNewTransactionsAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See NotifyNewTransactionsAsync for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyNewTransactionsAsync(verbose bool) FutureNotifyNewTransactionsResult { + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + cmd := btcjson.NewNotifyNewTransactionsCmd(&verbose) + return c.sendCmd(cmd) +} + +// NotifyNewTransactions registers the client to receive notifications every +// time a new transaction is accepted to the memory pool. The notifications are +// delivered to the notification handlers associated with the client. Calling +// this function has no effect if there are no notification handlers and will +// result in an error if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnTxAccepted (when verbose is false) or OnTxAcceptedVerbose (when verbose is +// true). +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyNewTransactions(verbose bool) error { + return c.NotifyNewTransactionsAsync(verbose).Receive() +} + +// FutureNotifyReceivedResult is a future promise to deliver the result of a +// NotifyReceivedAsync RPC invocation (or an applicable error). +// +// NOTE: Deprecated. Use FutureLoadTxFilterResult instead. +type FutureNotifyReceivedResult chan *response + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifyReceivedResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// notifyReceivedInternal is the same as notifyReceivedAsync except it accepts +// the converted addresses as a parameter so the client can more efficiently +// recreate the previous notification state on reconnect. +func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceivedResult { + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert addresses to strings. + cmd := btcjson.NewNotifyReceivedCmd(addresses) + return c.sendCmd(cmd) +} + +// NotifyReceivedAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See NotifyReceived for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use LoadTxFilterAsync instead. +func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyReceivedResult { + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.String()) + } + cmd := btcjson.NewNotifyReceivedCmd(addrs) + return c.sendCmd(cmd) +} + +// NotifyReceived registers the client to receive notifications every time a +// new transaction which pays to one of the passed addresses is accepted to +// memory pool or in a block connected to the block chain. In addition, when +// one of these transactions is detected, the client is also automatically +// registered for notifications when the new transaction outpoints the address +// now has available are spent (See NotifySpent). The notifications are +// delivered to the notification handlers associated with the client. Calling +// this function has no effect if there are no notification handlers and will +// result in an error if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// *OnRecvTx (for transactions that receive funds to one of the passed +// addresses) or OnRedeemingTx (for transactions which spend from one +// of the outpoints which are automatically registered upon receipt of funds to +// the address). +// +// NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use LoadTxFilter instead. +func (c *Client) NotifyReceived(addresses []btcutil.Address) error { + return c.NotifyReceivedAsync(addresses).Receive() +} + +// FutureRescanResult is a future promise to deliver the result of a RescanAsync +// or RescanEndHeightAsync RPC invocation (or an applicable error). +// +// NOTE: Deprecated. Use FutureRescanBlocksResult instead. +type FutureRescanResult chan *response + +// Receive waits for the response promised by the future and returns an error +// if the rescan was not successful. +func (r FutureRescanResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// RescanAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See Rescan for the blocking version and more details. +// +// NOTE: Rescan requests are not issued on client reconnect and must be +// performed manually (ideally with a new start height based on the last +// rescan progress notification). See the OnClientConnected notification +// callback for a good callsite to reissue rescan requests on connect and +// reconnect. +// +// NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use RescanBlocksAsync instead. +func (c *Client) RescanAsync(startBlock *chainhash.Hash, + addresses []btcutil.Address, + outpoints []*wire.OutPoint) FutureRescanResult { + + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert block hashes to strings. + var startBlockHashStr string + if startBlock != nil { + startBlockHashStr = startBlock.String() + } + + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.String()) + } + + // Convert outpoints. + ops := make([]btcjson.OutPoint, 0, len(outpoints)) + for _, op := range outpoints { + ops = append(ops, newOutPointFromWire(op)) + } + + cmd := btcjson.NewRescanCmd(startBlockHashStr, addrs, ops, nil) + return c.sendCmd(cmd) +} + +// Rescan rescans the block chain starting from the provided starting block to +// the end of the longest chain for transactions that pay to the passed +// addresses and transactions which spend the passed outpoints. +// +// The notifications of found transactions are delivered to the notification +// handlers associated with client and this call will not return until the +// rescan has completed. Calling this function has no effect if there are no +// notification handlers and will result in an error if the client is configured +// to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnRedeemingTx (for transactions which spend from the one of the +// passed outpoints), OnRecvTx (for transactions that receive funds +// to one of the passed addresses), and OnRescanProgress (for rescan progress +// updates). +// +// See RescanEndBlock to also specify an ending block to finish the rescan +// without continuing through the best block on the main chain. +// +// NOTE: Rescan requests are not issued on client reconnect and must be +// performed manually (ideally with a new start height based on the last +// rescan progress notification). See the OnClientConnected notification +// callback for a good callsite to reissue rescan requests on connect and +// reconnect. +// +// NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use RescanBlocks instead. +func (c *Client) Rescan(startBlock *chainhash.Hash, + addresses []btcutil.Address, + outpoints []*wire.OutPoint) error { + + return c.RescanAsync(startBlock, addresses, outpoints).Receive() +} + +// RescanEndBlockAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See RescanEndBlock for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use RescanBlocksAsync instead. +func (c *Client) RescanEndBlockAsync(startBlock *chainhash.Hash, + addresses []btcutil.Address, outpoints []*wire.OutPoint, + endBlock *chainhash.Hash) FutureRescanResult { + + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert block hashes to strings. + var startBlockHashStr, endBlockHashStr string + if startBlock != nil { + startBlockHashStr = startBlock.String() + } + if endBlock != nil { + endBlockHashStr = endBlock.String() + } + + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.String()) + } + + // Convert outpoints. + ops := make([]btcjson.OutPoint, 0, len(outpoints)) + for _, op := range outpoints { + ops = append(ops, newOutPointFromWire(op)) + } + + cmd := btcjson.NewRescanCmd(startBlockHashStr, addrs, ops, + &endBlockHashStr) + return c.sendCmd(cmd) +} + +// RescanEndHeight rescans the block chain starting from the provided starting +// block up to the provided ending block for transactions that pay to the +// passed addresses and transactions which spend the passed outpoints. +// +// The notifications of found transactions are delivered to the notification +// handlers associated with client and this call will not return until the +// rescan has completed. Calling this function has no effect if there are no +// notification handlers and will result in an error if the client is configured +// to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnRedeemingTx (for transactions which spend from the one of the +// passed outpoints), OnRecvTx (for transactions that receive funds +// to one of the passed addresses), and OnRescanProgress (for rescan progress +// updates). +// +// See Rescan to also perform a rescan through current end of the longest chain. +// +// NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use RescanBlocks instead. +func (c *Client) RescanEndHeight(startBlock *chainhash.Hash, + addresses []btcutil.Address, outpoints []*wire.OutPoint, + endBlock *chainhash.Hash) error { + + return c.RescanEndBlockAsync(startBlock, addresses, outpoints, + endBlock).Receive() +} + +// FutureLoadTxFilterResult is a future promise to deliver the result +// of a LoadTxFilterAsync RPC invocation (or an applicable error). +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +type FutureLoadTxFilterResult chan *response + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func (r FutureLoadTxFilterResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// LoadTxFilterAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See LoadTxFilter for the blocking version and more details. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func (c *Client) LoadTxFilterAsync(reload bool, addresses []btcutil.Address, + outPoints []wire.OutPoint) FutureLoadTxFilterResult { + + addrStrs := make([]string, len(addresses)) + for i, a := range addresses { + addrStrs[i] = a.EncodeAddress() + } + outPointObjects := make([]btcjson.OutPoint, len(outPoints)) + for i := range outPoints { + outPointObjects[i] = btcjson.OutPoint{ + Hash: outPoints[i].Hash.String(), + Index: outPoints[i].Index, + } + } + + cmd := btcjson.NewLoadTxFilterCmd(reload, addrStrs, outPointObjects) + return c.sendCmd(cmd) +} + +// LoadTxFilter loads, reloads, or adds data to a websocket client's transaction +// filter. The filter is consistently updated based on inspected transactions +// during mempool acceptance, block acceptance, and for all rescanned blocks. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func (c *Client) LoadTxFilter(reload bool, addresses []btcutil.Address, outPoints []wire.OutPoint) error { + return c.LoadTxFilterAsync(reload, addresses, outPoints).Receive() +} diff --git a/rpcclient/rawrequest.go b/rpcclient/rawrequest.go new file mode 100644 index 00000000..dd16fd04 --- /dev/null +++ b/rpcclient/rawrequest.go @@ -0,0 +1,78 @@ +// Copyright (c) 2014-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpcclient + +import ( + "encoding/json" + "errors" + + "github.com/btcsuite/btcd/btcjson" +) + +// FutureRawResult is a future promise to deliver the result of a RawRequest RPC +// invocation (or an applicable error). +type FutureRawResult chan *response + +// Receive waits for the response promised by the future and returns the raw +// response, or an error if the request was unsuccessful. +func (r FutureRawResult) Receive() (json.RawMessage, error) { + return receiveFuture(r) +} + +// RawRequestAsync returns an instance of a type that can be used to get the +// result of a custom RPC request at some future time by invoking the Receive +// function on the returned instance. +// +// See RawRequest for the blocking version and more details. +func (c *Client) RawRequestAsync(method string, params []json.RawMessage) FutureRawResult { + // Method may not be empty. + if method == "" { + return newFutureError(errors.New("no method")) + } + + // Marshal parameters as "[]" instead of "null" when no parameters + // are passed. + if params == nil { + params = []json.RawMessage{} + } + + // 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) + } + + // 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. +// This method may be used to send and receive requests and responses for +// requests that are not handled by this client package, or to proxy partially +// unmarshaled requests to another JSON-RPC server if a request cannot be +// handled directly. +func (c *Client) RawRequest(method string, params []json.RawMessage) (json.RawMessage, error) { + return c.RawRequestAsync(method, params).Receive() +} diff --git a/rpcclient/rawtransactions.go b/rpcclient/rawtransactions.go new file mode 100644 index 00000000..3bd6e5d3 --- /dev/null +++ b/rpcclient/rawtransactions.go @@ -0,0 +1,626 @@ +// Copyright (c) 2014-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpcclient + +import ( + "bytes" + "encoding/hex" + "encoding/json" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// SigHashType enumerates the available signature hashing types that the +// SignRawTransaction function accepts. +type SigHashType string + +// Constants used to indicate the signature hash type for SignRawTransaction. +const ( + // SigHashAll indicates ALL of the outputs should be signed. + SigHashAll SigHashType = "ALL" + + // SigHashNone indicates NONE of the outputs should be signed. This + // can be thought of as specifying the signer does not care where the + // bitcoins go. + SigHashNone SigHashType = "NONE" + + // SigHashSingle indicates that a SINGLE output should be signed. This + // can be thought of specifying the signer only cares about where ONE of + // the outputs goes, but not any of the others. + SigHashSingle SigHashType = "SINGLE" + + // SigHashAllAnyoneCanPay indicates that signer does not care where the + // other inputs to the transaction come from, so it allows other people + // to add inputs. In addition, it uses the SigHashAll signing method + // for outputs. + SigHashAllAnyoneCanPay SigHashType = "ALL|ANYONECANPAY" + + // SigHashNoneAnyoneCanPay indicates that signer does not care where the + // other inputs to the transaction come from, so it allows other people + // to add inputs. In addition, it uses the SigHashNone signing method + // for outputs. + SigHashNoneAnyoneCanPay SigHashType = "NONE|ANYONECANPAY" + + // SigHashSingleAnyoneCanPay indicates that signer does not care where + // the other inputs to the transaction come from, so it allows other + // people to add inputs. In addition, it uses the SigHashSingle signing + // method for outputs. + SigHashSingleAnyoneCanPay SigHashType = "SINGLE|ANYONECANPAY" +) + +// String returns the SighHashType in human-readable form. +func (s SigHashType) String() string { + return string(s) +} + +// FutureGetRawTransactionResult is a future promise to deliver the result of a +// GetRawTransactionAsync RPC invocation (or an applicable error). +type FutureGetRawTransactionResult chan *response + +// Receive waits for the response promised by the future and returns a +// transaction given its hash. +func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var txHex string + err = json.Unmarshal(res, &txHex) + if err != nil { + return nil, err + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(txHex) + if err != nil { + return nil, err + } + + // Deserialize the transaction and return it. + var msgTx wire.MsgTx + if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { + return nil, err + } + return btcutil.NewTx(&msgTx), nil +} + +// GetRawTransactionAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetRawTransaction for the blocking version and more details. +func (c *Client) GetRawTransactionAsync(txHash *chainhash.Hash) FutureGetRawTransactionResult { + hash := "" + if txHash != nil { + hash = txHash.String() + } + + cmd := btcjson.NewGetRawTransactionCmd(hash, btcjson.Int(0)) + return c.sendCmd(cmd) +} + +// GetRawTransaction returns a transaction given its hash. +// +// See GetRawTransactionVerbose to obtain additional information about the +// transaction. +func (c *Client) GetRawTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) { + return c.GetRawTransactionAsync(txHash).Receive() +} + +// FutureGetRawTransactionVerboseResult is a future promise to deliver the +// result of a GetRawTransactionVerboseAsync RPC invocation (or an applicable +// error). +type FutureGetRawTransactionVerboseResult chan *response + +// Receive waits for the response promised by the future and returns information +// about a transaction given its hash. +func (r FutureGetRawTransactionVerboseResult) Receive() (*btcjson.TxRawResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a gettrawtransaction result object. + var rawTxResult btcjson.TxRawResult + err = json.Unmarshal(res, &rawTxResult) + if err != nil { + return nil, err + } + + return &rawTxResult, nil +} + +// GetRawTransactionVerboseAsync returns an instance of a type that can be used +// to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetRawTransactionVerbose for the blocking version and more details. +func (c *Client) GetRawTransactionVerboseAsync(txHash *chainhash.Hash) FutureGetRawTransactionVerboseResult { + hash := "" + if txHash != nil { + hash = txHash.String() + } + + cmd := btcjson.NewGetRawTransactionCmd(hash, btcjson.Int(1)) + return c.sendCmd(cmd) +} + +// GetRawTransactionVerbose returns information about a transaction given +// its hash. +// +// See GetRawTransaction to obtain only the transaction already deserialized. +func (c *Client) GetRawTransactionVerbose(txHash *chainhash.Hash) (*btcjson.TxRawResult, error) { + return c.GetRawTransactionVerboseAsync(txHash).Receive() +} + +// FutureDecodeRawTransactionResult is a future promise to deliver the result +// of a DecodeRawTransactionAsync RPC invocation (or an applicable error). +type FutureDecodeRawTransactionResult chan *response + +// Receive waits for the response promised by the future and returns information +// about a transaction given its serialized bytes. +func (r FutureDecodeRawTransactionResult) Receive() (*btcjson.TxRawResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a decoderawtransaction result object. + var rawTxResult btcjson.TxRawResult + err = json.Unmarshal(res, &rawTxResult) + if err != nil { + return nil, err + } + + return &rawTxResult, nil +} + +// DecodeRawTransactionAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See DecodeRawTransaction for the blocking version and more details. +func (c *Client) DecodeRawTransactionAsync(serializedTx []byte) FutureDecodeRawTransactionResult { + txHex := hex.EncodeToString(serializedTx) + cmd := btcjson.NewDecodeRawTransactionCmd(txHex) + return c.sendCmd(cmd) +} + +// DecodeRawTransaction returns information about a transaction given its +// serialized bytes. +func (c *Client) DecodeRawTransaction(serializedTx []byte) (*btcjson.TxRawResult, error) { + return c.DecodeRawTransactionAsync(serializedTx).Receive() +} + +// FutureCreateRawTransactionResult is a future promise to deliver the result +// of a CreateRawTransactionAsync RPC invocation (or an applicable error). +type FutureCreateRawTransactionResult chan *response + +// Receive waits for the response promised by the future and returns a new +// transaction spending the provided inputs and sending to the provided +// addresses. +func (r FutureCreateRawTransactionResult) Receive() (*wire.MsgTx, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var txHex string + err = json.Unmarshal(res, &txHex) + if err != nil { + return nil, err + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(txHex) + if err != nil { + return nil, err + } + + // Deserialize the transaction and return it. + var msgTx wire.MsgTx + if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { + return nil, err + } + return &msgTx, nil +} + +// CreateRawTransactionAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See CreateRawTransaction for the blocking version and more details. +func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, + amounts map[btcutil.Address]btcutil.Amount, lockTime *int64) FutureCreateRawTransactionResult { + + convertedAmts := make(map[string]float64, len(amounts)) + for addr, amount := range amounts { + convertedAmts[addr.String()] = amount.ToBTC() + } + cmd := btcjson.NewCreateRawTransactionCmd(inputs, convertedAmts, lockTime) + return c.sendCmd(cmd) +} + +// CreateRawTransaction returns a new transaction spending the provided inputs +// and sending to the provided addresses. +func (c *Client) CreateRawTransaction(inputs []btcjson.TransactionInput, + amounts map[btcutil.Address]btcutil.Amount, lockTime *int64) (*wire.MsgTx, error) { + + return c.CreateRawTransactionAsync(inputs, amounts, lockTime).Receive() +} + +// FutureSendRawTransactionResult is a future promise to deliver the result +// of a SendRawTransactionAsync RPC invocation (or an applicable error). +type FutureSendRawTransactionResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of submitting the encoded transaction to the server which then relays it to +// the network. +func (r FutureSendRawTransactionResult) Receive() (*chainhash.Hash, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var txHashStr string + err = json.Unmarshal(res, &txHashStr) + if err != nil { + return nil, err + } + + return chainhash.NewHashFromStr(txHashStr) +} + +// SendRawTransactionAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendRawTransaction for the blocking version and more details. +func (c *Client) SendRawTransactionAsync(tx *wire.MsgTx, allowHighFees bool) FutureSendRawTransactionResult { + txHex := "" + if tx != nil { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex = hex.EncodeToString(buf.Bytes()) + } + + cmd := btcjson.NewSendRawTransactionCmd(txHex, &allowHighFees) + return c.sendCmd(cmd) +} + +// SendRawTransaction submits the encoded transaction to the server which will +// then relay it to the network. +func (c *Client) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) { + return c.SendRawTransactionAsync(tx, allowHighFees).Receive() +} + +// FutureSignRawTransactionResult is a future promise to deliver the result +// of one of the SignRawTransactionAsync family of RPC invocations (or an +// applicable error). +type FutureSignRawTransactionResult chan *response + +// Receive waits for the response promised by the future and returns the +// signed transaction as well as whether or not all inputs are now signed. +func (r FutureSignRawTransactionResult) Receive() (*wire.MsgTx, bool, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, false, err + } + + // Unmarshal as a signrawtransaction result. + var signRawTxResult btcjson.SignRawTransactionResult + err = json.Unmarshal(res, &signRawTxResult) + if err != nil { + return nil, false, err + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(signRawTxResult.Hex) + if err != nil { + return nil, false, err + } + + // Deserialize the transaction and return it. + var msgTx wire.MsgTx + if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { + return nil, false, err + } + + return &msgTx, signRawTxResult.Complete, nil +} + +// SignRawTransactionAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SignRawTransaction for the blocking version and more details. +func (c *Client) SignRawTransactionAsync(tx *wire.MsgTx) FutureSignRawTransactionResult { + txHex := "" + if tx != nil { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex = hex.EncodeToString(buf.Bytes()) + } + + cmd := btcjson.NewSignRawTransactionCmd(txHex, nil, nil, nil) + return c.sendCmd(cmd) +} + +// SignRawTransaction signs inputs for the passed transaction and returns the +// signed transaction as well as whether or not all inputs are now signed. +// +// This function assumes the RPC server already knows the input transactions and +// private keys for the passed transaction which needs to be signed and uses the +// default signature hash type. Use one of the SignRawTransaction# variants to +// specify that information if needed. +func (c *Client) SignRawTransaction(tx *wire.MsgTx) (*wire.MsgTx, bool, error) { + return c.SignRawTransactionAsync(tx).Receive() +} + +// SignRawTransaction2Async returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SignRawTransaction2 for the blocking version and more details. +func (c *Client) SignRawTransaction2Async(tx *wire.MsgTx, inputs []btcjson.RawTxInput) FutureSignRawTransactionResult { + txHex := "" + if tx != nil { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex = hex.EncodeToString(buf.Bytes()) + } + + cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, nil, nil) + return c.sendCmd(cmd) +} + +// SignRawTransaction2 signs inputs for the passed transaction given the list +// of information about the input transactions needed to perform the signing +// process. +// +// This only input transactions that need to be specified are ones the +// RPC server does not already know. Already known input transactions will be +// merged with the specified transactions. +// +// See SignRawTransaction if the RPC server already knows the input +// transactions. +func (c *Client) SignRawTransaction2(tx *wire.MsgTx, inputs []btcjson.RawTxInput) (*wire.MsgTx, bool, error) { + return c.SignRawTransaction2Async(tx, inputs).Receive() +} + +// SignRawTransaction3Async returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SignRawTransaction3 for the blocking version and more details. +func (c *Client) SignRawTransaction3Async(tx *wire.MsgTx, + inputs []btcjson.RawTxInput, + privKeysWIF []string) FutureSignRawTransactionResult { + + txHex := "" + if tx != nil { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex = hex.EncodeToString(buf.Bytes()) + } + + cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, &privKeysWIF, + nil) + return c.sendCmd(cmd) +} + +// SignRawTransaction3 signs inputs for the passed transaction given the list +// of information about extra input transactions and a list of private keys +// needed to perform the signing process. The private keys must be in wallet +// import format (WIF). +// +// This only input transactions that need to be specified are ones the +// RPC server does not already know. Already known input transactions will be +// merged with the specified transactions. This means the list of transaction +// inputs can be nil if the RPC server already knows them all. +// +// NOTE: Unlike the merging functionality of the input transactions, ONLY the +// specified private keys will be used, so even if the server already knows some +// of the private keys, they will NOT be used. +// +// See SignRawTransaction if the RPC server already knows the input +// transactions and private keys or SignRawTransaction2 if it already knows the +// private keys. +func (c *Client) SignRawTransaction3(tx *wire.MsgTx, + inputs []btcjson.RawTxInput, + privKeysWIF []string) (*wire.MsgTx, bool, error) { + + return c.SignRawTransaction3Async(tx, inputs, privKeysWIF).Receive() +} + +// SignRawTransaction4Async returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SignRawTransaction4 for the blocking version and more details. +func (c *Client) SignRawTransaction4Async(tx *wire.MsgTx, + inputs []btcjson.RawTxInput, privKeysWIF []string, + hashType SigHashType) FutureSignRawTransactionResult { + + txHex := "" + if tx != nil { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex = hex.EncodeToString(buf.Bytes()) + } + + cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, &privKeysWIF, + btcjson.String(string(hashType))) + return c.sendCmd(cmd) +} + +// SignRawTransaction4 signs inputs for the passed transaction using the +// the specified signature hash type given the list of information about extra +// input transactions and a potential list of private keys needed to perform +// the signing process. The private keys, if specified, must be in wallet +// import format (WIF). +// +// The only input transactions that need to be specified are ones the RPC server +// does not already know. This means the list of transaction inputs can be nil +// if the RPC server already knows them all. +// +// NOTE: Unlike the merging functionality of the input transactions, ONLY the +// specified private keys will be used, so even if the server already knows some +// of the private keys, they will NOT be used. The list of private keys can be +// nil in which case any private keys the RPC server knows will be used. +// +// This function should only used if a non-default signature hash type is +// desired. Otherwise, see SignRawTransaction if the RPC server already knows +// the input transactions and private keys, SignRawTransaction2 if it already +// knows the private keys, or SignRawTransaction3 if it does not know both. +func (c *Client) SignRawTransaction4(tx *wire.MsgTx, + inputs []btcjson.RawTxInput, privKeysWIF []string, + hashType SigHashType) (*wire.MsgTx, bool, error) { + + return c.SignRawTransaction4Async(tx, inputs, privKeysWIF, + hashType).Receive() +} + +// FutureSearchRawTransactionsResult is a future promise to deliver the result +// of the SearchRawTransactionsAsync RPC invocation (or an applicable error). +type FutureSearchRawTransactionsResult chan *response + +// Receive waits for the response promised by the future and returns the +// found raw transactions. +func (r FutureSearchRawTransactionsResult) Receive() ([]*wire.MsgTx, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of strings. + var searchRawTxnsResult []string + err = json.Unmarshal(res, &searchRawTxnsResult) + if err != nil { + return nil, err + } + + // Decode and deserialize each transaction. + msgTxns := make([]*wire.MsgTx, 0, len(searchRawTxnsResult)) + for _, hexTx := range searchRawTxnsResult { + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(hexTx) + if err != nil { + return nil, err + } + + // Deserialize the transaction and add it to the result slice. + var msgTx wire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + return nil, err + } + msgTxns = append(msgTxns, &msgTx) + } + + return msgTxns, nil +} + +// SearchRawTransactionsAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SearchRawTransactions for the blocking version and more details. +func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count int, reverse bool, filterAddrs []string) FutureSearchRawTransactionsResult { + addr := address.EncodeAddress() + verbose := btcjson.Int(0) + cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, + nil, &reverse, &filterAddrs) + return c.sendCmd(cmd) +} + +// SearchRawTransactions returns transactions that involve the passed address. +// +// NOTE: Chain servers do not typically provide this capability unless it has +// specifically been enabled. +// +// See SearchRawTransactionsVerbose to retrieve a list of data structures with +// information about the transactions instead of the transactions themselves. +func (c *Client) SearchRawTransactions(address btcutil.Address, skip, count int, reverse bool, filterAddrs []string) ([]*wire.MsgTx, error) { + return c.SearchRawTransactionsAsync(address, skip, count, reverse, filterAddrs).Receive() +} + +// FutureSearchRawTransactionsVerboseResult is a future promise to deliver the +// result of the SearchRawTransactionsVerboseAsync RPC invocation (or an +// applicable error). +type FutureSearchRawTransactionsVerboseResult chan *response + +// Receive waits for the response promised by the future and returns the +// found raw transactions. +func (r FutureSearchRawTransactionsVerboseResult) Receive() ([]*btcjson.SearchRawTransactionsResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of raw transaction results. + var result []*btcjson.SearchRawTransactionsResult + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// SearchRawTransactionsVerboseAsync returns an instance of a type that can be +// used to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SearchRawTransactionsVerbose for the blocking version and more details. +func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip, + count int, includePrevOut, reverse bool, filterAddrs *[]string) FutureSearchRawTransactionsVerboseResult { + + addr := address.EncodeAddress() + verbose := btcjson.Int(1) + var prevOut *int + if includePrevOut { + prevOut = btcjson.Int(1) + } + cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, + prevOut, &reverse, filterAddrs) + return c.sendCmd(cmd) +} + +// SearchRawTransactionsVerbose returns a list of data structures that describe +// transactions which involve the passed address. +// +// NOTE: Chain servers do not typically provide this capability unless it has +// specifically been enabled. +// +// See SearchRawTransactions to retrieve a list of raw transactions instead. +func (c *Client) SearchRawTransactionsVerbose(address btcutil.Address, skip, + count int, includePrevOut, reverse bool, filterAddrs []string) ([]*btcjson.SearchRawTransactionsResult, error) { + + return c.SearchRawTransactionsVerboseAsync(address, skip, count, + includePrevOut, reverse, &filterAddrs).Receive() +} diff --git a/rpcclient/wallet.go b/rpcclient/wallet.go new file mode 100644 index 00000000..caea70b8 --- /dev/null +++ b/rpcclient/wallet.go @@ -0,0 +1,2241 @@ +// Copyright (c) 2014-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpcclient + +import ( + "encoding/json" + "strconv" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" +) + +// ***************************** +// Transaction Listing Functions +// ***************************** + +// FutureGetTransactionResult is a future promise to deliver the result +// of a GetTransactionAsync RPC invocation (or an applicable error). +type FutureGetTransactionResult chan *response + +// Receive waits for the response promised by the future and returns detailed +// information about a wallet transaction. +func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a gettransaction result object + var getTx btcjson.GetTransactionResult + err = json.Unmarshal(res, &getTx) + if err != nil { + return nil, err + } + + return &getTx, nil +} + +// GetTransactionAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetTransaction for the blocking version and more details. +func (c *Client) GetTransactionAsync(txHash *chainhash.Hash) FutureGetTransactionResult { + hash := "" + if txHash != nil { + hash = txHash.String() + } + + cmd := btcjson.NewGetTransactionCmd(hash, nil) + return c.sendCmd(cmd) +} + +// GetTransaction returns detailed information about a wallet transaction. +// +// See GetRawTransaction to return the raw transaction instead. +func (c *Client) GetTransaction(txHash *chainhash.Hash) (*btcjson.GetTransactionResult, error) { + return c.GetTransactionAsync(txHash).Receive() +} + +// FutureListTransactionsResult is a future promise to deliver the result of a +// ListTransactionsAsync, ListTransactionsCountAsync, or +// ListTransactionsCountFromAsync RPC invocation (or an applicable error). +type FutureListTransactionsResult chan *response + +// Receive waits for the response promised by the future and returns a list of +// the most recent transactions. +func (r FutureListTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as an array of listtransaction result objects. + var transactions []btcjson.ListTransactionsResult + err = json.Unmarshal(res, &transactions) + if err != nil { + return nil, err + } + + return transactions, nil +} + +// ListTransactionsAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See ListTransactions for the blocking version and more details. +func (c *Client) ListTransactionsAsync(account string) FutureListTransactionsResult { + cmd := btcjson.NewListTransactionsCmd(&account, nil, nil, nil) + return c.sendCmd(cmd) +} + +// ListTransactions returns a list of the most recent transactions. +// +// See the ListTransactionsCount and ListTransactionsCountFrom to control the +// number of transactions returned and starting point, respectively. +func (c *Client) ListTransactions(account string) ([]btcjson.ListTransactionsResult, error) { + return c.ListTransactionsAsync(account).Receive() +} + +// ListTransactionsCountAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListTransactionsCount for the blocking version and more details. +func (c *Client) ListTransactionsCountAsync(account string, count int) FutureListTransactionsResult { + cmd := btcjson.NewListTransactionsCmd(&account, &count, nil, nil) + return c.sendCmd(cmd) +} + +// ListTransactionsCount returns a list of the most recent transactions up +// to the passed count. +// +// See the ListTransactions and ListTransactionsCountFrom functions for +// different options. +func (c *Client) ListTransactionsCount(account string, count int) ([]btcjson.ListTransactionsResult, error) { + return c.ListTransactionsCountAsync(account, count).Receive() +} + +// ListTransactionsCountFromAsync returns an instance of a type that can be used +// to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListTransactionsCountFrom for the blocking version and more details. +func (c *Client) ListTransactionsCountFromAsync(account string, count, from int) FutureListTransactionsResult { + cmd := btcjson.NewListTransactionsCmd(&account, &count, &from, nil) + return c.sendCmd(cmd) +} + +// ListTransactionsCountFrom returns a list of the most recent transactions up +// to the passed count while skipping the first 'from' transactions. +// +// See the ListTransactions and ListTransactionsCount functions to use defaults. +func (c *Client) ListTransactionsCountFrom(account string, count, from int) ([]btcjson.ListTransactionsResult, error) { + return c.ListTransactionsCountFromAsync(account, count, from).Receive() +} + +// FutureListUnspentResult is a future promise to deliver the result of a +// ListUnspentAsync, ListUnspentMinAsync, ListUnspentMinMaxAsync, or +// ListUnspentMinMaxAddressesAsync RPC invocation (or an applicable error). +type FutureListUnspentResult chan *response + +// Receive waits for the response promised by the future and returns all +// unspent wallet transaction outputs returned by the RPC call. If the +// future wac returnd by a call to ListUnspentMinAsync, ListUnspentMinMaxAsync, +// or ListUnspentMinMaxAddressesAsync, the range may be limited by the +// parameters of the RPC invocation. +func (r FutureListUnspentResult) Receive() ([]btcjson.ListUnspentResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as an array of listunspent results. + var unspent []btcjson.ListUnspentResult + err = json.Unmarshal(res, &unspent) + if err != nil { + return nil, err + } + + return unspent, nil +} + +// ListUnspentAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function +// on the returned instance. +// +// See ListUnspent for the blocking version and more details. +func (c *Client) ListUnspentAsync() FutureListUnspentResult { + cmd := btcjson.NewListUnspentCmd(nil, nil, nil) + return c.sendCmd(cmd) +} + +// ListUnspentMinAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function +// on the returned instance. +// +// See ListUnspentMin for the blocking version and more details. +func (c *Client) ListUnspentMinAsync(minConf int) FutureListUnspentResult { + cmd := btcjson.NewListUnspentCmd(&minConf, nil, nil) + return c.sendCmd(cmd) +} + +// ListUnspentMinMaxAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function +// on the returned instance. +// +// See ListUnspentMinMax for the blocking version and more details. +func (c *Client) ListUnspentMinMaxAsync(minConf, maxConf int) FutureListUnspentResult { + cmd := btcjson.NewListUnspentCmd(&minConf, &maxConf, nil) + return c.sendCmd(cmd) +} + +// ListUnspentMinMaxAddressesAsync returns an instance of a type that can be +// used to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListUnspentMinMaxAddresses for the blocking version and more details. +func (c *Client) ListUnspentMinMaxAddressesAsync(minConf, maxConf int, addrs []btcutil.Address) FutureListUnspentResult { + addrStrs := make([]string, 0, len(addrs)) + for _, a := range addrs { + addrStrs = append(addrStrs, a.EncodeAddress()) + } + + cmd := btcjson.NewListUnspentCmd(&minConf, &maxConf, &addrStrs) + return c.sendCmd(cmd) +} + +// ListUnspent returns all unspent transaction outputs known to a wallet, using +// the default number of minimum and maximum number of confirmations as a +// filter (1 and 999999, respectively). +func (c *Client) ListUnspent() ([]btcjson.ListUnspentResult, error) { + return c.ListUnspentAsync().Receive() +} + +// ListUnspentMin returns all unspent transaction outputs known to a wallet, +// using the specified number of minimum conformations and default number of +// maximum confiramtions (999999) as a filter. +func (c *Client) ListUnspentMin(minConf int) ([]btcjson.ListUnspentResult, error) { + return c.ListUnspentMinAsync(minConf).Receive() +} + +// ListUnspentMinMax returns all unspent transaction outputs known to a wallet, +// using the specified number of minimum and maximum number of confirmations as +// a filter. +func (c *Client) ListUnspentMinMax(minConf, maxConf int) ([]btcjson.ListUnspentResult, error) { + return c.ListUnspentMinMaxAsync(minConf, maxConf).Receive() +} + +// ListUnspentMinMaxAddresses returns all unspent transaction outputs that pay +// to any of specified addresses in a wallet using the specified number of +// minimum and maximum number of confirmations as a filter. +func (c *Client) ListUnspentMinMaxAddresses(minConf, maxConf int, addrs []btcutil.Address) ([]btcjson.ListUnspentResult, error) { + return c.ListUnspentMinMaxAddressesAsync(minConf, maxConf, addrs).Receive() +} + +// FutureListSinceBlockResult is a future promise to deliver the result of a +// ListSinceBlockAsync or ListSinceBlockMinConfAsync RPC invocation (or an +// applicable error). +type FutureListSinceBlockResult chan *response + +// Receive waits for the response promised by the future and returns all +// transactions added in blocks since the specified block hash, or all +// transactions if it is nil. +func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a listsinceblock result object. + var listResult btcjson.ListSinceBlockResult + err = json.Unmarshal(res, &listResult) + if err != nil { + return nil, err + } + + return &listResult, nil +} + +// ListSinceBlockAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See ListSinceBlock for the blocking version and more details. +func (c *Client) ListSinceBlockAsync(blockHash *chainhash.Hash) FutureListSinceBlockResult { + var hash *string + if blockHash != nil { + hash = btcjson.String(blockHash.String()) + } + + cmd := btcjson.NewListSinceBlockCmd(hash, nil, nil) + return c.sendCmd(cmd) +} + +// ListSinceBlock returns all transactions added in blocks since the specified +// block hash, or all transactions if it is nil, using the default number of +// minimum confirmations as a filter. +// +// See ListSinceBlockMinConf to override the minimum number of confirmations. +func (c *Client) ListSinceBlock(blockHash *chainhash.Hash) (*btcjson.ListSinceBlockResult, error) { + return c.ListSinceBlockAsync(blockHash).Receive() +} + +// ListSinceBlockMinConfAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListSinceBlockMinConf for the blocking version and more details. +func (c *Client) ListSinceBlockMinConfAsync(blockHash *chainhash.Hash, minConfirms int) FutureListSinceBlockResult { + var hash *string + if blockHash != nil { + hash = btcjson.String(blockHash.String()) + } + + cmd := btcjson.NewListSinceBlockCmd(hash, &minConfirms, nil) + return c.sendCmd(cmd) +} + +// ListSinceBlockMinConf returns all transactions added in blocks since the +// specified block hash, or all transactions if it is nil, using the specified +// number of minimum confirmations as a filter. +// +// See ListSinceBlock to use the default minimum number of confirmations. +func (c *Client) ListSinceBlockMinConf(blockHash *chainhash.Hash, minConfirms int) (*btcjson.ListSinceBlockResult, error) { + return c.ListSinceBlockMinConfAsync(blockHash, minConfirms).Receive() +} + +// ************************** +// Transaction Send Functions +// ************************** + +// FutureLockUnspentResult is a future promise to deliver the error result of a +// LockUnspentAsync RPC invocation. +type FutureLockUnspentResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of locking or unlocking the unspent output(s). +func (r FutureLockUnspentResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// LockUnspentAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See LockUnspent for the blocking version and more details. +func (c *Client) LockUnspentAsync(unlock bool, ops []*wire.OutPoint) FutureLockUnspentResult { + outputs := make([]btcjson.TransactionInput, len(ops)) + for i, op := range ops { + outputs[i] = btcjson.TransactionInput{ + Txid: op.Hash.String(), + Vout: op.Index, + } + } + cmd := btcjson.NewLockUnspentCmd(unlock, outputs) + return c.sendCmd(cmd) +} + +// LockUnspent marks outputs as locked or unlocked, depending on the value of +// the unlock bool. When locked, the unspent output will not be selected as +// input for newly created, non-raw transactions, and will not be returned in +// future ListUnspent results, until the output is marked unlocked again. +// +// If unlock is false, each outpoint in ops will be marked locked. If unlocked +// is true and specific outputs are specified in ops (len != 0), exactly those +// outputs will be marked unlocked. If unlocked is true and no outpoints are +// specified, all previous locked outputs are marked unlocked. +// +// The locked or unlocked state of outputs are not written to disk and after +// restarting a wallet process, this data will be reset (every output unlocked). +// +// NOTE: While this method would be a bit more readable if the unlock bool was +// reversed (that is, LockUnspent(true, ...) locked the outputs), it has been +// left as unlock to keep compatibility with the reference client API and to +// avoid confusion for those who are already familiar with the lockunspent RPC. +func (c *Client) LockUnspent(unlock bool, ops []*wire.OutPoint) error { + return c.LockUnspentAsync(unlock, ops).Receive() +} + +// FutureListLockUnspentResult is a future promise to deliver the result of a +// ListLockUnspentAsync RPC invocation (or an applicable error). +type FutureListLockUnspentResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of all currently locked unspent outputs. +func (r FutureListLockUnspentResult) Receive() ([]*wire.OutPoint, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of transaction inputs. + var inputs []btcjson.TransactionInput + err = json.Unmarshal(res, &inputs) + if err != nil { + return nil, err + } + + // Create a slice of outpoints from the transaction input structs. + ops := make([]*wire.OutPoint, len(inputs)) + for i, input := range inputs { + sha, err := chainhash.NewHashFromStr(input.Txid) + if err != nil { + return nil, err + } + ops[i] = wire.NewOutPoint(sha, input.Vout) + } + + return ops, nil +} + +// ListLockUnspentAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See ListLockUnspent for the blocking version and more details. +func (c *Client) ListLockUnspentAsync() FutureListLockUnspentResult { + cmd := btcjson.NewListLockUnspentCmd() + return c.sendCmd(cmd) +} + +// ListLockUnspent returns a slice of outpoints for all unspent outputs marked +// as locked by a wallet. Unspent outputs may be marked locked using +// LockOutput. +func (c *Client) ListLockUnspent() ([]*wire.OutPoint, error) { + return c.ListLockUnspentAsync().Receive() +} + +// FutureSetTxFeeResult is a future promise to deliver the result of a +// SetTxFeeAsync RPC invocation (or an applicable error). +type FutureSetTxFeeResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of setting an optional transaction fee per KB that helps ensure transactions +// are processed quickly. Most transaction are 1KB. +func (r FutureSetTxFeeResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// SetTxFeeAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SetTxFee for the blocking version and more details. +func (c *Client) SetTxFeeAsync(fee btcutil.Amount) FutureSetTxFeeResult { + cmd := btcjson.NewSetTxFeeCmd(fee.ToBTC()) + return c.sendCmd(cmd) +} + +// SetTxFee sets an optional transaction fee per KB that helps ensure +// transactions are processed quickly. Most transaction are 1KB. +func (c *Client) SetTxFee(fee btcutil.Amount) error { + return c.SetTxFeeAsync(fee).Receive() +} + +// FutureSendToAddressResult is a future promise to deliver the result of a +// SendToAddressAsync RPC invocation (or an applicable error). +type FutureSendToAddressResult chan *response + +// Receive waits for the response promised by the future and returns the hash +// of the transaction sending the passed amount to the given address. +func (r FutureSendToAddressResult) Receive() (*chainhash.Hash, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var txHash string + err = json.Unmarshal(res, &txHash) + if err != nil { + return nil, err + } + + return chainhash.NewHashFromStr(txHash) +} + +// SendToAddressAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SendToAddress for the blocking version and more details. +func (c *Client) SendToAddressAsync(address btcutil.Address, amount btcutil.Amount) FutureSendToAddressResult { + addr := address.EncodeAddress() + cmd := btcjson.NewSendToAddressCmd(addr, amount.ToBTC(), nil, nil) + return c.sendCmd(cmd) +} + +// SendToAddress sends the passed amount to the given address. +// +// See SendToAddressComment to associate comments with the transaction in the +// wallet. The comments are not part of the transaction and are only internal +// to the wallet. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendToAddress(address btcutil.Address, amount btcutil.Amount) (*chainhash.Hash, error) { + return c.SendToAddressAsync(address, amount).Receive() +} + +// SendToAddressCommentAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SendToAddressComment for the blocking version and more details. +func (c *Client) SendToAddressCommentAsync(address btcutil.Address, + amount btcutil.Amount, comment, + commentTo string) FutureSendToAddressResult { + + addr := address.EncodeAddress() + cmd := btcjson.NewSendToAddressCmd(addr, amount.ToBTC(), &comment, + &commentTo) + return c.sendCmd(cmd) +} + +// SendToAddressComment sends the passed amount to the given address and stores +// the provided comment and comment to in the wallet. The comment parameter is +// intended to be used for the purpose of the transaction while the commentTo +// parameter is indended to be used for who the transaction is being sent to. +// +// The comments are not part of the transaction and are only internal +// to the wallet. +// +// See SendToAddress to avoid using comments. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendToAddressComment(address btcutil.Address, amount btcutil.Amount, comment, commentTo string) (*chainhash.Hash, error) { + return c.SendToAddressCommentAsync(address, amount, comment, + commentTo).Receive() +} + +// FutureSendFromResult is a future promise to deliver the result of a +// SendFromAsync, SendFromMinConfAsync, or SendFromCommentAsync RPC invocation +// (or an applicable error). +type FutureSendFromResult chan *response + +// Receive waits for the response promised by the future and returns the hash +// of the transaction sending amount to the given address using the provided +// account as a source of funds. +func (r FutureSendFromResult) Receive() (*chainhash.Hash, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var txHash string + err = json.Unmarshal(res, &txHash) + if err != nil { + return nil, err + } + + return chainhash.NewHashFromStr(txHash) +} + +// SendFromAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SendFrom for the blocking version and more details. +func (c *Client) SendFromAsync(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount) FutureSendFromResult { + addr := toAddress.EncodeAddress() + cmd := btcjson.NewSendFromCmd(fromAccount, addr, amount.ToBTC(), nil, + nil, nil) + return c.sendCmd(cmd) +} + +// SendFrom sends the passed amount to the given address using the provided +// account as a source of funds. Only funds with the default number of minimum +// confirmations will be used. +// +// See SendFromMinConf and SendFromComment for different options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendFrom(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount) (*chainhash.Hash, error) { + return c.SendFromAsync(fromAccount, toAddress, amount).Receive() +} + +// SendFromMinConfAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendFromMinConf for the blocking version and more details. +func (c *Client) SendFromMinConfAsync(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount, minConfirms int) FutureSendFromResult { + addr := toAddress.EncodeAddress() + cmd := btcjson.NewSendFromCmd(fromAccount, addr, amount.ToBTC(), + &minConfirms, nil, nil) + return c.sendCmd(cmd) +} + +// SendFromMinConf sends the passed amount to the given address using the +// provided account as a source of funds. Only funds with the passed number of +// minimum confirmations will be used. +// +// See SendFrom to use the default number of minimum confirmations and +// SendFromComment for additional options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendFromMinConf(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount, minConfirms int) (*chainhash.Hash, error) { + return c.SendFromMinConfAsync(fromAccount, toAddress, amount, + minConfirms).Receive() +} + +// SendFromCommentAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendFromComment for the blocking version and more details. +func (c *Client) SendFromCommentAsync(fromAccount string, + toAddress btcutil.Address, amount btcutil.Amount, minConfirms int, + comment, commentTo string) FutureSendFromResult { + + addr := toAddress.EncodeAddress() + cmd := btcjson.NewSendFromCmd(fromAccount, addr, amount.ToBTC(), + &minConfirms, &comment, &commentTo) + return c.sendCmd(cmd) +} + +// SendFromComment sends the passed amount to the given address using the +// provided account as a source of funds and stores the provided comment and +// comment to in the wallet. The comment parameter is intended to be used for +// the purpose of the transaction while the commentTo parameter is indended to +// be used for who the transaction is being sent to. Only funds with the passed +// number of minimum confirmations will be used. +// +// See SendFrom and SendFromMinConf to use defaults. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendFromComment(fromAccount string, toAddress btcutil.Address, + amount btcutil.Amount, minConfirms int, + comment, commentTo string) (*chainhash.Hash, error) { + + return c.SendFromCommentAsync(fromAccount, toAddress, amount, + minConfirms, comment, commentTo).Receive() +} + +// FutureSendManyResult is a future promise to deliver the result of a +// SendManyAsync, SendManyMinConfAsync, or SendManyCommentAsync RPC invocation +// (or an applicable error). +type FutureSendManyResult chan *response + +// Receive waits for the response promised by the future and returns the hash +// of the transaction sending multiple amounts to multiple addresses using the +// provided account as a source of funds. +func (r FutureSendManyResult) Receive() (*chainhash.Hash, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmashal result as a string. + var txHash string + err = json.Unmarshal(res, &txHash) + if err != nil { + return nil, err + } + + return chainhash.NewHashFromStr(txHash) +} + +// SendManyAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SendMany for the blocking version and more details. +func (c *Client) SendManyAsync(fromAccount string, amounts map[btcutil.Address]btcutil.Amount) FutureSendManyResult { + convertedAmounts := make(map[string]float64, len(amounts)) + for addr, amount := range amounts { + convertedAmounts[addr.EncodeAddress()] = amount.ToBTC() + } + cmd := btcjson.NewSendManyCmd(fromAccount, convertedAmounts, nil, nil) + return c.sendCmd(cmd) +} + +// SendMany sends multiple amounts to multiple addresses using the provided +// account as a source of funds in a single transaction. Only funds with the +// default number of minimum confirmations will be used. +// +// See SendManyMinConf and SendManyComment for different options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendMany(fromAccount string, amounts map[btcutil.Address]btcutil.Amount) (*chainhash.Hash, error) { + return c.SendManyAsync(fromAccount, amounts).Receive() +} + +// SendManyMinConfAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendManyMinConf for the blocking version and more details. +func (c *Client) SendManyMinConfAsync(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, + minConfirms int) FutureSendManyResult { + + convertedAmounts := make(map[string]float64, len(amounts)) + for addr, amount := range amounts { + convertedAmounts[addr.EncodeAddress()] = amount.ToBTC() + } + cmd := btcjson.NewSendManyCmd(fromAccount, convertedAmounts, + &minConfirms, nil) + return c.sendCmd(cmd) +} + +// SendManyMinConf sends multiple amounts to multiple addresses using the +// provided account as a source of funds in a single transaction. Only funds +// with the passed number of minimum confirmations will be used. +// +// See SendMany to use the default number of minimum confirmations and +// SendManyComment for additional options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendManyMinConf(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, + minConfirms int) (*chainhash.Hash, error) { + + return c.SendManyMinConfAsync(fromAccount, amounts, minConfirms).Receive() +} + +// SendManyCommentAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendManyComment for the blocking version and more details. +func (c *Client) SendManyCommentAsync(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, minConfirms int, + comment string) FutureSendManyResult { + + convertedAmounts := make(map[string]float64, len(amounts)) + for addr, amount := range amounts { + convertedAmounts[addr.EncodeAddress()] = amount.ToBTC() + } + cmd := btcjson.NewSendManyCmd(fromAccount, convertedAmounts, + &minConfirms, &comment) + return c.sendCmd(cmd) +} + +// SendManyComment sends multiple amounts to multiple addresses using the +// provided account as a source of funds in a single transaction and stores the +// provided comment in the wallet. The comment parameter is intended to be used +// for the purpose of the transaction Only funds with the passed number of +// minimum confirmations will be used. +// +// See SendMany and SendManyMinConf to use defaults. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendManyComment(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, minConfirms int, + comment string) (*chainhash.Hash, error) { + + return c.SendManyCommentAsync(fromAccount, amounts, minConfirms, + comment).Receive() +} + +// ************************* +// Address/Account Functions +// ************************* + +// FutureAddMultisigAddressResult is a future promise to deliver the result of a +// AddMultisigAddressAsync RPC invocation (or an applicable error). +type FutureAddMultisigAddressResult chan *response + +// Receive waits for the response promised by the future and returns the +// multisignature address that requires the specified number of signatures for +// the provided addresses. +func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var addr string + err = json.Unmarshal(res, &addr) + if err != nil { + return nil, err + } + + return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams) +} + +// AddMultisigAddressAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See AddMultisigAddress for the blocking version and more details. +func (c *Client) AddMultisigAddressAsync(requiredSigs int, addresses []btcutil.Address, account string) FutureAddMultisigAddressResult { + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.String()) + } + + cmd := btcjson.NewAddMultisigAddressCmd(requiredSigs, addrs, &account) + return c.sendCmd(cmd) +} + +// AddMultisigAddress adds a multisignature address that requires the specified +// number of signatures for the provided addresses to the wallet. +func (c *Client) AddMultisigAddress(requiredSigs int, addresses []btcutil.Address, account string) (btcutil.Address, error) { + return c.AddMultisigAddressAsync(requiredSigs, addresses, + account).Receive() +} + +// FutureCreateMultisigResult is a future promise to deliver the result of a +// CreateMultisigAsync RPC invocation (or an applicable error). +type FutureCreateMultisigResult chan *response + +// Receive waits for the response promised by the future and returns the +// multisignature address and script needed to redeem it. +func (r FutureCreateMultisigResult) Receive() (*btcjson.CreateMultiSigResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a createmultisig result object. + var multisigRes btcjson.CreateMultiSigResult + err = json.Unmarshal(res, &multisigRes) + if err != nil { + return nil, err + } + + return &multisigRes, nil +} + +// CreateMultisigAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See CreateMultisig for the blocking version and more details. +func (c *Client) CreateMultisigAsync(requiredSigs int, addresses []btcutil.Address) FutureCreateMultisigResult { + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.String()) + } + + cmd := btcjson.NewCreateMultisigCmd(requiredSigs, addrs) + return c.sendCmd(cmd) +} + +// CreateMultisig creates a multisignature address that requires the specified +// number of signatures for the provided addresses and returns the +// multisignature address and script needed to redeem it. +func (c *Client) CreateMultisig(requiredSigs int, addresses []btcutil.Address) (*btcjson.CreateMultiSigResult, error) { + return c.CreateMultisigAsync(requiredSigs, addresses).Receive() +} + +// FutureCreateNewAccountResult is a future promise to deliver the result of a +// CreateNewAccountAsync RPC invocation (or an applicable error). +type FutureCreateNewAccountResult chan *response + +// Receive waits for the response promised by the future and returns the +// result of creating new account. +func (r FutureCreateNewAccountResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// CreateNewAccountAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See CreateNewAccount for the blocking version and more details. +func (c *Client) CreateNewAccountAsync(account string) FutureCreateNewAccountResult { + cmd := btcjson.NewCreateNewAccountCmd(account) + return c.sendCmd(cmd) +} + +// CreateNewAccount creates a new wallet account. +func (c *Client) CreateNewAccount(account string) error { + return c.CreateNewAccountAsync(account).Receive() +} + +// FutureGetNewAddressResult is a future promise to deliver the result of a +// GetNewAddressAsync RPC invocation (or an applicable error). +type FutureGetNewAddressResult chan *response + +// Receive waits for the response promised by the future and returns a new +// address. +func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var addr string + err = json.Unmarshal(res, &addr) + if err != nil { + return nil, err + } + + return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams) +} + +// GetNewAddressAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetNewAddress for the blocking version and more details. +func (c *Client) GetNewAddressAsync(account string) FutureGetNewAddressResult { + cmd := btcjson.NewGetNewAddressCmd(&account) + return c.sendCmd(cmd) +} + +// GetNewAddress returns a new address. +func (c *Client) GetNewAddress(account string) (btcutil.Address, error) { + return c.GetNewAddressAsync(account).Receive() +} + +// FutureGetRawChangeAddressResult is a future promise to deliver the result of +// a GetRawChangeAddressAsync RPC invocation (or an applicable error). +type FutureGetRawChangeAddressResult chan *response + +// Receive waits for the response promised by the future and returns a new +// address for receiving change that will be associated with the provided +// account. Note that this is only for raw transactions and NOT for normal use. +func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var addr string + err = json.Unmarshal(res, &addr) + if err != nil { + return nil, err + } + + return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams) +} + +// GetRawChangeAddressAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetRawChangeAddress for the blocking version and more details. +func (c *Client) GetRawChangeAddressAsync(account string) FutureGetRawChangeAddressResult { + cmd := btcjson.NewGetRawChangeAddressCmd(&account) + return c.sendCmd(cmd) +} + +// GetRawChangeAddress returns a new address for receiving change that will be +// associated with the provided account. Note that this is only for raw +// transactions and NOT for normal use. +func (c *Client) GetRawChangeAddress(account string) (btcutil.Address, error) { + return c.GetRawChangeAddressAsync(account).Receive() +} + +// FutureGetAccountAddressResult is a future promise to deliver the result of a +// GetAccountAddressAsync RPC invocation (or an applicable error). +type FutureGetAccountAddressResult chan *response + +// Receive waits for the response promised by the future and returns the current +// Bitcoin address for receiving payments to the specified account. +func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var addr string + err = json.Unmarshal(res, &addr) + if err != nil { + return nil, err + } + + return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams) +} + +// GetAccountAddressAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetAccountAddress for the blocking version and more details. +func (c *Client) GetAccountAddressAsync(account string) FutureGetAccountAddressResult { + cmd := btcjson.NewGetAccountAddressCmd(account) + return c.sendCmd(cmd) +} + +// GetAccountAddress returns the current Bitcoin address for receiving payments +// to the specified account. +func (c *Client) GetAccountAddress(account string) (btcutil.Address, error) { + return c.GetAccountAddressAsync(account).Receive() +} + +// FutureGetAccountResult is a future promise to deliver the result of a +// GetAccountAsync RPC invocation (or an applicable error). +type FutureGetAccountResult chan *response + +// Receive waits for the response promised by the future and returns the account +// associated with the passed address. +func (r FutureGetAccountResult) Receive() (string, error) { + res, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Unmarshal result as a string. + var account string + err = json.Unmarshal(res, &account) + if err != nil { + return "", err + } + + return account, nil +} + +// GetAccountAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetAccount for the blocking version and more details. +func (c *Client) GetAccountAsync(address btcutil.Address) FutureGetAccountResult { + addr := address.EncodeAddress() + cmd := btcjson.NewGetAccountCmd(addr) + return c.sendCmd(cmd) +} + +// GetAccount returns the account associated with the passed address. +func (c *Client) GetAccount(address btcutil.Address) (string, error) { + return c.GetAccountAsync(address).Receive() +} + +// FutureSetAccountResult is a future promise to deliver the result of a +// SetAccountAsync RPC invocation (or an applicable error). +type FutureSetAccountResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of setting the account to be associated with the passed address. +func (r FutureSetAccountResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// SetAccountAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SetAccount for the blocking version and more details. +func (c *Client) SetAccountAsync(address btcutil.Address, account string) FutureSetAccountResult { + addr := address.EncodeAddress() + cmd := btcjson.NewSetAccountCmd(addr, account) + return c.sendCmd(cmd) +} + +// SetAccount sets the account associated with the passed address. +func (c *Client) SetAccount(address btcutil.Address, account string) error { + return c.SetAccountAsync(address, account).Receive() +} + +// FutureGetAddressesByAccountResult is a future promise to deliver the result +// of a GetAddressesByAccountAsync RPC invocation (or an applicable error). +type FutureGetAddressesByAccountResult chan *response + +// Receive waits for the response promised by the future and returns the list of +// addresses associated with the passed account. +func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmashal result as an array of string. + var addrStrings []string + err = json.Unmarshal(res, &addrStrings) + if err != nil { + return nil, err + } + + addrs := make([]btcutil.Address, 0, len(addrStrings)) + for _, addrStr := range addrStrings { + addr, err := btcutil.DecodeAddress(addrStr, + &chaincfg.MainNetParams) + if err != nil { + return nil, err + } + addrs = append(addrs, addr) + } + + return addrs, nil +} + +// GetAddressesByAccountAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetAddressesByAccount for the blocking version and more details. +func (c *Client) GetAddressesByAccountAsync(account string) FutureGetAddressesByAccountResult { + cmd := btcjson.NewGetAddressesByAccountCmd(account) + return c.sendCmd(cmd) +} + +// GetAddressesByAccount returns the list of addresses associated with the +// passed account. +func (c *Client) GetAddressesByAccount(account string) ([]btcutil.Address, error) { + return c.GetAddressesByAccountAsync(account).Receive() +} + +// FutureMoveResult is a future promise to deliver the result of a MoveAsync, +// MoveMinConfAsync, or MoveCommentAsync RPC invocation (or an applicable +// error). +type FutureMoveResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of the move operation. +func (r FutureMoveResult) Receive() (bool, error) { + res, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Unmarshal result as a boolean. + var moveResult bool + err = json.Unmarshal(res, &moveResult) + if err != nil { + return false, err + } + + return moveResult, nil +} + +// MoveAsync returns an instance of a type that can be used to get the result of +// the RPC at some future time by invoking the Receive function on the returned +// instance. +// +// See Move for the blocking version and more details. +func (c *Client) MoveAsync(fromAccount, toAccount string, amount btcutil.Amount) FutureMoveResult { + cmd := btcjson.NewMoveCmd(fromAccount, toAccount, amount.ToBTC(), nil, + nil) + return c.sendCmd(cmd) +} + +// Move moves specified amount from one account in your wallet to another. Only +// funds with the default number of minimum confirmations will be used. +// +// See MoveMinConf and MoveComment for different options. +func (c *Client) Move(fromAccount, toAccount string, amount btcutil.Amount) (bool, error) { + return c.MoveAsync(fromAccount, toAccount, amount).Receive() +} + +// MoveMinConfAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See MoveMinConf for the blocking version and more details. +func (c *Client) MoveMinConfAsync(fromAccount, toAccount string, + amount btcutil.Amount, minConfirms int) FutureMoveResult { + + cmd := btcjson.NewMoveCmd(fromAccount, toAccount, amount.ToBTC(), + &minConfirms, nil) + return c.sendCmd(cmd) +} + +// MoveMinConf moves specified amount from one account in your wallet to +// another. Only funds with the passed number of minimum confirmations will be +// used. +// +// See Move to use the default number of minimum confirmations and MoveComment +// for additional options. +func (c *Client) MoveMinConf(fromAccount, toAccount string, amount btcutil.Amount, minConf int) (bool, error) { + return c.MoveMinConfAsync(fromAccount, toAccount, amount, minConf).Receive() +} + +// MoveCommentAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See MoveComment for the blocking version and more details. +func (c *Client) MoveCommentAsync(fromAccount, toAccount string, + amount btcutil.Amount, minConfirms int, comment string) FutureMoveResult { + + cmd := btcjson.NewMoveCmd(fromAccount, toAccount, amount.ToBTC(), + &minConfirms, &comment) + return c.sendCmd(cmd) +} + +// MoveComment moves specified amount from one account in your wallet to +// another and stores the provided comment in the wallet. The comment +// parameter is only available in the wallet. Only funds with the passed number +// of minimum confirmations will be used. +// +// See Move and MoveMinConf to use defaults. +func (c *Client) MoveComment(fromAccount, toAccount string, amount btcutil.Amount, + minConf int, comment string) (bool, error) { + + return c.MoveCommentAsync(fromAccount, toAccount, amount, minConf, + comment).Receive() +} + +// FutureRenameAccountResult is a future promise to deliver the result of a +// RenameAccountAsync RPC invocation (or an applicable error). +type FutureRenameAccountResult chan *response + +// Receive waits for the response promised by the future and returns the +// result of creating new account. +func (r FutureRenameAccountResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// RenameAccountAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See RenameAccount for the blocking version and more details. +func (c *Client) RenameAccountAsync(oldAccount, newAccount string) FutureRenameAccountResult { + cmd := btcjson.NewRenameAccountCmd(oldAccount, newAccount) + return c.sendCmd(cmd) +} + +// RenameAccount creates a new wallet account. +func (c *Client) RenameAccount(oldAccount, newAccount string) error { + return c.RenameAccountAsync(oldAccount, newAccount).Receive() +} + +// FutureValidateAddressResult is a future promise to deliver the result of a +// ValidateAddressAsync RPC invocation (or an applicable error). +type FutureValidateAddressResult chan *response + +// Receive waits for the response promised by the future and returns information +// about the given bitcoin address. +func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressWalletResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a validateaddress result object. + var addrResult btcjson.ValidateAddressWalletResult + err = json.Unmarshal(res, &addrResult) + if err != nil { + return nil, err + } + + return &addrResult, nil +} + +// ValidateAddressAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See ValidateAddress for the blocking version and more details. +func (c *Client) ValidateAddressAsync(address btcutil.Address) FutureValidateAddressResult { + addr := address.EncodeAddress() + cmd := btcjson.NewValidateAddressCmd(addr) + return c.sendCmd(cmd) +} + +// ValidateAddress returns information about the given bitcoin address. +func (c *Client) ValidateAddress(address btcutil.Address) (*btcjson.ValidateAddressWalletResult, error) { + return c.ValidateAddressAsync(address).Receive() +} + +// FutureKeyPoolRefillResult is a future promise to deliver the result of a +// KeyPoolRefillAsync RPC invocation (or an applicable error). +type FutureKeyPoolRefillResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of refilling the key pool. +func (r FutureKeyPoolRefillResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// KeyPoolRefillAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See KeyPoolRefill for the blocking version and more details. +func (c *Client) KeyPoolRefillAsync() FutureKeyPoolRefillResult { + cmd := btcjson.NewKeyPoolRefillCmd(nil) + return c.sendCmd(cmd) +} + +// KeyPoolRefill fills the key pool as necessary to reach the default size. +// +// See KeyPoolRefillSize to override the size of the key pool. +func (c *Client) KeyPoolRefill() error { + return c.KeyPoolRefillAsync().Receive() +} + +// KeyPoolRefillSizeAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See KeyPoolRefillSize for the blocking version and more details. +func (c *Client) KeyPoolRefillSizeAsync(newSize uint) FutureKeyPoolRefillResult { + cmd := btcjson.NewKeyPoolRefillCmd(&newSize) + return c.sendCmd(cmd) +} + +// KeyPoolRefillSize fills the key pool as necessary to reach the specified +// size. +func (c *Client) KeyPoolRefillSize(newSize uint) error { + return c.KeyPoolRefillSizeAsync(newSize).Receive() +} + +// ************************ +// Amount/Balance Functions +// ************************ + +// FutureListAccountsResult is a future promise to deliver the result of a +// ListAccountsAsync or ListAccountsMinConfAsync RPC invocation (or an +// applicable error). +type FutureListAccountsResult chan *response + +// Receive waits for the response promised by the future and returns returns a +// map of account names and their associated balances. +func (r FutureListAccountsResult) Receive() (map[string]btcutil.Amount, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a json object. + var accounts map[string]float64 + err = json.Unmarshal(res, &accounts) + if err != nil { + return nil, err + } + + accountsMap := make(map[string]btcutil.Amount) + for k, v := range accounts { + amount, err := btcutil.NewAmount(v) + if err != nil { + return nil, err + } + + accountsMap[k] = amount + } + + return accountsMap, nil +} + +// ListAccountsAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ListAccounts for the blocking version and more details. +func (c *Client) ListAccountsAsync() FutureListAccountsResult { + cmd := btcjson.NewListAccountsCmd(nil) + return c.sendCmd(cmd) +} + +// ListAccounts returns a map of account names and their associated balances +// using the default number of minimum confirmations. +// +// See ListAccountsMinConf to override the minimum number of confirmations. +func (c *Client) ListAccounts() (map[string]btcutil.Amount, error) { + return c.ListAccountsAsync().Receive() +} + +// ListAccountsMinConfAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListAccountsMinConf for the blocking version and more details. +func (c *Client) ListAccountsMinConfAsync(minConfirms int) FutureListAccountsResult { + cmd := btcjson.NewListAccountsCmd(&minConfirms) + return c.sendCmd(cmd) +} + +// ListAccountsMinConf returns a map of account names and their associated +// balances using the specified number of minimum confirmations. +// +// See ListAccounts to use the default minimum number of confirmations. +func (c *Client) ListAccountsMinConf(minConfirms int) (map[string]btcutil.Amount, error) { + return c.ListAccountsMinConfAsync(minConfirms).Receive() +} + +// FutureGetBalanceResult is a future promise to deliver the result of a +// GetBalanceAsync or GetBalanceMinConfAsync RPC invocation (or an applicable +// error). +type FutureGetBalanceResult chan *response + +// Receive waits for the response promised by the future and returns the +// available balance from the server for the specified account. +func (r FutureGetBalanceResult) Receive() (btcutil.Amount, error) { + res, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Unmarshal result as a floating point number. + var balance float64 + err = json.Unmarshal(res, &balance) + if err != nil { + return 0, err + } + + amount, err := btcutil.NewAmount(balance) + if err != nil { + return 0, err + } + + return amount, nil +} + +// FutureGetBalanceParseResult is same as FutureGetBalanceResult except +// that the result is expected to be a string which is then parsed into +// a float64 value +// This is required for compatiblity with servers like blockchain.info +type FutureGetBalanceParseResult chan *response + +// Receive waits for the response promised by the future and returns the +// available balance from the server for the specified account. +func (r FutureGetBalanceParseResult) Receive() (btcutil.Amount, error) { + res, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Unmarshal result as a string + var balanceString string + err = json.Unmarshal(res, &balanceString) + if err != nil { + return 0, err + } + + balance, err := strconv.ParseFloat(balanceString, 64) + if err != nil { + return 0, err + } + amount, err := btcutil.NewAmount(balance) + if err != nil { + return 0, err + } + + return amount, nil +} + +// GetBalanceAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetBalance for the blocking version and more details. +func (c *Client) GetBalanceAsync(account string) FutureGetBalanceResult { + cmd := btcjson.NewGetBalanceCmd(&account, nil) + return c.sendCmd(cmd) +} + +// GetBalance returns the available balance from the server for the specified +// account using the default number of minimum confirmations. The account may +// be "*" for all accounts. +// +// See GetBalanceMinConf to override the minimum number of confirmations. +func (c *Client) GetBalance(account string) (btcutil.Amount, error) { + return c.GetBalanceAsync(account).Receive() +} + +// GetBalanceMinConfAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetBalanceMinConf for the blocking version and more details. +func (c *Client) GetBalanceMinConfAsync(account string, minConfirms int) FutureGetBalanceResult { + cmd := btcjson.NewGetBalanceCmd(&account, &minConfirms) + return c.sendCmd(cmd) +} + +// GetBalanceMinConf returns the available balance from the server for the +// specified account using the specified number of minimum confirmations. The +// account may be "*" for all accounts. +// +// See GetBalance to use the default minimum number of confirmations. +func (c *Client) GetBalanceMinConf(account string, minConfirms int) (btcutil.Amount, error) { + if c.config.EnableBCInfoHacks { + response := c.GetBalanceMinConfAsync(account, minConfirms) + return FutureGetBalanceParseResult(response).Receive() + } + return c.GetBalanceMinConfAsync(account, minConfirms).Receive() +} + +// FutureGetReceivedByAccountResult is a future promise to deliver the result of +// a GetReceivedByAccountAsync or GetReceivedByAccountMinConfAsync RPC +// invocation (or an applicable error). +type FutureGetReceivedByAccountResult chan *response + +// Receive waits for the response promised by the future and returns the total +// amount received with the specified account. +func (r FutureGetReceivedByAccountResult) Receive() (btcutil.Amount, error) { + res, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Unmarshal result as a floating point number. + var balance float64 + err = json.Unmarshal(res, &balance) + if err != nil { + return 0, err + } + + amount, err := btcutil.NewAmount(balance) + if err != nil { + return 0, err + } + + return amount, nil +} + +// GetReceivedByAccountAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetReceivedByAccount for the blocking version and more details. +func (c *Client) GetReceivedByAccountAsync(account string) FutureGetReceivedByAccountResult { + cmd := btcjson.NewGetReceivedByAccountCmd(account, nil) + return c.sendCmd(cmd) +} + +// GetReceivedByAccount returns the total amount received with the specified +// account with at least the default number of minimum confirmations. +// +// See GetReceivedByAccountMinConf to override the minimum number of +// confirmations. +func (c *Client) GetReceivedByAccount(account string) (btcutil.Amount, error) { + return c.GetReceivedByAccountAsync(account).Receive() +} + +// GetReceivedByAccountMinConfAsync returns an instance of a type that can be +// used to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetReceivedByAccountMinConf for the blocking version and more details. +func (c *Client) GetReceivedByAccountMinConfAsync(account string, minConfirms int) FutureGetReceivedByAccountResult { + cmd := btcjson.NewGetReceivedByAccountCmd(account, &minConfirms) + return c.sendCmd(cmd) +} + +// GetReceivedByAccountMinConf returns the total amount received with the +// specified account with at least the specified number of minimum +// confirmations. +// +// See GetReceivedByAccount to use the default minimum number of confirmations. +func (c *Client) GetReceivedByAccountMinConf(account string, minConfirms int) (btcutil.Amount, error) { + return c.GetReceivedByAccountMinConfAsync(account, minConfirms).Receive() +} + +// FutureGetUnconfirmedBalanceResult is a future promise to deliver the result +// of a GetUnconfirmedBalanceAsync RPC invocation (or an applicable error). +type FutureGetUnconfirmedBalanceResult chan *response + +// Receive waits for the response promised by the future and returns returns the +// unconfirmed balance from the server for the specified account. +func (r FutureGetUnconfirmedBalanceResult) Receive() (btcutil.Amount, error) { + res, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Unmarshal result as a floating point number. + var balance float64 + err = json.Unmarshal(res, &balance) + if err != nil { + return 0, err + } + + amount, err := btcutil.NewAmount(balance) + if err != nil { + return 0, err + } + + return amount, nil +} + +// GetUnconfirmedBalanceAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetUnconfirmedBalance for the blocking version and more details. +func (c *Client) GetUnconfirmedBalanceAsync(account string) FutureGetUnconfirmedBalanceResult { + cmd := btcjson.NewGetUnconfirmedBalanceCmd(&account) + return c.sendCmd(cmd) +} + +// GetUnconfirmedBalance returns the unconfirmed balance from the server for +// the specified account. +func (c *Client) GetUnconfirmedBalance(account string) (btcutil.Amount, error) { + return c.GetUnconfirmedBalanceAsync(account).Receive() +} + +// FutureGetReceivedByAddressResult is a future promise to deliver the result of +// a GetReceivedByAddressAsync or GetReceivedByAddressMinConfAsync RPC +// invocation (or an applicable error). +type FutureGetReceivedByAddressResult chan *response + +// Receive waits for the response promised by the future and returns the total +// amount received by the specified address. +func (r FutureGetReceivedByAddressResult) Receive() (btcutil.Amount, error) { + res, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Unmarshal result as a floating point number. + var balance float64 + err = json.Unmarshal(res, &balance) + if err != nil { + return 0, err + } + + amount, err := btcutil.NewAmount(balance) + if err != nil { + return 0, err + } + + return amount, nil +} + +// GetReceivedByAddressAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetReceivedByAddress for the blocking version and more details. +func (c *Client) GetReceivedByAddressAsync(address btcutil.Address) FutureGetReceivedByAddressResult { + addr := address.EncodeAddress() + cmd := btcjson.NewGetReceivedByAddressCmd(addr, nil) + return c.sendCmd(cmd) + +} + +// GetReceivedByAddress returns the total amount received by the specified +// address with at least the default number of minimum confirmations. +// +// See GetReceivedByAddressMinConf to override the minimum number of +// confirmations. +func (c *Client) GetReceivedByAddress(address btcutil.Address) (btcutil.Amount, error) { + return c.GetReceivedByAddressAsync(address).Receive() +} + +// GetReceivedByAddressMinConfAsync returns an instance of a type that can be +// used to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetReceivedByAddressMinConf for the blocking version and more details. +func (c *Client) GetReceivedByAddressMinConfAsync(address btcutil.Address, minConfirms int) FutureGetReceivedByAddressResult { + addr := address.EncodeAddress() + cmd := btcjson.NewGetReceivedByAddressCmd(addr, &minConfirms) + return c.sendCmd(cmd) +} + +// GetReceivedByAddressMinConf returns the total amount received by the specified +// address with at least the specified number of minimum confirmations. +// +// See GetReceivedByAddress to use the default minimum number of confirmations. +func (c *Client) GetReceivedByAddressMinConf(address btcutil.Address, minConfirms int) (btcutil.Amount, error) { + return c.GetReceivedByAddressMinConfAsync(address, minConfirms).Receive() +} + +// FutureListReceivedByAccountResult is a future promise to deliver the result +// of a ListReceivedByAccountAsync, ListReceivedByAccountMinConfAsync, or +// ListReceivedByAccountIncludeEmptyAsync RPC invocation (or an applicable +// error). +type FutureListReceivedByAccountResult chan *response + +// Receive waits for the response promised by the future and returns a list of +// balances by account. +func (r FutureListReceivedByAccountResult) Receive() ([]btcjson.ListReceivedByAccountResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of listreceivedbyaccount result objects. + var received []btcjson.ListReceivedByAccountResult + err = json.Unmarshal(res, &received) + if err != nil { + return nil, err + } + + return received, nil +} + +// ListReceivedByAccountAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListReceivedByAccount for the blocking version and more details. +func (c *Client) ListReceivedByAccountAsync() FutureListReceivedByAccountResult { + cmd := btcjson.NewListReceivedByAccountCmd(nil, nil, nil) + return c.sendCmd(cmd) +} + +// ListReceivedByAccount lists balances by account using the default number +// of minimum confirmations and including accounts that haven't received any +// payments. +// +// See ListReceivedByAccountMinConf to override the minimum number of +// confirmations and ListReceivedByAccountIncludeEmpty to filter accounts that +// haven't received any payments from the results. +func (c *Client) ListReceivedByAccount() ([]btcjson.ListReceivedByAccountResult, error) { + return c.ListReceivedByAccountAsync().Receive() +} + +// ListReceivedByAccountMinConfAsync returns an instance of a type that can be +// used to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListReceivedByAccountMinConf for the blocking version and more details. +func (c *Client) ListReceivedByAccountMinConfAsync(minConfirms int) FutureListReceivedByAccountResult { + cmd := btcjson.NewListReceivedByAccountCmd(&minConfirms, nil, nil) + return c.sendCmd(cmd) +} + +// ListReceivedByAccountMinConf lists balances by account using the specified +// number of minimum confirmations not including accounts that haven't received +// any payments. +// +// See ListReceivedByAccount to use the default minimum number of confirmations +// and ListReceivedByAccountIncludeEmpty to also include accounts that haven't +// received any payments in the results. +func (c *Client) ListReceivedByAccountMinConf(minConfirms int) ([]btcjson.ListReceivedByAccountResult, error) { + return c.ListReceivedByAccountMinConfAsync(minConfirms).Receive() +} + +// ListReceivedByAccountIncludeEmptyAsync returns an instance of a type that can +// be used to get the result of the RPC at some future time by invoking the +// Receive function on the returned instance. +// +// See ListReceivedByAccountIncludeEmpty for the blocking version and more details. +func (c *Client) ListReceivedByAccountIncludeEmptyAsync(minConfirms int, includeEmpty bool) FutureListReceivedByAccountResult { + cmd := btcjson.NewListReceivedByAccountCmd(&minConfirms, &includeEmpty, + nil) + return c.sendCmd(cmd) +} + +// ListReceivedByAccountIncludeEmpty lists balances by account using the +// specified number of minimum confirmations and including accounts that +// haven't received any payments depending on specified flag. +// +// See ListReceivedByAccount and ListReceivedByAccountMinConf to use defaults. +func (c *Client) ListReceivedByAccountIncludeEmpty(minConfirms int, includeEmpty bool) ([]btcjson.ListReceivedByAccountResult, error) { + return c.ListReceivedByAccountIncludeEmptyAsync(minConfirms, + includeEmpty).Receive() +} + +// FutureListReceivedByAddressResult is a future promise to deliver the result +// of a ListReceivedByAddressAsync, ListReceivedByAddressMinConfAsync, or +// ListReceivedByAddressIncludeEmptyAsync RPC invocation (or an applicable +// error). +type FutureListReceivedByAddressResult chan *response + +// Receive waits for the response promised by the future and returns a list of +// balances by address. +func (r FutureListReceivedByAddressResult) Receive() ([]btcjson.ListReceivedByAddressResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of listreceivedbyaddress result objects. + var received []btcjson.ListReceivedByAddressResult + err = json.Unmarshal(res, &received) + if err != nil { + return nil, err + } + + return received, nil +} + +// ListReceivedByAddressAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListReceivedByAddress for the blocking version and more details. +func (c *Client) ListReceivedByAddressAsync() FutureListReceivedByAddressResult { + cmd := btcjson.NewListReceivedByAddressCmd(nil, nil, nil) + return c.sendCmd(cmd) +} + +// ListReceivedByAddress lists balances by address using the default number +// of minimum confirmations not including addresses that haven't received any +// payments or watching only addresses. +// +// See ListReceivedByAddressMinConf to override the minimum number of +// confirmations and ListReceivedByAddressIncludeEmpty to also include addresses +// that haven't received any payments in the results. +func (c *Client) ListReceivedByAddress() ([]btcjson.ListReceivedByAddressResult, error) { + return c.ListReceivedByAddressAsync().Receive() +} + +// ListReceivedByAddressMinConfAsync returns an instance of a type that can be +// used to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListReceivedByAddressMinConf for the blocking version and more details. +func (c *Client) ListReceivedByAddressMinConfAsync(minConfirms int) FutureListReceivedByAddressResult { + cmd := btcjson.NewListReceivedByAddressCmd(&minConfirms, nil, nil) + return c.sendCmd(cmd) +} + +// ListReceivedByAddressMinConf lists balances by address using the specified +// number of minimum confirmations not including addresses that haven't received +// any payments. +// +// See ListReceivedByAddress to use the default minimum number of confirmations +// and ListReceivedByAddressIncludeEmpty to also include addresses that haven't +// received any payments in the results. +func (c *Client) ListReceivedByAddressMinConf(minConfirms int) ([]btcjson.ListReceivedByAddressResult, error) { + return c.ListReceivedByAddressMinConfAsync(minConfirms).Receive() +} + +// ListReceivedByAddressIncludeEmptyAsync returns an instance of a type that can +// be used to get the result of the RPC at some future time by invoking the +// Receive function on the returned instance. +// +// See ListReceivedByAccountIncludeEmpty for the blocking version and more details. +func (c *Client) ListReceivedByAddressIncludeEmptyAsync(minConfirms int, includeEmpty bool) FutureListReceivedByAddressResult { + cmd := btcjson.NewListReceivedByAddressCmd(&minConfirms, &includeEmpty, + nil) + return c.sendCmd(cmd) +} + +// ListReceivedByAddressIncludeEmpty lists balances by address using the +// specified number of minimum confirmations and including addresses that +// haven't received any payments depending on specified flag. +// +// See ListReceivedByAddress and ListReceivedByAddressMinConf to use defaults. +func (c *Client) ListReceivedByAddressIncludeEmpty(minConfirms int, includeEmpty bool) ([]btcjson.ListReceivedByAddressResult, error) { + return c.ListReceivedByAddressIncludeEmptyAsync(minConfirms, + includeEmpty).Receive() +} + +// ************************ +// Wallet Locking Functions +// ************************ + +// FutureWalletLockResult is a future promise to deliver the result of a +// WalletLockAsync RPC invocation (or an applicable error). +type FutureWalletLockResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of locking the wallet. +func (r FutureWalletLockResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// WalletLockAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See WalletLock for the blocking version and more details. +func (c *Client) WalletLockAsync() FutureWalletLockResult { + cmd := btcjson.NewWalletLockCmd() + return c.sendCmd(cmd) +} + +// WalletLock locks the wallet by removing the encryption key from memory. +// +// After calling this function, the WalletPassphrase function must be used to +// unlock the wallet prior to calling any other function which requires the +// wallet to be unlocked. +func (c *Client) WalletLock() error { + return c.WalletLockAsync().Receive() +} + +// WalletPassphrase unlocks the wallet by using the passphrase to derive the +// decryption key which is then stored in memory for the specified timeout +// (in seconds). +func (c *Client) WalletPassphrase(passphrase string, timeoutSecs int64) error { + cmd := btcjson.NewWalletPassphraseCmd(passphrase, timeoutSecs) + _, err := c.sendCmdAndWait(cmd) + return err +} + +// FutureWalletPassphraseChangeResult is a future promise to deliver the result +// of a WalletPassphraseChangeAsync RPC invocation (or an applicable error). +type FutureWalletPassphraseChangeResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of changing the wallet passphrase. +func (r FutureWalletPassphraseChangeResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// WalletPassphraseChangeAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See WalletPassphraseChange for the blocking version and more details. +func (c *Client) WalletPassphraseChangeAsync(old, new string) FutureWalletPassphraseChangeResult { + cmd := btcjson.NewWalletPassphraseChangeCmd(old, new) + return c.sendCmd(cmd) +} + +// WalletPassphraseChange changes the wallet passphrase from the specified old +// to new passphrase. +func (c *Client) WalletPassphraseChange(old, new string) error { + return c.WalletPassphraseChangeAsync(old, new).Receive() +} + +// ************************* +// Message Signing Functions +// ************************* + +// FutureSignMessageResult is a future promise to deliver the result of a +// SignMessageAsync RPC invocation (or an applicable error). +type FutureSignMessageResult chan *response + +// Receive waits for the response promised by the future and returns the message +// signed with the private key of the specified address. +func (r FutureSignMessageResult) Receive() (string, error) { + res, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Unmarshal result as a string. + var b64 string + err = json.Unmarshal(res, &b64) + if err != nil { + return "", err + } + + return b64, nil +} + +// SignMessageAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SignMessage for the blocking version and more details. +func (c *Client) SignMessageAsync(address btcutil.Address, message string) FutureSignMessageResult { + addr := address.EncodeAddress() + cmd := btcjson.NewSignMessageCmd(addr, message) + return c.sendCmd(cmd) +} + +// SignMessage signs a message with the private key of the specified address. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SignMessage(address btcutil.Address, message string) (string, error) { + return c.SignMessageAsync(address, message).Receive() +} + +// FutureVerifyMessageResult is a future promise to deliver the result of a +// VerifyMessageAsync RPC invocation (or an applicable error). +type FutureVerifyMessageResult chan *response + +// Receive waits for the response promised by the future and returns whether or +// not the message was successfully verified. +func (r FutureVerifyMessageResult) Receive() (bool, error) { + res, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Unmarshal result as a boolean. + var verified bool + err = json.Unmarshal(res, &verified) + if err != nil { + return false, err + } + + return verified, nil +} + +// VerifyMessageAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See VerifyMessage for the blocking version and more details. +func (c *Client) VerifyMessageAsync(address btcutil.Address, signature, message string) FutureVerifyMessageResult { + addr := address.EncodeAddress() + cmd := btcjson.NewVerifyMessageCmd(addr, signature, message) + return c.sendCmd(cmd) +} + +// VerifyMessage verifies a signed message. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) VerifyMessage(address btcutil.Address, signature, message string) (bool, error) { + return c.VerifyMessageAsync(address, signature, message).Receive() +} + +// ********************* +// Dump/Import Functions +// ********************* + +// FutureDumpPrivKeyResult is a future promise to deliver the result of a +// DumpPrivKeyAsync RPC invocation (or an applicable error). +type FutureDumpPrivKeyResult chan *response + +// Receive waits for the response promised by the future and returns the private +// key corresponding to the passed address encoded in the wallet import format +// (WIF) +func (r FutureDumpPrivKeyResult) Receive() (*btcutil.WIF, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var privKeyWIF string + err = json.Unmarshal(res, &privKeyWIF) + if err != nil { + return nil, err + } + + return btcutil.DecodeWIF(privKeyWIF) +} + +// DumpPrivKeyAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See DumpPrivKey for the blocking version and more details. +func (c *Client) DumpPrivKeyAsync(address btcutil.Address) FutureDumpPrivKeyResult { + addr := address.EncodeAddress() + cmd := btcjson.NewDumpPrivKeyCmd(addr) + return c.sendCmd(cmd) +} + +// DumpPrivKey gets the private key corresponding to the passed address encoded +// in the wallet import format (WIF). +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) DumpPrivKey(address btcutil.Address) (*btcutil.WIF, error) { + return c.DumpPrivKeyAsync(address).Receive() +} + +// FutureImportAddressResult is a future promise to deliver the result of an +// ImportAddressAsync RPC invocation (or an applicable error). +type FutureImportAddressResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of importing the passed public address. +func (r FutureImportAddressResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// ImportAddressAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ImportAddress for the blocking version and more details. +func (c *Client) ImportAddressAsync(address string) FutureImportAddressResult { + cmd := btcjson.NewImportAddressCmd(address, nil) + return c.sendCmd(cmd) +} + +// ImportAddress imports the passed public address. +func (c *Client) ImportAddress(address string) error { + return c.ImportAddressAsync(address).Receive() +} + +// ImportAddressRescanAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ImportAddress for the blocking version and more details. +func (c *Client) ImportAddressRescanAsync(address string, rescan bool) FutureImportAddressResult { + cmd := btcjson.NewImportAddressCmd(address, &rescan) + return c.sendCmd(cmd) +} + +// ImportAddressRescan imports the passed public address. When rescan is true, +// the block history is scanned for transactions addressed to provided address. +func (c *Client) ImportAddressRescan(address string, rescan bool) error { + return c.ImportAddressRescanAsync(address, rescan).Receive() +} + +// FutureImportPrivKeyResult is a future promise to deliver the result of an +// ImportPrivKeyAsync RPC invocation (or an applicable error). +type FutureImportPrivKeyResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of importing the passed private key which must be the wallet import format +// (WIF). +func (r FutureImportPrivKeyResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// ImportPrivKeyAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ImportPrivKey for the blocking version and more details. +func (c *Client) ImportPrivKeyAsync(privKeyWIF *btcutil.WIF) FutureImportPrivKeyResult { + wif := "" + if privKeyWIF != nil { + wif = privKeyWIF.String() + } + + cmd := btcjson.NewImportPrivKeyCmd(wif, nil, nil) + return c.sendCmd(cmd) +} + +// ImportPrivKey imports the passed private key which must be the wallet import +// format (WIF). +func (c *Client) ImportPrivKey(privKeyWIF *btcutil.WIF) error { + return c.ImportPrivKeyAsync(privKeyWIF).Receive() +} + +// ImportPrivKeyLabelAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ImportPrivKey for the blocking version and more details. +func (c *Client) ImportPrivKeyLabelAsync(privKeyWIF *btcutil.WIF, label string) FutureImportPrivKeyResult { + wif := "" + if privKeyWIF != nil { + wif = privKeyWIF.String() + } + + cmd := btcjson.NewImportPrivKeyCmd(wif, &label, nil) + return c.sendCmd(cmd) +} + +// ImportPrivKeyLabel imports the passed private key which must be the wallet import +// format (WIF). It sets the account label to the one provided. +func (c *Client) ImportPrivKeyLabel(privKeyWIF *btcutil.WIF, label string) error { + return c.ImportPrivKeyLabelAsync(privKeyWIF, label).Receive() +} + +// ImportPrivKeyRescanAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ImportPrivKey for the blocking version and more details. +func (c *Client) ImportPrivKeyRescanAsync(privKeyWIF *btcutil.WIF, label string, rescan bool) FutureImportPrivKeyResult { + wif := "" + if privKeyWIF != nil { + wif = privKeyWIF.String() + } + + cmd := btcjson.NewImportPrivKeyCmd(wif, &label, &rescan) + return c.sendCmd(cmd) +} + +// ImportPrivKeyRescan imports the passed private key which must be the wallet import +// format (WIF). It sets the account label to the one provided. When rescan is true, +// the block history is scanned for transactions addressed to provided privKey. +func (c *Client) ImportPrivKeyRescan(privKeyWIF *btcutil.WIF, label string, rescan bool) error { + return c.ImportPrivKeyRescanAsync(privKeyWIF, label, rescan).Receive() +} + +// FutureImportPubKeyResult is a future promise to deliver the result of an +// ImportPubKeyAsync RPC invocation (or an applicable error). +type FutureImportPubKeyResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of importing the passed public key. +func (r FutureImportPubKeyResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// ImportPubKeyAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ImportPubKey for the blocking version and more details. +func (c *Client) ImportPubKeyAsync(pubKey string) FutureImportPubKeyResult { + cmd := btcjson.NewImportPubKeyCmd(pubKey, nil) + return c.sendCmd(cmd) +} + +// ImportPubKey imports the passed public key. +func (c *Client) ImportPubKey(pubKey string) error { + return c.ImportPubKeyAsync(pubKey).Receive() +} + +// ImportPubKeyRescanAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ImportPubKey for the blocking version and more details. +func (c *Client) ImportPubKeyRescanAsync(pubKey string, rescan bool) FutureImportPubKeyResult { + cmd := btcjson.NewImportPubKeyCmd(pubKey, &rescan) + return c.sendCmd(cmd) +} + +// ImportPubKeyRescan imports the passed public key. When rescan is true, the +// block history is scanned for transactions addressed to provided pubkey. +func (c *Client) ImportPubKeyRescan(pubKey string, rescan bool) error { + return c.ImportPubKeyRescanAsync(pubKey, rescan).Receive() +} + +// *********************** +// Miscellaneous Functions +// *********************** + +// NOTE: While getinfo is implemented here (in wallet.go), a btcd chain server +// will respond to getinfo requests as well, excluding any wallet information. + +// FutureGetInfoResult is a future promise to deliver the result of a +// GetInfoAsync RPC invocation (or an applicable error). +type FutureGetInfoResult chan *response + +// Receive waits for the response promised by the future and returns the info +// provided by the server. +func (r FutureGetInfoResult) Receive() (*btcjson.InfoWalletResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a getinfo result object. + var infoRes btcjson.InfoWalletResult + err = json.Unmarshal(res, &infoRes) + if err != nil { + return nil, err + } + + return &infoRes, nil +} + +// GetInfoAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetInfo for the blocking version and more details. +func (c *Client) GetInfoAsync() FutureGetInfoResult { + cmd := btcjson.NewGetInfoCmd() + return c.sendCmd(cmd) +} + +// GetInfo returns miscellaneous info regarding the RPC server. The returned +// info object may be void of wallet information if the remote server does +// not include wallet functionality. +func (c *Client) GetInfo() (*btcjson.InfoWalletResult, error) { + return c.GetInfoAsync().Receive() +} + +// TODO(davec): Implement +// backupwallet (NYI in btcwallet) +// encryptwallet (Won't be supported by btcwallet since it's always encrypted) +// getwalletinfo (NYI in btcwallet or btcjson) +// listaddressgroupings (NYI in btcwallet) +// listreceivedbyaccount (NYI in btcwallet) + +// DUMP +// importwallet (NYI in btcwallet) +// dumpwallet (NYI in btcwallet)