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