b89c91b9d6
This refactors the notification state mutex out of the state itself to the client. This is being done since the code makes a copy of the notification state and accesses that copy immutably, and therefore there is no need for it to have its own mutex.
1102 lines
35 KiB
Go
1102 lines
35 KiB
Go
// Copyright (c) 2014-2016 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package btcrpcclient
|
|
|
|
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.
|
|
OnBlockConnected func(hash *chainhash.Hash, height int32, t time.Time)
|
|
|
|
// 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.
|
|
OnBlockDisconnected func(hash *chainhash.Hash, height int32, t time.Time)
|
|
|
|
// 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.
|
|
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.
|
|
OnRedeemingTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails)
|
|
|
|
// OnRescanFinished is invoked after a rescan finishes due to a previous
|
|
// call to Rescan or RescanEndHeight. Finished rescans should be
|
|
// 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.
|
|
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.
|
|
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<X> 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)
|
|
|
|
// 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)
|
|
|
|
// 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)
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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, btcutil.Amount(amt), nil
|
|
}
|
|
|
|
// parseTxAcceptedVerboseNtfnParams parses out details about a raw transaction
|
|
// from the parameters of a txacceptedverbose notification.
|
|
func parseTxAcceptedVerboseNtfnParams(params []json.RawMessage) (*btcjson.TxRawResult,
|
|
error) {
|
|
|
|
if len(params) != 1 {
|
|
return nil, wrongNumParams(len(params))
|
|
}
|
|
|
|
// Unmarshal first parameter as a raw transaction result object.
|
|
var rawTx btcjson.TxRawResult
|
|
err := json.Unmarshal(params[0], &rawTx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO: change txacceptedverbose 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, btcutil.Amount(bal), confirmed, nil
|
|
}
|
|
|
|
// parseWalletLockStateNtfnParams parses out the account name and locked
|
|
// state of an account from the parameters of a walletlockstate notification.
|
|
func parseWalletLockStateNtfnParams(params []json.RawMessage) (account string,
|
|
locked bool, err error) {
|
|
|
|
if len(params) != 2 {
|
|
return "", false, wrongNumParams(len(params))
|
|
}
|
|
|
|
// Unmarshal first parameter as a string.
|
|
err = json.Unmarshal(params[0], &account)
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
|
|
// Unmarshal second parameter as a boolean.
|
|
err = json.Unmarshal(params[1], &locked)
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
|
|
return account, locked, nil
|
|
}
|
|
|
|
// FutureNotifyBlocksResult is a future promise to deliver the result of a
|
|
// NotifyBlocksAsync RPC invocation (or an applicable error).
|
|
type FutureNotifyBlocksResult chan *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)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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).
|
|
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)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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.
|
|
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.
|
|
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)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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).
|
|
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)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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.
|
|
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.
|
|
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).
|
|
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)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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.
|
|
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.
|
|
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.
|
|
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.
|
|
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()
|
|
}
|