notify.go raw

   1  package rpcclient
   2  
   3  import (
   4  	"bytes"
   5  	"encoding/hex"
   6  	js "encoding/json"
   7  	"errors"
   8  	"fmt"
   9  	"github.com/p9c/p9/pkg/amt"
  10  	"github.com/p9c/p9/pkg/btcaddr"
  11  	"time"
  12  	
  13  	"github.com/p9c/p9/pkg/btcjson"
  14  	"github.com/p9c/p9/pkg/chainhash"
  15  	"github.com/p9c/p9/pkg/util"
  16  	"github.com/p9c/p9/pkg/wire"
  17  )
  18  
  19  var (
  20  	// ErrWebsocketsRequired is an error to describe the condition where the caller is trying to use a websocket-only
  21  	// feature, such as requesting notifications or other websocket requests when the client is configured to run in
  22  	// HTTP POST mode.
  23  	ErrWebsocketsRequired = errors.New(
  24  		"a websocket connection is required " +
  25  			"to use this feature",
  26  	)
  27  )
  28  
  29  // notificationState is used to track the current state of successfully registered notification so the state can be
  30  // automatically re-established on reconnect.
  31  type notificationState struct {
  32  	notifyBlocks       bool
  33  	notifyNewTx        bool
  34  	notifyNewTxVerbose bool
  35  	notifyReceived     map[string]struct{}
  36  	notifySpent        map[btcjson.OutPoint]struct{}
  37  }
  38  
  39  // Copy returns a deep copy of the receiver.
  40  func (s *notificationState) Copy() *notificationState {
  41  	var stateCopy notificationState
  42  	stateCopy.notifyBlocks = s.notifyBlocks
  43  	stateCopy.notifyNewTx = s.notifyNewTx
  44  	stateCopy.notifyNewTxVerbose = s.notifyNewTxVerbose
  45  	stateCopy.notifyReceived = make(map[string]struct{})
  46  	for addr := range s.notifyReceived {
  47  		stateCopy.notifyReceived[addr] = struct{}{}
  48  	}
  49  	stateCopy.notifySpent = make(map[btcjson.OutPoint]struct{})
  50  	for op := range s.notifySpent {
  51  		stateCopy.notifySpent[op] = struct{}{}
  52  	}
  53  	return &stateCopy
  54  }
  55  
  56  func // newNotificationState returns a new notification state ready to be
  57  // populated.
  58  newNotificationState() *notificationState {
  59  	return &notificationState{
  60  		notifyReceived: make(map[string]struct{}),
  61  		notifySpent:    make(map[btcjson.OutPoint]struct{}),
  62  	}
  63  }
  64  
  65  func // newNilFutureResult returns a new future result channel that already
  66  // has the result waiting on the channel with the reply set to nil.
  67  // This is useful to ignore things such as notifications when the caller didn't
  68  // specify any notification handlers.
  69  newNilFutureResult() chan *response {
  70  	responseChan := make(chan *response, 1)
  71  	responseChan <- &response{result: nil, err: nil}
  72  	return responseChan
  73  }
  74  
  75  // NotificationHandlers defines callback function pointers to invoke with
  76  // notifications. Since all of the functions are nil by default, all notifications are effectively ignored until
  77  // their handlers are set to a concrete callback.
  78  //
  79  // NOTE: Unless otherwise documented, these handlers must NOT directly call any blocking calls on the client
  80  // instance since the input reader goroutine blocks until the callback has completed. Doing so will result in a
  81  // deadlock situation.
  82  type NotificationHandlers struct {
  83  	// OnClientConnected is invoked when the client connects or reconnects to the RPC server. This callback is run async
  84  	// with the rest of the notification handlers, and is safe for blocking client requests.
  85  	OnClientConnected func()
  86  	// OnBlockConnected is invoked when a block is connected to the longest (best) chain. It will only be invoked if a
  87  	// preceding call to NotifyBlocks has been made to register for the notification and the function is non-nil. NOTE:
  88  	// Deprecated. Use OnFilteredBlockConnected instead.
  89  	OnBlockConnected func(hash *chainhash.Hash, height int32, t time.Time)
  90  	// OnFilteredBlockConnected is invoked when a block is connected to the longest (best) chain. It will only be
  91  	// invoked if a preceding call to NotifyBlocks has been made to register for the notification and the function is
  92  	// non-nil. Its parameters differ from OnBlockConnected: it receives the block's height, header, and relevant
  93  	// transactions.
  94  	OnFilteredBlockConnected func(height int32, header *wire.BlockHeader, txs []*util.Tx)
  95  	// OnBlockDisconnected is invoked when a block is disconnected from the longest (best) chain. It will only be
  96  	// invoked if a preceding call to NotifyBlocks has been made to register for the notification and the function is
  97  	// non-nil. NOTE: Deprecated. Use OnFilteredBlockDisconnected instead.
  98  	OnBlockDisconnected func(hash *chainhash.Hash, height int32, t time.Time)
  99  	// OnFilteredBlockDisconnected is invoked when a block is disconnected from the longest (best) chain. It will only
 100  	// be invoked if a preceding NotifyBlocks has been made to register for the notification and the call to function is
 101  	// non-nil. Its parameters differ from OnBlockDisconnected: it receives the block's height and header.
 102  	OnFilteredBlockDisconnected func(height int32, header *wire.BlockHeader)
 103  	// OnRecvTx is invoked when a transaction that receives funds to a registered address is received into the memory
 104  	// pool and also connected to the longest (best) chain. It will only be invoked if a preceding call to
 105  	// NotifyReceived, Rescan, or RescanEndHeight has been made to register for the notification and the function is
 106  	// non-nil. NOTE: Deprecated. Use OnRelevantTxAccepted instead.
 107  	OnRecvTx func(transaction *util.Tx, details *btcjson.BlockDetails)
 108  	// OnRedeemingTx is invoked when a transaction that spends a registered outpoint is received into the memory pool
 109  	// and also connected to the longest (best) chain.
 110  	//
 111  	// It will only be invoked if a preceding call to NotifySpent, Rescan, or RescanEndHeight has been made to register
 112  	// for the notification and the function is non-nil.
 113  	//
 114  	// NOTE: The NotifyReceived will automatically register notifications for the outpoints that are now "owned" as a
 115  	// result of receiving funds to the registered addresses.
 116  	//
 117  	// This means it is possible for this to invoked indirectly as the result of a NotifyReceived call. NOTE:
 118  	// Deprecated. Use OnRelevantTxAccepted instead.
 119  	OnRedeemingTx func(transaction *util.Tx, details *btcjson.BlockDetails)
 120  	// OnRelevantTxAccepted is invoked when an unmined transaction passes the client's transaction filter.
 121  	//
 122  	// NOTE: This is a btcsuite extension ported from github.com/decred/dcrrpcclient.
 123  	OnRelevantTxAccepted func(transaction []byte)
 124  	// OnRescanFinished is invoked after a rescan finishes due to a previous call to Rescan or RescanEndHeight. Finished
 125  	// rescans should be signaled on this notification, rather than relying on the return result of a rescan request,
 126  	// due to how pod may send various rescan notifications after the rescan request has already returned.
 127  	//
 128  	// NOTE: Deprecated. Not used with RescanBlocks.
 129  	OnRescanFinished func(hash *chainhash.Hash, height int32, blkTime time.Time)
 130  	// OnRescanProgress is invoked periodically when a rescan is underway. It will only be invoked if a preceding call
 131  	// to Rescan or RescanEndHeight has been made and the function is non-nil.
 132  	//
 133  	// NOTE: Deprecated. Not used with RescanBlocks.
 134  	OnRescanProgress func(hash *chainhash.Hash, height int32, blkTime time.Time)
 135  	// OnTxAccepted is invoked when a transaction is accepted into the memory pool. It will only be invoked if a
 136  	// preceding call to NotifyNewTransactions with the verbose flag set to false has been made to register for the
 137  	// notification and the function is non-nil.
 138  	OnTxAccepted func(hash *chainhash.Hash, amount amt.Amount)
 139  	// OnTxAccepted is invoked when a transaction is accepted into the memory pool. It will only be invoked if a
 140  	// preceding call to NotifyNewTransactions with the verbose flag set to true has been made to register for the
 141  	// notification and the function is non-nil.
 142  	OnTxAcceptedVerbose func(txDetails *btcjson.TxRawResult)
 143  	// OnPodConnected is invoked when a wallet connects or disconnects from pod. This will only be available when client
 144  	// is connected to a wallet server such as btcwallet.
 145  	OnPodConnected func(connected bool)
 146  	// OnAccountBalance is invoked with account balance updates. This will only be available when speaking to a wallet
 147  	// server such as btcwallet.
 148  	OnAccountBalance func(account string, balance amt.Amount, confirmed bool)
 149  	// OnWalletLockState is invoked when a wallet is locked or unlocked. This will only be available when client is
 150  	// connected to a wallet server such as btcwallet.
 151  	OnWalletLockState func(locked bool)
 152  	// OnUnknownNotification is invoked when an unrecognized notification is received. This typically means the
 153  	// notification handling code for this package needs to be updated for a new notification type or the caller is
 154  	// using a custom notification this package does not know about.
 155  	OnUnknownNotification func(method string, params []js.RawMessage)
 156  }
 157  
 158  // handleNotification examines the passed notification type, performs conversions to get the raw notification types into
 159  // higher level types and delivers the notification to the appropriate On<X> handler registered with the client.
 160  func (c *Client) handleNotification(ntfn *rawNotification) {
 161  	D.Ln("<<<Handling Notification>>>", ntfn.Method)
 162  	// Ignore the notification if the client is not interested in any notifications.
 163  	if c.ntfnHandlers == nil {
 164  		D.Ln("<<<no notification handlers registered>>>")
 165  		return
 166  	}
 167  	switch ntfn.Method {
 168  	// OnBlockConnected
 169  	case btcjson.BlockConnectedNtfnMethod:
 170  		// Ignore the notification if the client is not interested in it.
 171  		if c.ntfnHandlers.OnBlockConnected == nil {
 172  			D.Ln("<<<no OnBlockConnected callback registered>>>")
 173  			return
 174  		}
 175  		blockHash, blockHeight, blockTime, e := parseChainNtfnParams(ntfn.Params)
 176  		if e != nil {
 177  			W.Ln("received invalid block connected notification:", e)
 178  			return
 179  		}
 180  		c.ntfnHandlers.OnBlockConnected(blockHash, blockHeight, blockTime)
 181  	// OnFilteredBlockConnected
 182  	case btcjson.FilteredBlockConnectedNtfnMethod:
 183  		// Ignore the notification if the client is not interested in it.
 184  		if c.ntfnHandlers.OnFilteredBlockConnected == nil {
 185  			D.Ln("<<<no OnFilteredBlockConnected callback registered>>>")
 186  			return
 187  		}
 188  		blockHeight, blockHeader, transactions, e :=
 189  			parseFilteredBlockConnectedParams(ntfn.Params)
 190  		if e != nil {
 191  			W.Ln(
 192  				"received invalid filtered block connected notification:",
 193  				e,
 194  			)
 195  			return
 196  		}
 197  		c.ntfnHandlers.OnFilteredBlockConnected(
 198  			blockHeight,
 199  			blockHeader, transactions,
 200  		)
 201  	// OnBlockDisconnected
 202  	case btcjson.BlockDisconnectedNtfnMethod:
 203  		// Ignore the notification if the client is not interested in it.
 204  		if c.ntfnHandlers.OnBlockDisconnected == nil {
 205  			D.Ln("<<<no OnBlockDisconnected callback registered>>>")
 206  			return
 207  		}
 208  		blockHash, blockHeight, blockTime, e := parseChainNtfnParams(ntfn.Params)
 209  		if e != nil {
 210  			W.Ln("received invalid block connected notification:", e)
 211  			return
 212  		}
 213  		c.ntfnHandlers.OnBlockDisconnected(blockHash, blockHeight, blockTime)
 214  	// OnFilteredBlockDisconnected
 215  	case btcjson.FilteredBlockDisconnectedNtfnMethod:
 216  		// Ignore the notification if the client is not interested in it.
 217  		if c.ntfnHandlers.OnFilteredBlockDisconnected == nil {
 218  			D.Ln("<<<no OnFilteredBlockDisconnected callback registered>>>")
 219  			return
 220  		}
 221  		blockHeight, blockHeader, e := parseFilteredBlockDisconnectedParams(ntfn.Params)
 222  		if e != nil {
 223  			W.Ln(
 224  				"received invalid filtered block disconnected"+
 225  					" notification"+
 226  					":", e,
 227  			)
 228  			return
 229  		}
 230  		c.ntfnHandlers.OnFilteredBlockDisconnected(blockHeight, blockHeader)
 231  	// OnRecvTx
 232  	case btcjson.RecvTxNtfnMethod:
 233  		// Ignore the notification if the client is not interested in it.
 234  		if c.ntfnHandlers.OnRecvTx == nil {
 235  			D.Ln("<<<no OnRecvTx callback registered>>>")
 236  			return
 237  		}
 238  		tx, block, e := parseChainTxNtfnParams(ntfn.Params)
 239  		if e != nil {
 240  			W.Ln("received invalid recvtx notification:", e)
 241  			return
 242  		}
 243  		c.ntfnHandlers.OnRecvTx(tx, block)
 244  	// OnRedeemingTx
 245  	case btcjson.RedeemingTxNtfnMethod:
 246  		// Ignore the notification if the client is not interested in it.
 247  		if c.ntfnHandlers.OnRedeemingTx == nil {
 248  			D.Ln("<<<no OnRedeemingTx callback registered>>>")
 249  			return
 250  		}
 251  		tx, block, e := parseChainTxNtfnParams(ntfn.Params)
 252  		if e != nil {
 253  			W.Ln("received invalid redeemingtx notification:", e)
 254  			return
 255  		}
 256  		c.ntfnHandlers.OnRedeemingTx(tx, block)
 257  	// OnRelevantTxAccepted
 258  	case btcjson.RelevantTxAcceptedNtfnMethod:
 259  		// Ignore the notification if the client is not interested in it.
 260  		if c.ntfnHandlers.OnRelevantTxAccepted == nil {
 261  			D.Ln("<<<no OnRelevantTxAccepted callback registered>>>")
 262  			return
 263  		}
 264  		transaction, e := parseRelevantTxAcceptedParams(ntfn.Params)
 265  		if e != nil {
 266  			W.Ln("received invalid relevanttxaccepted notification:", e)
 267  			return
 268  		}
 269  		c.ntfnHandlers.OnRelevantTxAccepted(transaction)
 270  	// OnRescanFinished
 271  	case btcjson.RescanFinishedNtfnMethod:
 272  		// Ignore the notification if the client is not interested in it.
 273  		if c.ntfnHandlers.OnRescanFinished == nil {
 274  			D.Ln("<<<no OnRescanFinished callback registered>>>")
 275  			return
 276  		}
 277  		hash, height, blkTime, e := parseRescanProgressParams(ntfn.Params)
 278  		if e != nil {
 279  			W.Ln("received invalid rescanfinished notification:", e)
 280  			return
 281  		}
 282  		c.ntfnHandlers.OnRescanFinished(hash, height, blkTime)
 283  	// OnRescanProgress
 284  	case btcjson.RescanProgressNtfnMethod:
 285  		// Ignore the notification if the client is not interested in it.
 286  		if c.ntfnHandlers.OnRescanProgress == nil {
 287  			D.Ln("<<<no OnRescanProgress callback registered>>>")
 288  			return
 289  		}
 290  		hash, height, blkTime, e := parseRescanProgressParams(ntfn.Params)
 291  		if e != nil {
 292  			W.Ln("received invalid rescanprogress notification:", e)
 293  			return
 294  		}
 295  		c.ntfnHandlers.OnRescanProgress(hash, height, blkTime)
 296  	// OnTxAccepted
 297  	case btcjson.TxAcceptedNtfnMethod:
 298  		// Ignore the notification if the client is not interested in it.
 299  		if c.ntfnHandlers.OnTxAccepted == nil {
 300  			D.Ln("<<<no OnTxAccepted callback registered>>>")
 301  			return
 302  		}
 303  		hash, amt, e := parseTxAcceptedNtfnParams(ntfn.Params)
 304  		if e != nil {
 305  			W.Ln("received invalid tx accepted notification:", e)
 306  			return
 307  		}
 308  		c.ntfnHandlers.OnTxAccepted(hash, amt)
 309  	// OnTxAcceptedVerbose
 310  	case btcjson.TxAcceptedVerboseNtfnMethod:
 311  		// Ignore the notification if the client is not interested in it.
 312  		if c.ntfnHandlers.OnTxAcceptedVerbose == nil {
 313  			D.Ln("<<<no OnTxAcceptedVerbose callback registered>>>")
 314  			return
 315  		}
 316  		rawTx, e := parseTxAcceptedVerboseNtfnParams(ntfn.Params)
 317  		if e != nil {
 318  			W.Ln("received invalid tx accepted verbose notification:", e)
 319  			return
 320  		}
 321  		c.ntfnHandlers.OnTxAcceptedVerbose(rawTx)
 322  	// OnPodConnected
 323  	case btcjson.PodConnectedNtfnMethod:
 324  		// Ignore the notification if the client is not interested in it.
 325  		if c.ntfnHandlers.OnPodConnected == nil {
 326  			D.Ln("<<<no OnPodConnected callback registered>>>")
 327  			return
 328  		}
 329  		connected, e := parsePodConnectedNtfnParams(ntfn.Params)
 330  		if e != nil {
 331  			W.Ln("received invalid pod connected notification:", e)
 332  			return
 333  		}
 334  		c.ntfnHandlers.OnPodConnected(connected)
 335  	// OnAccountBalance
 336  	case btcjson.AccountBalanceNtfnMethod:
 337  		// Ignore the notification if the client is not interested in it.
 338  		if c.ntfnHandlers.OnAccountBalance == nil {
 339  			D.Ln("<<<no OnAccountBalance callback registered>>>")
 340  			return
 341  		}
 342  		account, bal, conf, e := parseAccountBalanceNtfnParams(ntfn.Params)
 343  		if e != nil {
 344  			W.Ln("received invalid account balance notification:", e)
 345  			return
 346  		}
 347  		c.ntfnHandlers.OnAccountBalance(account, bal, conf)
 348  	// OnWalletLockState
 349  	case btcjson.WalletLockStateNtfnMethod:
 350  		// Ignore the notification if the client is not interested in it.
 351  		if c.ntfnHandlers.OnWalletLockState == nil {
 352  			D.Ln("<<<no OnWalletLockState callback registered>>>")
 353  			return
 354  		}
 355  		// The account name is not notified, so the return value is discarded.
 356  		_, locked, e := parseWalletLockStateNtfnParams(ntfn.Params)
 357  		if e != nil {
 358  			W.Ln("received invalid wallet lock state notification:", e)
 359  			return
 360  		}
 361  		c.ntfnHandlers.OnWalletLockState(locked)
 362  	// OnUnknownNotification
 363  	default:
 364  		if c.ntfnHandlers.OnUnknownNotification == nil {
 365  			D.Ln("<<<no OnUnknownNotification callback registered>>>")
 366  			return
 367  		}
 368  		c.ntfnHandlers.OnUnknownNotification(ntfn.Method, ntfn.Params)
 369  	}
 370  }
 371  
 372  // wrongNumParams is an error type describing an unparseable JSON-RPC notification due to an incorrect number of
 373  // parameters for the expected notification type.
 374  //
 375  // The value is the number of parameters of the invalid notification.
 376  type wrongNumParams int
 377  
 378  // BTCJSONError satisfies the builtin error interface.
 379  func (e wrongNumParams) Error() string {
 380  	return fmt.Sprintf("wrong number of parameters (%d)", e)
 381  }
 382  
 383  // parseChainNtfnParams parses out the block hash and height from the parameters of blockconnected and blockdisconnected
 384  // notifications.
 385  func parseChainNtfnParams(params []js.RawMessage) (
 386  	*chainhash.Hash,
 387  	int32, time.Time, error,
 388  ) {
 389  	if len(params) != 3 {
 390  		return nil, 0, time.Time{}, wrongNumParams(len(params))
 391  	}
 392  	// Unmarshal first parameter as a string.
 393  	var blockHashStr string
 394  	e := js.Unmarshal(params[0], &blockHashStr)
 395  	if e != nil {
 396  		return nil, 0, time.Time{}, e
 397  	}
 398  	// Unmarshal second parameter as an integer.
 399  	var blockHeight int32
 400  	e = js.Unmarshal(params[1], &blockHeight)
 401  	if e != nil {
 402  		return nil, 0, time.Time{}, e
 403  	}
 404  	// Unmarshal third parameter as unix time.
 405  	var blockTimeUnix int64
 406  	e = js.Unmarshal(params[2], &blockTimeUnix)
 407  	if e != nil {
 408  		return nil, 0, time.Time{}, e
 409  	}
 410  	// Create hash from block hash string.
 411  	blockHash, e := chainhash.NewHashFromStr(blockHashStr)
 412  	if e != nil {
 413  		return nil, 0, time.Time{}, e
 414  	}
 415  	// Create time.Time from unix time.
 416  	blockTime := time.Unix(blockTimeUnix, 0)
 417  	return blockHash, blockHeight, blockTime, nil
 418  }
 419  
 420  // parseFilteredBlockConnectedParams parses out the parameters included in a filteredblockconnected notification.
 421  //
 422  // NOTE: This is a pod extension ported from github. com/decred/dcrrpcclient and requires a websocket connection.
 423  func parseFilteredBlockConnectedParams(params []js.RawMessage) (
 424  	int32,
 425  	*wire.BlockHeader, []*util.Tx, error,
 426  ) {
 427  	if len(params) < 3 {
 428  		return 0, nil, nil, wrongNumParams(len(params))
 429  	}
 430  	// Unmarshal first parameter as an integer.
 431  	var blockHeight int32
 432  	e := js.Unmarshal(params[0], &blockHeight)
 433  	if e != nil {
 434  		return 0, nil, nil, e
 435  	}
 436  	// Unmarshal second parameter as a slice of bytes.
 437  	blockHeaderBytes, e := parseHexParam(params[1])
 438  	if e != nil {
 439  		return 0, nil, nil, e
 440  	}
 441  	// Deserialize block header from slice of bytes.
 442  	var blockHeader wire.BlockHeader
 443  	e = blockHeader.Deserialize(bytes.NewReader(blockHeaderBytes))
 444  	if e != nil {
 445  		return 0, nil, nil, e
 446  	}
 447  	// Unmarshal third parameter as a slice of hex-encoded strings.
 448  	var hexTransactions []string
 449  	e = js.Unmarshal(params[2], &hexTransactions)
 450  	if e != nil {
 451  		return 0, nil, nil, e
 452  	}
 453  	// Create slice of transactions from slice of strings by hex-decoding.
 454  	transactions := make([]*util.Tx, len(hexTransactions))
 455  	for i, hexTx := range hexTransactions {
 456  		transaction, e := hex.DecodeString(hexTx)
 457  		if e != nil {
 458  			return 0, nil, nil, e
 459  		}
 460  		transactions[i], e = util.NewTxFromBytes(transaction)
 461  		if e != nil {
 462  			return 0, nil, nil, e
 463  		}
 464  	}
 465  	return blockHeight, &blockHeader, transactions, nil
 466  }
 467  
 468  // parseFilteredBlockDisconnectedParams parses out the parameters included in a filteredblockdisconnected notification.
 469  //
 470  // NOTE: This is a pod extension ported from github. com/decred/dcrrpcclient and requires a websocket connection.
 471  func parseFilteredBlockDisconnectedParams(params []js.RawMessage) (
 472  	int32,
 473  	*wire.BlockHeader, error,
 474  ) {
 475  	if len(params) < 2 {
 476  		return 0, nil, wrongNumParams(len(params))
 477  	}
 478  	// Unmarshal first parameter as an integer.
 479  	var blockHeight int32
 480  	e := js.Unmarshal(params[0], &blockHeight)
 481  	if e != nil {
 482  		return 0, nil, e
 483  	}
 484  	// Unmarshal second parameter as a slice of bytes.
 485  	blockHeaderBytes, e := parseHexParam(params[1])
 486  	if e != nil {
 487  		return 0, nil, e
 488  	}
 489  	// Deserialize block header from slice of bytes.
 490  	var blockHeader wire.BlockHeader
 491  	e = blockHeader.Deserialize(bytes.NewReader(blockHeaderBytes))
 492  	if e != nil {
 493  		return 0, nil, e
 494  	}
 495  	return blockHeight, &blockHeader, nil
 496  }
 497  
 498  func parseHexParam(param js.RawMessage) ([]byte, error) {
 499  	var s string
 500  	e := js.Unmarshal(param, &s)
 501  	if e != nil {
 502  		return nil, e
 503  	}
 504  	return hex.DecodeString(s)
 505  }
 506  
 507  // parseRelevantTxAcceptedParams parses out the parameter included in a relevanttxaccepted notification.
 508  func parseRelevantTxAcceptedParams(params []js.RawMessage) (transaction []byte, e error) {
 509  	if len(params) < 1 {
 510  		return nil, wrongNumParams(len(params))
 511  	}
 512  	return parseHexParam(params[0])
 513  }
 514  
 515  // parseChainTxNtfnParams parses out the transaction and optional details about the block it's mined in from the
 516  // parameters of recvtx and redeemingtx notifications.
 517  func parseChainTxNtfnParams(params []js.RawMessage) (
 518  	*util.Tx,
 519  	*btcjson.BlockDetails, error,
 520  ) {
 521  	if len(params) == 0 || len(params) > 2 {
 522  		return nil, nil, wrongNumParams(len(params))
 523  	}
 524  	// Unmarshal first parameter as a string.
 525  	var txHex string
 526  	e := js.Unmarshal(params[0], &txHex)
 527  	if e != nil {
 528  		return nil, nil, e
 529  	}
 530  	// If present, unmarshal second optional parameter as the block details JSON object.
 531  	var block *btcjson.BlockDetails
 532  	if len(params) > 1 {
 533  		e = js.Unmarshal(params[1], &block)
 534  		if e != nil {
 535  			return nil, nil, e
 536  		}
 537  	}
 538  	// Hex decode and deserialize the transaction.
 539  	serializedTx, e := hex.DecodeString(txHex)
 540  	if e != nil {
 541  		return nil, nil, e
 542  	}
 543  	var msgTx wire.MsgTx
 544  	e = msgTx.Deserialize(bytes.NewReader(serializedTx))
 545  	if e != nil {
 546  		return nil, nil, e
 547  	}
 548  	// TODO: Change recvtx and redeemingtx callback signatures to use nicer
 549  	//  types for details about the block (block hash as a chainhash.Hash,
 550  	//  block time as a time.Time, etc.).
 551  	return util.NewTx(&msgTx), block, nil
 552  }
 553  
 554  // parseRescanProgressParams parses out the height of the last rescanned block from the parameters of rescanfinished and
 555  // rescanprogress notifications.
 556  func parseRescanProgressParams(params []js.RawMessage) (*chainhash.Hash, int32, time.Time, error) {
 557  	if len(params) != 3 {
 558  		return nil, 0, time.Time{}, wrongNumParams(len(params))
 559  	}
 560  	// Unmarshal first parameter as an string.
 561  	var hashStr string
 562  	e := js.Unmarshal(params[0], &hashStr)
 563  	if e != nil {
 564  		return nil, 0, time.Time{}, e
 565  	}
 566  	// Unmarshal second parameter as an integer.
 567  	var height int32
 568  	e = js.Unmarshal(params[1], &height)
 569  	if e != nil {
 570  		return nil, 0, time.Time{}, e
 571  	}
 572  	// Unmarshal third parameter as an integer.
 573  	var blkTime int64
 574  	e = js.Unmarshal(params[2], &blkTime)
 575  	if e != nil {
 576  		return nil, 0, time.Time{}, e
 577  	}
 578  	// Decode string encoding of block hash.
 579  	hash, e := chainhash.NewHashFromStr(hashStr)
 580  	if e != nil {
 581  		return nil, 0, time.Time{}, e
 582  	}
 583  	return hash, height, time.Unix(blkTime, 0), nil
 584  }
 585  
 586  // parseTxAcceptedNtfnParams parses out the transaction hash and total amount from the parameters of a txaccepted
 587  // notification.
 588  func parseTxAcceptedNtfnParams(params []js.RawMessage) (
 589  	*chainhash.Hash,
 590  	amt.Amount, error,
 591  ) {
 592  	if len(params) != 2 {
 593  		return nil, 0, wrongNumParams(len(params))
 594  	}
 595  	// Unmarshal first parameter as a string.
 596  	var txHashStr string
 597  	e := js.Unmarshal(params[0], &txHashStr)
 598  	if e != nil {
 599  		return nil, 0, e
 600  	}
 601  	// Unmarshal second parameter as a floating point number.
 602  	var fAmt float64
 603  	e = js.Unmarshal(params[1], &fAmt)
 604  	if e != nil {
 605  		return nil, 0, e
 606  	}
 607  	// Bounds check amount.
 608  	amt, e := amt.NewAmount(fAmt)
 609  	if e != nil {
 610  		return nil, 0, e
 611  	}
 612  	// Decode string encoding of transaction sha.
 613  	txHash, e := chainhash.NewHashFromStr(txHashStr)
 614  	if e != nil {
 615  		return nil, 0, e
 616  	}
 617  	return txHash, amt, nil
 618  }
 619  
 620  // parseTxAcceptedVerboseNtfnParams parses out details about a raw transaction from the parameters of a
 621  // txacceptedverbose notification.
 622  func parseTxAcceptedVerboseNtfnParams(params []js.RawMessage) (
 623  	*btcjson.TxRawResult,
 624  	error,
 625  ) {
 626  	if len(params) != 1 {
 627  		return nil, wrongNumParams(len(params))
 628  	}
 629  	// Unmarshal first parameter as a raw transaction result object.
 630  	var rawTx btcjson.TxRawResult
 631  	e := js.Unmarshal(params[0], &rawTx)
 632  	if e != nil {
 633  		return nil, e
 634  	}
 635  	// TODO: change txacceptedverbose notification callbacks to use nicer
 636  	//  types for all details about the transaction (i.e.
 637  	//  decoding hashes from their string encoding).
 638  	return &rawTx, nil
 639  }
 640  
 641  // parsePodConnectedNtfnParams parses out the connection status of pod and btcwallet from the parameters of a
 642  // podconnected notification.
 643  func parsePodConnectedNtfnParams(params []js.RawMessage) (bool, error) {
 644  	if len(params) != 1 {
 645  		return false, wrongNumParams(len(params))
 646  	}
 647  	// Unmarshal first parameter as a boolean.
 648  	var connected bool
 649  	e := js.Unmarshal(params[0], &connected)
 650  	if e != nil {
 651  		return false, e
 652  	}
 653  	return connected, nil
 654  }
 655  
 656  // parseAccountBalanceNtfnParams parses out the account name, total balance, and whether or not the balance is confirmed
 657  // or unconfirmed from the parameters of an accountbalance notification.
 658  func parseAccountBalanceNtfnParams(params []js.RawMessage) (
 659  	account string,
 660  	balance amt.Amount, confirmed bool, e error,
 661  ) {
 662  	if len(params) != 3 {
 663  		return "", 0, false, wrongNumParams(len(params))
 664  	}
 665  	// Unmarshal first parameter as a string.
 666  	e = js.Unmarshal(params[0], &account)
 667  	if e != nil {
 668  		return "", 0, false, e
 669  	}
 670  	// Unmarshal second parameter as a floating point number.
 671  	var fBal float64
 672  	e = js.Unmarshal(params[1], &fBal)
 673  	if e != nil {
 674  		return "", 0, false, e
 675  	}
 676  	// Unmarshal third parameter as a boolean.
 677  	e = js.Unmarshal(params[2], &confirmed)
 678  	if e != nil {
 679  		return "", 0, false, e
 680  	}
 681  	// Bounds check amount.
 682  	bal, e := amt.NewAmount(fBal)
 683  	if e != nil {
 684  		return "", 0, false, e
 685  	}
 686  	return account, bal, confirmed, nil
 687  }
 688  
 689  // parseWalletLockStateNtfnParams parses out the account name and locked state of an account from the parameters of a
 690  // walletlockstate notification.
 691  func parseWalletLockStateNtfnParams(params []js.RawMessage) (
 692  	account string,
 693  	locked bool, e error,
 694  ) {
 695  	if len(params) != 2 {
 696  		return "", false, wrongNumParams(len(params))
 697  	}
 698  	// Unmarshal first parameter as a string.
 699  	e = js.Unmarshal(params[0], &account)
 700  	if e != nil {
 701  		return "", false, e
 702  	}
 703  	// Unmarshal second parameter as a boolean.
 704  	e = js.Unmarshal(params[1], &locked)
 705  	if e != nil {
 706  		return "", false, e
 707  	}
 708  	return account, locked, nil
 709  }
 710  
 711  // FutureNotifyBlocksResult is a future promise to deliver the result of a NotifyBlocksAsync RPC invocation (or an
 712  // applicable error).
 713  type FutureNotifyBlocksResult chan *response
 714  
 715  // Receive waits for the response promised by the future and returns an
 716  // error if the registration was not successful.
 717  func (r FutureNotifyBlocksResult) Receive() (e error) {
 718  	_, e = receiveFuture(r)
 719  	return e
 720  }
 721  
 722  // NotifyBlocksAsync returns an instance of a type that can be used to get the result of the RPC at some future time by
 723  // invoking the Receive function on the returned instance.
 724  //
 725  // See NotifyBlocks for the blocking version and more details.
 726  //
 727  // NOTE: This is a pod extension and requires a websocket connection.
 728  func (c *Client) NotifyBlocksAsync() FutureNotifyBlocksResult {
 729  	// Not supported in HTTP POST mode.
 730  	if c.config.HTTPPostMode {
 731  		return newFutureError(ErrWebsocketsRequired)
 732  	}
 733  	// Ignore the notification if the client is not interested in notifications.
 734  	if c.ntfnHandlers == nil {
 735  		return newNilFutureResult()
 736  	}
 737  	cmd := btcjson.NewNotifyBlocksCmd()
 738  	return c.sendCmd(cmd)
 739  }
 740  
 741  // NotifyBlocks registers the client to receive notifications when blocks are connected and disconnected from the main
 742  // chain.
 743  //
 744  // The notifications are delivered to the notification handlers associated with the client. Calling this function has no
 745  // effect if there are no notification handlers and will result in an error if the client is configured to run in HTTP
 746  // POST mode.
 747  //
 748  // The notifications delivered as a result of this call will be via one of or OnBlockDisconnected. NOTE: This is a pod
 749  // extension and requires a websocket connection.
 750  func (c *Client) NotifyBlocks() (e error) {
 751  	return c.NotifyBlocksAsync().Receive()
 752  }
 753  
 754  // FutureNotifySpentResult is a future promise to deliver the result of a NotifySpentAsync RPC invocation (or an
 755  // applicable error).
 756  //
 757  // NOTE: Deprecated. Use FutureLoadTxFilterResult instead.
 758  type FutureNotifySpentResult chan *response
 759  
 760  // Receive waits for the response promised by the future and returns an
 761  // error if the registration was not successful.
 762  func (r FutureNotifySpentResult) Receive() (e error) {
 763  	_, e = receiveFuture(r)
 764  	return e
 765  }
 766  
 767  // notifySpentInternal is the same as notifySpentAsync except it accepts the converted outpoints as a parameter so the
 768  // client can more efficiently recreate the previous notification state on reconnect.
 769  func (c *Client) notifySpentInternal(outpoints []btcjson.OutPoint) FutureNotifySpentResult {
 770  	// Not supported in HTTP POST mode.
 771  	if c.config.HTTPPostMode {
 772  		return newFutureError(ErrWebsocketsRequired)
 773  	}
 774  	// Ignore the notification if the client is not interested in notifications.
 775  	if c.ntfnHandlers == nil {
 776  		return newNilFutureResult()
 777  	}
 778  	cmd := btcjson.NewNotifySpentCmd(outpoints)
 779  	return c.sendCmd(cmd)
 780  }
 781  
 782  // newOutPointFromWire constructs the json representation of a transaction outpoint from the wire type.
 783  func newOutPointFromWire(op *wire.OutPoint) btcjson.OutPoint {
 784  	return btcjson.OutPoint{
 785  		Hash:  op.Hash.String(),
 786  		Index: op.Index,
 787  	}
 788  }
 789  
 790  // NotifySpentAsync returns an instance of a type that can be used to get the result of the RPC at some future time by
 791  // invoking the Receive function on the returned instance.
 792  //
 793  // See NotifySpent for the blocking version and more details.
 794  //
 795  // NOTE: This is a pod extension and requires a websocket connection.
 796  //
 797  // NOTE: Deprecated. Use LoadTxFilterAsync instead.
 798  func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentResult {
 799  	// Not supported in HTTP POST mode.
 800  	if c.config.HTTPPostMode {
 801  		return newFutureError(ErrWebsocketsRequired)
 802  	}
 803  	// Ignore the notification if the client is not interested in notifications.
 804  	if c.ntfnHandlers == nil {
 805  		return newNilFutureResult()
 806  	}
 807  	ops := make([]btcjson.OutPoint, 0, len(outpoints))
 808  	for _, outpoint := range outpoints {
 809  		ops = append(ops, newOutPointFromWire(outpoint))
 810  	}
 811  	cmd := btcjson.NewNotifySpentCmd(ops)
 812  	return c.sendCmd(cmd)
 813  }
 814  
 815  // NotifySpent registers the client to receive notifications when the passed transaction outputs are spent.
 816  //
 817  // The notifications are delivered to the notification handlers associated with the client. Calling this function has no
 818  // effect if there are no notification handlers and will result in an error if the client is configured to run in HTTP
 819  // POST mode.
 820  //
 821  // The notifications delivered as a result of this call will be via OnRedeemingTx.
 822  //
 823  // NOTE: This is a pod extension and requires a websocket connection.
 824  //
 825  // NOTE: Deprecated. Use LoadTxFilter instead.
 826  func (c *Client) NotifySpent(outpoints []*wire.OutPoint) (e error) {
 827  	return c.NotifySpentAsync(outpoints).Receive()
 828  }
 829  
 830  // FutureNotifyNewTransactionsResult is a future promise to deliver the result of a NotifyNewTransactionsAsync RPC
 831  // invocation (or an applicable error).
 832  type FutureNotifyNewTransactionsResult chan *response
 833  
 834  // Receive waits for the response promised by the future and returns an error if the registration was not successful.
 835  func (r FutureNotifyNewTransactionsResult) Receive() (e error) {
 836  	_, e = receiveFuture(r)
 837  	return e
 838  }
 839  
 840  // NotifyNewTransactionsAsync returns an instance of a type that can be used to get the result of the RPC at some future
 841  // time by invoking the Receive function on the returned instance.
 842  //
 843  // See NotifyNewTransactionsAsync for the blocking version and more details.
 844  //
 845  // NOTE: This is a pod extension and requires a websocket connection.
 846  func (c *Client) NotifyNewTransactionsAsync(verbose bool) FutureNotifyNewTransactionsResult {
 847  	// Not supported in HTTP POST mode.
 848  	if c.config.HTTPPostMode {
 849  		return newFutureError(ErrWebsocketsRequired)
 850  	}
 851  	// Ignore the notification if the client is not interested in notifications.
 852  	if c.ntfnHandlers == nil {
 853  		return newNilFutureResult()
 854  	}
 855  	cmd := btcjson.NewNotifyNewTransactionsCmd(&verbose)
 856  	return c.sendCmd(cmd)
 857  }
 858  
 859  // NotifyNewTransactions registers the client to receive notifications every time a new transaction is accepted to the
 860  // memory pool.
 861  //
 862  // The notifications are delivered to the notification handlers associated with the client. Calling this function has no
 863  // effect if there are no notification handlers and will result in an error if the client is configured to run in HTTP
 864  // POST mode.
 865  //
 866  // The notifications delivered as a result of this call will be via one of OnTxAccepted (when verbose is false) or
 867  // OnTxAcceptedVerbose ( when verbose is true). NOTE: This is a pod extension and requires a websocket connection.
 868  func (c *Client) NotifyNewTransactions(verbose bool) (e error) {
 869  	return c.NotifyNewTransactionsAsync(verbose).Receive()
 870  }
 871  
 872  // FutureNotifyReceivedResult is a future promise to deliver the result of a NotifyReceivedAsync RPC invocation (or an
 873  // applicable error).
 874  //
 875  // NOTE: Deprecated. Use FutureLoadTxFilterResult instead.
 876  type FutureNotifyReceivedResult chan *response
 877  
 878  // Receive waits for the response promised by the future and returns an error if the registration was not successful.
 879  func (r FutureNotifyReceivedResult) Receive() (e error) {
 880  	_, e = receiveFuture(r)
 881  	return e
 882  }
 883  
 884  // notifyReceivedInternal is the same as notifyReceivedAsync except it accepts the converted addresses as a parameter so
 885  // the client can more efficiently recreate the previous notification state on reconnect.
 886  func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceivedResult {
 887  	// Not supported in HTTP POST mode.
 888  	if c.config.HTTPPostMode {
 889  		return newFutureError(ErrWebsocketsRequired)
 890  	}
 891  	// Ignore the notification if the client is not interested in notifications.
 892  	if c.ntfnHandlers == nil {
 893  		return newNilFutureResult()
 894  	}
 895  	// Convert addresses to strings.
 896  	cmd := btcjson.NewNotifyReceivedCmd(addresses)
 897  	return c.sendCmd(cmd)
 898  }
 899  
 900  // NotifyReceivedAsync returns an instance of a type that can be used to get the result of the RPC at some future time
 901  // by invoking the Receive function on the returned instance.
 902  //
 903  // See NotifyReceived for the blocking version and more details.
 904  //
 905  // NOTE: This is a pod extension and requires a websocket connection.
 906  //
 907  // NOTE: Deprecated. Use LoadTxFilterAsync instead.
 908  func (c *Client) NotifyReceivedAsync(addresses []btcaddr.Address) FutureNotifyReceivedResult {
 909  	// Not supported in HTTP POST mode.
 910  	if c.config.HTTPPostMode {
 911  		return newFutureError(ErrWebsocketsRequired)
 912  	}
 913  	// Ignore the notification if the client is not interested in notifications.
 914  	if c.ntfnHandlers == nil {
 915  		return newNilFutureResult()
 916  	}
 917  	// Convert addresses to strings.
 918  	addrs := make([]string, 0, len(addresses))
 919  	for _, addr := range addresses {
 920  		addrs = append(addrs, addr.String())
 921  	}
 922  	cmd := btcjson.NewNotifyReceivedCmd(addrs)
 923  	return c.sendCmd(cmd)
 924  }
 925  
 926  // NotifyReceived registers the client to receive notifications every time a new transaction which pays to one of the
 927  // passed addresses is accepted to memory pool or in a block connected to the block chain.
 928  //
 929  // In addition, when one of these transactions is detected, the client is also automatically registered for
 930  // notifications when the new transaction outpoints the address now has available are spent (
 931  //
 932  // See NotifySpent). The notifications are delivered to the notification handlers associated with the client.
 933  //
 934  // Calling this function has no effect if there are no notification handlers and will result in an error if the client
 935  // is configured to run in HTTP POST mode.
 936  //
 937  // The notifications delivered as a result of this call will be via one of *OnRecvTx (for transactions that receive
 938  // funds to one of the passed addresses) or OnRedeemingTx ( for transactions which spend from one of the outpoints which
 939  // are automatically registered upon receipt of funds to the address).
 940  //
 941  // NOTE: This is a pod extension and requires a websocket connection.
 942  //
 943  // NOTE: Deprecated. Use LoadTxFilter instead.
 944  func (c *Client) NotifyReceived(addresses []btcaddr.Address) (e error) {
 945  	return c.NotifyReceivedAsync(addresses).Receive()
 946  }
 947  
 948  // FutureRescanResult is a future promise to deliver the result of a RescanAsync or RescanEndHeightAsync RPC invocation
 949  // ( or an applicable error).
 950  //
 951  // NOTE: Deprecated. Use FutureRescanBlocksResult instead.
 952  type FutureRescanResult chan *response
 953  
 954  // Receive waits for the response promised by the future and returns an error if the rescan was not successful.
 955  func (r FutureRescanResult) Receive() (e error) {
 956  	_, e = receiveFuture(r)
 957  	return e
 958  }
 959  
 960  // RescanAsync returns an instance of a type that can be used to get the result of the RPC at some future time by
 961  // invoking the Receive function on the returned instance.
 962  //
 963  // See Rescan for the blocking version and more details.
 964  //
 965  // NOTE: Rescan requests are not issued on client reconnect and must be performed manually (ideally with a new start
 966  // height based on the last rescan progress notification).
 967  //
 968  // See the OnClientConnected notification callback for a good call site to reissue rescan requests on connect and
 969  // reconnect.
 970  //
 971  // NOTE: This is a pod extension and requires a websocket connection.
 972  //
 973  // NOTE: Deprecated. Use RescanBlocksAsync instead.
 974  func (c *Client) RescanAsync(
 975  	startBlock *chainhash.Hash,
 976  	addresses []btcaddr.Address,
 977  	outpoints []*wire.OutPoint,
 978  ) FutureRescanResult {
 979  	// Not supported in HTTP POST mode.
 980  	if c.config.HTTPPostMode {
 981  		return newFutureError(ErrWebsocketsRequired)
 982  	}
 983  	// Ignore the notification if the client is not interested in notifications.
 984  	if c.ntfnHandlers == nil {
 985  		return newNilFutureResult()
 986  	}
 987  	// Convert block hashes to strings.
 988  	var startBlockHashStr string
 989  	if startBlock != nil {
 990  		startBlockHashStr = startBlock.String()
 991  	}
 992  	// Convert addresses to strings.
 993  	addrs := make([]string, 0, len(addresses))
 994  	for _, addr := range addresses {
 995  		addrs = append(addrs, addr.String())
 996  	}
 997  	// Convert outpoints.
 998  	ops := make([]btcjson.OutPoint, 0, len(outpoints))
 999  	for _, op := range outpoints {
1000  		ops = append(ops, newOutPointFromWire(op))
1001  	}
1002  	cmd := btcjson.NewRescanCmd(startBlockHashStr, addrs, ops, nil)
1003  	return c.sendCmd(cmd)
1004  }
1005  
1006  // Rescan rescans the block chain starting from the provided starting block to the end of the longest chain for
1007  // transactions that pay to the passed addresses and transactions which spend the passed outpoints.
1008  //
1009  // The notifications of found transactions are delivered to the notification handlers associated with client and this
1010  // call will not return until the rescan has completed. Calling this function has no effect if there are no notification
1011  // handlers and will result in an error if the client is configured to run in HTTP POST mode.
1012  //
1013  // The notifications delivered as a result of this call will be via one of OnRedeemingTx (for transactions which spend
1014  // from the one of the passed outpoints), OnRecvTx (for transactions that receive funds to one of the passed addresses),
1015  // and OnRescanProgress (for rescan progress updates).
1016  //
1017  // See RescanEndBlock to also specify an ending block to finish the rescan without continuing through the best block on
1018  // the main chain.
1019  //
1020  // NOTE: Rescan requests are not issued on client reconnect and must be performed manually (ideally with a new start
1021  // height based on the last rescan progress notification).
1022  //
1023  // See the OnClientConnected notification callback for a good call site to reissue rescan requests on connect and
1024  // reconnect.
1025  //
1026  // NOTE: This is a pod extension and requires a websocket connection.
1027  //
1028  // NOTE: Deprecated. Use RescanBlocks instead.
1029  func (c *Client) Rescan(
1030  	startBlock *chainhash.Hash,
1031  	addresses []btcaddr.Address,
1032  	outpoints []*wire.OutPoint,
1033  ) (e error) {
1034  	return c.RescanAsync(startBlock, addresses, outpoints).Receive()
1035  }
1036  
1037  // RescanEndBlockAsync returns an instance of a type that can be used to get the result of the RPC at some future time
1038  // by invoking the Receive function on the returned instance.
1039  //
1040  // See RescanEndBlock for the blocking version and more details.
1041  //
1042  // NOTE: This is a pod extension and requires a websocket connection.
1043  //
1044  // NOTE: Deprecated. Use RescanBlocksAsync instead.
1045  func (c *Client) RescanEndBlockAsync(
1046  	startBlock *chainhash.Hash,
1047  	addresses []btcaddr.Address, outpoints []*wire.OutPoint,
1048  	endBlock *chainhash.Hash,
1049  ) FutureRescanResult {
1050  	// Not supported in HTTP POST mode.
1051  	if c.config.HTTPPostMode {
1052  		return newFutureError(ErrWebsocketsRequired)
1053  	}
1054  	// Ignore the notification if the client is not interested in notifications.
1055  	if c.ntfnHandlers == nil {
1056  		return newNilFutureResult()
1057  	}
1058  	// Convert block hashes to strings.
1059  	var startBlockHashStr, endBlockHashStr string
1060  	if startBlock != nil {
1061  		startBlockHashStr = startBlock.String()
1062  	}
1063  	if endBlock != nil {
1064  		endBlockHashStr = endBlock.String()
1065  	}
1066  	// Convert addresses to strings.
1067  	addrs := make([]string, 0, len(addresses))
1068  	for _, addr := range addresses {
1069  		addrs = append(addrs, addr.String())
1070  	}
1071  	// Convert outpoints.
1072  	ops := make([]btcjson.OutPoint, 0, len(outpoints))
1073  	for _, op := range outpoints {
1074  		ops = append(ops, newOutPointFromWire(op))
1075  	}
1076  	cmd := btcjson.NewRescanCmd(
1077  		startBlockHashStr, addrs, ops,
1078  		&endBlockHashStr,
1079  	)
1080  	return c.sendCmd(cmd)
1081  }
1082  
1083  // RescanEndHeight rescans the block chain starting from the provided starting block up to the provided ending block for
1084  // transactions that pay to the passed addresses and transactions which spend the passed outpoints.
1085  //
1086  // The notifications of found transactions are delivered to the notification handlers associated with client and this
1087  // call will not return until the rescan has completed.
1088  //
1089  // Calling this function has no effect if there are no notification handlers and will result in an error if the client
1090  // is configured to run in HTTP POST mode.
1091  //
1092  // The notifications delivered as a result of this call will be via one of OnRedeemingTx (for transactions which spend
1093  // from the one of the passed outpoints), OnRecvTx (for transactions that receive funds to one of the passed addresses),
1094  // and OnRescanProgress (for rescan progress updates).
1095  //
1096  // See Rescan to also perform a rescan through current end of the longest
1097  // chain. NOTE: This is a pod extension and requires a websocket connection.
1098  //
1099  // NOTE: Deprecated. Use RescanBlocks instead.
1100  func (c *Client) RescanEndHeight(
1101  	startBlock *chainhash.Hash,
1102  	addresses []btcaddr.Address, outpoints []*wire.OutPoint,
1103  	endBlock *chainhash.Hash,
1104  ) (e error) {
1105  	return c.RescanEndBlockAsync(
1106  		startBlock, addresses, outpoints,
1107  		endBlock,
1108  	).Receive()
1109  }
1110  
1111  // FutureLoadTxFilterResult is a future promise to deliver the result of a LoadTxFilterAsync RPC invocation (or an
1112  // applicable error).
1113  //
1114  // NOTE: This is a pod extension ported from github.com/decred/dcrrpcclient and requires a websocket connection.
1115  type FutureLoadTxFilterResult chan *response
1116  
1117  // Receive waits for the response promised by the future and returns an error if the registration was not successful.
1118  //
1119  // NOTE: This is a pod extension ported from github.com/decred/dcrrpcclient and requires a websocket connection.
1120  func (r FutureLoadTxFilterResult) Receive() (e error) {
1121  	_, e = receiveFuture(r)
1122  	return e
1123  }
1124  
1125  // LoadTxFilterAsync returns an instance of a type that can be used to get the result of the RPC at some future time by
1126  // invoking the Receive function on the returned instance.
1127  //
1128  // See LoadTxFilter for the blocking version and more details.
1129  //
1130  // NOTE: This is a pod extension ported from github. com/decred/dcrrpcclient and requires a websocket connection.
1131  func (c *Client) LoadTxFilterAsync(
1132  	reload bool, addresses []btcaddr.Address,
1133  	outPoints []wire.OutPoint,
1134  ) FutureLoadTxFilterResult {
1135  	addrStrs := make([]string, len(addresses))
1136  	for i, a := range addresses {
1137  		addrStrs[i] = a.EncodeAddress()
1138  	}
1139  	outPointObjects := make([]btcjson.OutPoint, len(outPoints))
1140  	for i := range outPoints {
1141  		outPointObjects[i] = btcjson.OutPoint{
1142  			Hash:  outPoints[i].Hash.String(),
1143  			Index: outPoints[i].Index,
1144  		}
1145  	}
1146  	cmd := btcjson.NewLoadTxFilterCmd(reload, addrStrs, outPointObjects)
1147  	return c.sendCmd(cmd)
1148  }
1149  
1150  // LoadTxFilter loads reloads or adds data to a websocket client's transaction filter.
1151  //
1152  // The filter is consistently updated based on inspected transactions during mempool acceptance, block acceptance, and
1153  // for all rescanned blocks.
1154  //
1155  // NOTE: This is a pod extension ported from github. com/decred/dcrrpcclient and requires a websocket connection.
1156  func (c *Client) LoadTxFilter(reload bool, addresses []btcaddr.Address, outPoints []wire.OutPoint) (e error) {
1157  	return c.LoadTxFilterAsync(reload, addresses, outPoints).Receive()
1158  }
1159