github.com/decred/dcrlnd@v0.7.6/chainntnfs/txnotifier.go (about)

     1  package chainntnfs
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"sync"
     8  	"sync/atomic"
     9  
    10  	"github.com/decred/dcrd/chaincfg/chainhash"
    11  	"github.com/decred/dcrd/chaincfg/v3"
    12  	"github.com/decred/dcrd/dcrutil/v4"
    13  	"github.com/decred/dcrd/wire"
    14  	"github.com/decred/dcrlnd/chainscan"
    15  	"github.com/decred/dcrlnd/channeldb"
    16  )
    17  
    18  const (
    19  	// ReorgSafetyLimit is the chain depth beyond which it is assumed a
    20  	// block will not be reorganized out of the chain. This is used to
    21  	// determine when to prune old confirmation requests so that reorgs are
    22  	// handled correctly. The average number of blocks in a day is a
    23  	// reasonable value to use.
    24  	ReorgSafetyLimit = 256
    25  
    26  	// MaxNumConfs is the maximum number of confirmations that can be
    27  	// requested on a transaction.
    28  	MaxNumConfs = ReorgSafetyLimit
    29  )
    30  
    31  var (
    32  	// ZeroHash is the value that should be used as the txid when
    33  	// registering for the confirmation of a script on-chain. This allows
    34  	// the notifier to match _and_ dispatch upon the inclusion of the script
    35  	// on-chain, rather than the txid.
    36  	ZeroHash chainhash.Hash
    37  
    38  	// ZeroOutPoint is the value that should be used as the outpoint when
    39  	// registering for the spend of a script on-chain. This allows the
    40  	// notifier to match _and_ dispatch upon detecting the spend of the
    41  	// script on-chain, rather than the outpoint.
    42  	ZeroOutPoint wire.OutPoint
    43  )
    44  
    45  var (
    46  	// ErrTxNotifierExiting is an error returned when attempting to interact
    47  	// with the TxNotifier but it been shut down.
    48  	ErrTxNotifierExiting = errors.New("TxNotifier is exiting")
    49  
    50  	// ErrNoScript is an error returned when a confirmation/spend
    51  	// registration is attempted without providing an accompanying output
    52  	// script.
    53  	ErrNoScript = errors.New("an output script must be provided")
    54  
    55  	// ErrNoHeightHint is an error returned when a confirmation/spend
    56  	// registration is attempted without providing an accompanying height
    57  	// hint.
    58  	ErrNoHeightHint = errors.New("a height hint greater than 0 must be " +
    59  		"provided")
    60  
    61  	// ErrNumConfsOutOfRange is an error returned when a confirmation/spend
    62  	// registration is attempted and the number of confirmations provided is
    63  	// out of range.
    64  	ErrNumConfsOutOfRange = fmt.Errorf("number of confirmations must be "+
    65  		"between %d and %d", 1, MaxNumConfs)
    66  )
    67  
    68  // rescanState indicates the progression of a registration before the notifier
    69  // can begin dispatching confirmations at tip.
    70  type rescanState byte
    71  
    72  const (
    73  	// rescanNotStarted is the initial state, denoting that a historical
    74  	// dispatch may be required.
    75  	rescanNotStarted rescanState = iota
    76  
    77  	// rescanPending indicates that a dispatch has already been made, and we
    78  	// are waiting for its completion. No other rescans should be dispatched
    79  	// while in this state.
    80  	rescanPending
    81  
    82  	// rescanComplete signals either that a rescan was dispatched and has
    83  	// completed, or that we began watching at tip immediately. In either
    84  	// case, the notifier can only dispatch notifications from tip when in
    85  	// this state.
    86  	rescanComplete
    87  )
    88  
    89  // confNtfnSet holds all known, registered confirmation notifications for a
    90  // txid/output script. If duplicates notifications are requested, only one
    91  // historical dispatch will be spawned to ensure redundant scans are not
    92  // permitted. A single conf detail will be constructed and dispatched to all
    93  // interested clients.
    94  type confNtfnSet struct {
    95  	// ntfns keeps tracks of all the active client notification requests for
    96  	// a transaction/output script
    97  	ntfns map[uint64]*ConfNtfn
    98  
    99  	// rescanStatus represents the current rescan state for the
   100  	// transaction/output script.
   101  	rescanStatus rescanState
   102  
   103  	// details serves as a cache of the confirmation details of a
   104  	// transaction that we'll use to determine if a transaction/output
   105  	// script has already confirmed at the time of registration.
   106  	// details is also used to make sure that in case of an address reuse
   107  	// (funds sent to a previously confirmed script) no additional
   108  	// notification is registered which would lead to an inconsistent state.
   109  	details *TxConfirmation
   110  }
   111  
   112  // newConfNtfnSet constructs a fresh confNtfnSet for a group of clients
   113  // interested in a notification for a particular txid.
   114  func newConfNtfnSet() *confNtfnSet {
   115  	return &confNtfnSet{
   116  		ntfns:        make(map[uint64]*ConfNtfn),
   117  		rescanStatus: rescanNotStarted,
   118  	}
   119  }
   120  
   121  // spendNtfnSet holds all known, registered spend notifications for a spend
   122  // request (outpoint/output script). If duplicate notifications are requested,
   123  // only one historical dispatch will be spawned to ensure redundant scans are
   124  // not permitted.
   125  type spendNtfnSet struct {
   126  	// ntfns keeps tracks of all the active client notification requests for
   127  	// an outpoint/output script.
   128  	ntfns map[uint64]*SpendNtfn
   129  
   130  	// rescanStatus represents the current rescan state for the spend
   131  	// request (outpoint/output script).
   132  	rescanStatus rescanState
   133  
   134  	// details serves as a cache of the spend details for an outpoint/output
   135  	// script that we'll use to determine if it has already been spent at
   136  	// the time of registration.
   137  	details *SpendDetail
   138  }
   139  
   140  // newSpendNtfnSet constructs a new spend notification set.
   141  func newSpendNtfnSet() *spendNtfnSet {
   142  	return &spendNtfnSet{
   143  		ntfns:        make(map[uint64]*SpendNtfn),
   144  		rescanStatus: rescanNotStarted,
   145  	}
   146  }
   147  
   148  // ConfRequest encapsulates a request for a confirmation notification of either
   149  // a txid or output script.
   150  type ConfRequest struct {
   151  	// TxID is the hash of the transaction for which confirmation
   152  	// notifications are requested. If set to a zero hash, then a
   153  	// confirmation notification will be dispatched upon inclusion of the
   154  	// _script_, rather than the txid.
   155  	TxID chainhash.Hash
   156  
   157  	// PkScript is the public key script of an outpoint created in this
   158  	// transaction.
   159  	PkScript chainscan.PkScript
   160  }
   161  
   162  // NewConfRequest creates a request for a confirmation notification of either a
   163  // txid or output script. A nil txid or an allocated ZeroHash can be used to
   164  // dispatch the confirmation notification on the script.
   165  func NewConfRequest(txid *chainhash.Hash, pkScript []byte) (ConfRequest, error) {
   166  	var r ConfRequest
   167  	scriptVersion := uint16(0)
   168  	outputScript, err := chainscan.ParsePkScript(scriptVersion, pkScript)
   169  	if err != nil {
   170  		return r, err
   171  	}
   172  
   173  	// We'll only set a txid for which we'll dispatch a confirmation
   174  	// notification on this request if one was provided. Otherwise, we'll
   175  	// default to dispatching on the confirmation of the script instead.
   176  	if txid != nil {
   177  		r.TxID = *txid
   178  	}
   179  	r.PkScript = outputScript
   180  
   181  	return r, nil
   182  }
   183  
   184  // String returns the string representation of the ConfRequest.
   185  func (r ConfRequest) String() string {
   186  	if r.TxID != ZeroHash {
   187  		return fmt.Sprintf("txid=%v", r.TxID)
   188  	}
   189  	return fmt.Sprintf("script=%v", r.PkScript)
   190  }
   191  
   192  // ConfHintKey returns the key that will be used to index the confirmation
   193  // request's hint within the height hint cache.
   194  func (r ConfRequest) ConfHintKey() ([]byte, error) {
   195  	if r.TxID == ZeroHash {
   196  		return r.PkScript.Script(), nil
   197  	}
   198  
   199  	var txid bytes.Buffer
   200  	if err := channeldb.WriteElement(&txid, r.TxID); err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	return txid.Bytes(), nil
   205  }
   206  
   207  // MatchesTx determines whether the given transaction satisfies the confirmation
   208  // request. If the confirmation request is for a script, then we'll check all of
   209  // the outputs of the transaction to determine if it matches. Otherwise, we'll
   210  // match on the txid.
   211  func (r ConfRequest) MatchesTx(tx *wire.MsgTx) bool {
   212  	scriptMatches := func() bool {
   213  		pkScript := r.PkScript.Script()
   214  		for _, txOut := range tx.TxOut {
   215  			if bytes.Equal(txOut.PkScript, pkScript) {
   216  				return true
   217  			}
   218  		}
   219  
   220  		return false
   221  	}
   222  
   223  	if r.TxID != ZeroHash {
   224  		return r.TxID == tx.TxHash() && scriptMatches()
   225  	}
   226  
   227  	return scriptMatches()
   228  }
   229  
   230  // ConfNtfn represents a notifier client's request to receive a notification
   231  // once the target transaction/output script gets sufficient confirmations. The
   232  // client is asynchronously notified via the ConfirmationEvent channels.
   233  type ConfNtfn struct {
   234  	// ConfID uniquely identifies the confirmation notification request for
   235  	// the specified transaction/output script.
   236  	ConfID uint64
   237  
   238  	// ConfRequest represents either the txid or script we should detect
   239  	// inclusion of within the chain.
   240  	ConfRequest
   241  
   242  	// NumConfirmations is the number of confirmations after which the
   243  	// notification is to be sent.
   244  	NumConfirmations uint32
   245  
   246  	// Event contains references to the channels that the notifications are to
   247  	// be sent over.
   248  	Event *ConfirmationEvent
   249  
   250  	// HeightHint is the minimum height in the chain that we expect to find
   251  	// this txid.
   252  	HeightHint uint32
   253  
   254  	// dispatched is false if the confirmed notification has not been sent yet.
   255  	dispatched bool
   256  }
   257  
   258  // HistoricalConfDispatch parameterizes a manual rescan for a particular
   259  // transaction/output script. The parameters include the start and end block
   260  // heights specifying the range of blocks to scan.
   261  type HistoricalConfDispatch struct {
   262  	// ConfRequest represents either the txid or script we should detect
   263  	// inclusion of within the chain.
   264  	ConfRequest
   265  
   266  	// StartHeight specifies the block height at which to begin the
   267  	// historical rescan.
   268  	StartHeight uint32
   269  
   270  	// EndHeight specifies the last block height (inclusive) that the
   271  	// historical scan should consider.
   272  	EndHeight uint32
   273  }
   274  
   275  // ConfRegistration encompasses all of the information required for callers to
   276  // retrieve details about a confirmation event.
   277  type ConfRegistration struct {
   278  	// Event contains references to the channels that the notifications are
   279  	// to be sent over.
   280  	Event *ConfirmationEvent
   281  
   282  	// HistoricalDispatch, if non-nil, signals to the client who registered
   283  	// the notification that they are responsible for attempting to manually
   284  	// rescan blocks for the txid/output script between the start and end
   285  	// heights.
   286  	HistoricalDispatch *HistoricalConfDispatch
   287  
   288  	// Height is the height of the TxNotifier at the time the confirmation
   289  	// notification was registered. This can be used so that backends can
   290  	// request to be notified of confirmations from this point forwards.
   291  	Height uint32
   292  }
   293  
   294  // SpendRequest encapsulates a request for a spend notification of either an
   295  // outpoint or output script.
   296  type SpendRequest struct {
   297  	// OutPoint is the outpoint for which a client has requested a spend
   298  	// notification for. If set to a zero outpoint, then a spend
   299  	// notification will be dispatched upon detecting the spend of the
   300  	// _script_, rather than the outpoint.
   301  	OutPoint wire.OutPoint
   302  
   303  	// PkScript is the script of the outpoint. If a zero outpoint is set,
   304  	// then this can be an arbitrary script.
   305  	PkScript chainscan.PkScript
   306  }
   307  
   308  // NewSpendRequest creates a request for a spend notification of either an
   309  // outpoint or output script. A nil outpoint or an allocated ZeroOutPoint can be
   310  // used to dispatch the confirmation notification on the script.
   311  func NewSpendRequest(op *wire.OutPoint, pkScript []byte) (SpendRequest, error) {
   312  	var r SpendRequest
   313  	scriptVersion := uint16(0)
   314  	outputScript, err := chainscan.ParsePkScript(scriptVersion, pkScript)
   315  	if err != nil {
   316  		return r, err
   317  	}
   318  
   319  	// We'll only set an outpoint for which we'll dispatch a spend
   320  	// notification on this request if one was provided. Otherwise, we'll
   321  	// default to dispatching on the spend of the script instead.
   322  	if op != nil {
   323  		r.OutPoint = *op
   324  	}
   325  	r.PkScript = outputScript
   326  
   327  	return r, nil
   328  }
   329  
   330  // String returns the string representation of the SpendRequest.
   331  func (r SpendRequest) String() string {
   332  	if r.OutPoint != ZeroOutPoint {
   333  		return fmt.Sprintf("outpoint=%v, script=%v", r.OutPoint,
   334  			r.PkScript)
   335  	}
   336  	return fmt.Sprintf("outpoint=<zero>, script=%v", r.PkScript)
   337  }
   338  
   339  // SpendHintKey returns the key that will be used to index the spend request's
   340  // hint within the height hint cache.
   341  func (r SpendRequest) SpendHintKey() ([]byte, error) {
   342  	if r.OutPoint == ZeroOutPoint {
   343  		return r.PkScript.Script(), nil
   344  	}
   345  
   346  	var outpoint bytes.Buffer
   347  	err := channeldb.WriteElement(&outpoint, r.OutPoint)
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  
   352  	return outpoint.Bytes(), nil
   353  }
   354  
   355  // SpendNtfn represents a client's request to receive a notification once an
   356  // outpoint/output script has been spent on-chain. The client is asynchronously
   357  // notified via the SpendEvent channels.
   358  type SpendNtfn struct {
   359  	// SpendID uniquely identies the spend notification request for the
   360  	// specified outpoint/output script.
   361  	SpendID uint64
   362  
   363  	// SpendRequest represents either the outpoint or script we should
   364  	// detect the spend of.
   365  	SpendRequest
   366  
   367  	// Event contains references to the channels that the notifications are
   368  	// to be sent over.
   369  	Event *SpendEvent
   370  
   371  	// HeightHint is the earliest height in the chain that we expect to find
   372  	// the spending transaction of the specified outpoint/output script.
   373  	// This value will be overridden by the spend hint cache if it contains
   374  	// an entry for it.
   375  	HeightHint uint32
   376  
   377  	// dispatched signals whether a spend notification has been disptached
   378  	// to the client.
   379  	dispatched bool
   380  }
   381  
   382  // HistoricalSpendDispatch parameterizes a manual rescan to determine the
   383  // spending details (if any) of an outpoint/output script. The parameters
   384  // include the start and end block heights specifying the range of blocks to
   385  // scan.
   386  type HistoricalSpendDispatch struct {
   387  	// SpendRequest represents either the outpoint or script we should
   388  	// detect the spend of.
   389  	SpendRequest
   390  
   391  	// StartHeight specified the block height at which to begin the
   392  	// historical rescan.
   393  	StartHeight uint32
   394  
   395  	// EndHeight specifies the last block height (inclusive) that the
   396  	// historical rescan should consider.
   397  	EndHeight uint32
   398  }
   399  
   400  // SpendRegistration encompasses all of the information required for callers to
   401  // retrieve details about a spend event.
   402  type SpendRegistration struct {
   403  	// Event contains references to the channels that the notifications are
   404  	// to be sent over.
   405  	Event *SpendEvent
   406  
   407  	// HistoricalDispatch, if non-nil, signals to the client who registered
   408  	// the notification that they are responsible for attempting to manually
   409  	// rescan blocks for the txid/output script between the start and end
   410  	// heights.
   411  	HistoricalDispatch *HistoricalSpendDispatch
   412  
   413  	// Height is the height of the TxNotifier at the time the spend
   414  	// notification was registered. This can be used so that backends can
   415  	// request to be notified of spends from this point forwards.
   416  	Height uint32
   417  }
   418  
   419  // TxNotifier is a struct responsible for delivering transaction notifications
   420  // to subscribers. These notifications can be of two different types:
   421  // transaction/output script confirmations and/or outpoint/output script spends.
   422  // The TxNotifier will watch the blockchain as new blocks come in, in order to
   423  // satisfy its client requests.
   424  type TxNotifier struct {
   425  	confClientCounter  uint64 // To be used atomically.
   426  	spendClientCounter uint64 // To be used atomically.
   427  
   428  	// chainParams is the network parameters where the notifier runs.
   429  	chainParams *chaincfg.Params
   430  
   431  	// currentHeight is the height of the tracked blockchain. It is used to
   432  	// determine the number of confirmations a tx has and ensure blocks are
   433  	// connected and disconnected in order.
   434  	currentHeight uint32
   435  
   436  	// reorgSafetyLimit is the chain depth beyond which it is assumed a
   437  	// block will not be reorganized out of the chain. This is used to
   438  	// determine when to prune old notification requests so that reorgs are
   439  	// handled correctly. The coinbase maturity period is a reasonable value
   440  	// to use.
   441  	reorgSafetyLimit uint32
   442  
   443  	// reorgDepth is the depth of a chain organization that this system is
   444  	// being informed of. This is incremented as long as a sequence of
   445  	// blocks are disconnected without being interrupted by a new block.
   446  	reorgDepth uint32
   447  
   448  	// confNotifications is an index of confirmation notification requests
   449  	// by transaction hash/output script.
   450  	confNotifications map[ConfRequest]*confNtfnSet
   451  
   452  	// confsByInitialHeight is an index of watched transactions/output
   453  	// scripts by the height that they are included at in the chain. This
   454  	// is tracked so that incorrect notifications are not sent if a
   455  	// transaction/output script is reorged out of the chain and so that
   456  	// negative confirmations can be recognized.
   457  	confsByInitialHeight map[uint32]map[ConfRequest]struct{}
   458  
   459  	// ntfnsByConfirmHeight is an index of notification requests by the
   460  	// height at which the transaction/output script will have sufficient
   461  	// confirmations.
   462  	ntfnsByConfirmHeight map[uint32]map[*ConfNtfn]struct{}
   463  
   464  	// spendNotifications is an index of all active notification requests
   465  	// per outpoint/output script.
   466  	spendNotifications map[SpendRequest]*spendNtfnSet
   467  
   468  	// spendsByHeight is an index that keeps tracks of the spending height
   469  	// of outpoints/output scripts we are currently tracking notifications
   470  	// for. This is used in order to recover from spending transactions
   471  	// being reorged out of the chain.
   472  	spendsByHeight map[uint32]map[SpendRequest]struct{}
   473  
   474  	// confirmHintCache is a cache used to maintain the latest height hints
   475  	// for transactions/output scripts. Each height hint represents the
   476  	// earliest height at which they scripts could have been confirmed
   477  	// within the chain.
   478  	confirmHintCache ConfirmHintCache
   479  
   480  	// spendHintCache is a cache used to maintain the latest height hints
   481  	// for outpoints/output scripts. Each height hint represents the
   482  	// earliest height at which they could have been spent within the chain.
   483  	spendHintCache SpendHintCache
   484  
   485  	// quit is closed in order to signal that the notifier is gracefully
   486  	// exiting.
   487  	quit chan struct{}
   488  
   489  	sync.Mutex
   490  }
   491  
   492  // NewTxNotifier creates a TxNotifier. The current height of the blockchain is
   493  // accepted as a parameter. The different hint caches (confirm and spend) are
   494  // used as an optimization in order to retrieve a better starting point when
   495  // dispatching a recan for a historical event in the chain.
   496  func NewTxNotifier(startHeight uint32, reorgSafetyLimit uint32,
   497  	confirmHintCache ConfirmHintCache,
   498  	spendHintCache SpendHintCache, chainParams *chaincfg.Params) *TxNotifier {
   499  
   500  	return &TxNotifier{
   501  		chainParams:          chainParams,
   502  		currentHeight:        startHeight,
   503  		reorgSafetyLimit:     reorgSafetyLimit,
   504  		confNotifications:    make(map[ConfRequest]*confNtfnSet),
   505  		confsByInitialHeight: make(map[uint32]map[ConfRequest]struct{}),
   506  		ntfnsByConfirmHeight: make(map[uint32]map[*ConfNtfn]struct{}),
   507  		spendNotifications:   make(map[SpendRequest]*spendNtfnSet),
   508  		spendsByHeight:       make(map[uint32]map[SpendRequest]struct{}),
   509  		confirmHintCache:     confirmHintCache,
   510  		spendHintCache:       spendHintCache,
   511  		quit:                 make(chan struct{}),
   512  	}
   513  }
   514  
   515  // newConfNtfn validates all of the parameters required to successfully create
   516  // and register a confirmation notification.
   517  func (n *TxNotifier) newConfNtfn(txid *chainhash.Hash,
   518  	pkScript []byte, numConfs, heightHint uint32) (*ConfNtfn, error) {
   519  
   520  	// An accompanying output script must always be provided.
   521  	if len(pkScript) == 0 {
   522  		return nil, ErrNoScript
   523  	}
   524  
   525  	// Enforce that we will not dispatch confirmations beyond the reorg
   526  	// safety limit.
   527  	if numConfs == 0 || numConfs > n.reorgSafetyLimit {
   528  		return nil, ErrNumConfsOutOfRange
   529  	}
   530  
   531  	// A height hint must be provided to prevent scanning from the genesis
   532  	// block.
   533  	if heightHint == 0 {
   534  		return nil, ErrNoHeightHint
   535  	}
   536  
   537  	// Ensure the output script is of a supported type.
   538  	confRequest, err := NewConfRequest(txid, pkScript)
   539  	if err != nil {
   540  		return nil, err
   541  	}
   542  
   543  	confID := atomic.AddUint64(&n.confClientCounter, 1)
   544  	return &ConfNtfn{
   545  		ConfID:           confID,
   546  		ConfRequest:      confRequest,
   547  		NumConfirmations: numConfs,
   548  		Event: NewConfirmationEvent(numConfs, func() {
   549  			n.CancelConf(confRequest, confID)
   550  		}),
   551  		HeightHint: heightHint,
   552  	}, nil
   553  }
   554  
   555  // RegisterConf handles a new confirmation notification request. The client will
   556  // be notified when the transaction/output script gets a sufficient number of
   557  // confirmations in the blockchain.
   558  //
   559  // NOTE: If the transaction/output script has already been included in a block
   560  // on the chain, the confirmation details must be provided with the
   561  // UpdateConfDetails method, otherwise we will wait for the transaction/output
   562  // script to confirm even though it already has.
   563  func (n *TxNotifier) RegisterConf(txid *chainhash.Hash, pkScript []byte,
   564  	numConfs, heightHint uint32) (*ConfRegistration, error) {
   565  
   566  	select {
   567  	case <-n.quit:
   568  		return nil, ErrTxNotifierExiting
   569  	default:
   570  	}
   571  
   572  	// We'll start by performing a series of validation checks.
   573  	ntfn, err := n.newConfNtfn(txid, pkScript, numConfs, heightHint)
   574  	if err != nil {
   575  		return nil, err
   576  	}
   577  
   578  	// Before proceeding to register the notification, we'll query our
   579  	// height hint cache to determine whether a better one exists.
   580  	//
   581  	// TODO(conner): verify that all submitted height hints are identical.
   582  	startHeight := ntfn.HeightHint
   583  	hint, err := n.confirmHintCache.QueryConfirmHint(ntfn.ConfRequest)
   584  	if err == nil {
   585  		if hint > startHeight {
   586  			Log.Debugf("Using height hint %d retrieved from cache "+
   587  				"for %v instead of %d for conf subscription",
   588  				hint, ntfn.ConfRequest, startHeight)
   589  			startHeight = hint
   590  		}
   591  	} else if err != ErrConfirmHintNotFound {
   592  		Log.Errorf("Unable to query confirm hint for %v: %v",
   593  			ntfn.ConfRequest, err)
   594  	}
   595  
   596  	Log.Infof("New confirmation subscription: conf_id=%d, %v, "+
   597  		"num_confs=%v height_hint=%d", ntfn.ConfID, ntfn.ConfRequest,
   598  		numConfs, startHeight)
   599  
   600  	n.Lock()
   601  	defer n.Unlock()
   602  
   603  	confSet, ok := n.confNotifications[ntfn.ConfRequest]
   604  	if !ok {
   605  		// If this is the first registration for this request, construct
   606  		// a confSet to coalesce all notifications for the same request.
   607  		confSet = newConfNtfnSet()
   608  		n.confNotifications[ntfn.ConfRequest] = confSet
   609  	}
   610  	confSet.ntfns[ntfn.ConfID] = ntfn
   611  
   612  	switch confSet.rescanStatus {
   613  
   614  	// A prior rescan has already completed and we are actively watching at
   615  	// tip for this request.
   616  	case rescanComplete:
   617  		// If the confirmation details for this set of notifications has
   618  		// already been found, we'll attempt to deliver them immediately
   619  		// to this client.
   620  		Log.Debugf("Attempting to dispatch confirmation for %v on "+
   621  			"registration since rescan has finished",
   622  			ntfn.ConfRequest)
   623  
   624  		err := n.dispatchConfDetails(ntfn, confSet.details)
   625  		if err != nil {
   626  			return nil, err
   627  		}
   628  
   629  		return &ConfRegistration{
   630  			Event:              ntfn.Event,
   631  			HistoricalDispatch: nil,
   632  			Height:             n.currentHeight,
   633  		}, nil
   634  
   635  	// A rescan is already in progress, return here to prevent dispatching
   636  	// another. When the rescan returns, this notification's details will be
   637  	// updated as well.
   638  	case rescanPending:
   639  		Log.Debugf("Waiting for pending rescan to finish before "+
   640  			"notifying %v at tip", ntfn.ConfRequest)
   641  
   642  		return &ConfRegistration{
   643  			Event:              ntfn.Event,
   644  			HistoricalDispatch: nil,
   645  			Height:             n.currentHeight,
   646  		}, nil
   647  
   648  	// If no rescan has been dispatched, attempt to do so now.
   649  	case rescanNotStarted:
   650  	}
   651  
   652  	// If the provided or cached height hint indicates that the
   653  	// transaction with the given txid/output script is to be confirmed at a
   654  	// height greater than the notifier's current height, we'll refrain from
   655  	// spawning a historical dispatch.
   656  	if startHeight > n.currentHeight {
   657  		Log.Debugf("Height hint is above current height, not "+
   658  			"dispatching historical confirmation rescan for %v",
   659  			ntfn.ConfRequest)
   660  
   661  		// Set the rescan status to complete, which will allow the
   662  		// notifier to start delivering messages for this set
   663  		// immediately.
   664  		confSet.rescanStatus = rescanComplete
   665  		return &ConfRegistration{
   666  			Event:              ntfn.Event,
   667  			HistoricalDispatch: nil,
   668  			Height:             n.currentHeight,
   669  		}, nil
   670  	}
   671  
   672  	Log.Debugf("Dispatching historical confirmation rescan for %v",
   673  		ntfn.ConfRequest)
   674  
   675  	// Construct the parameters for historical dispatch, scanning the range
   676  	// of blocks between our best known height hint and the notifier's
   677  	// current height. The notifier will begin also watching for
   678  	// confirmations at tip starting with the next block.
   679  	dispatch := &HistoricalConfDispatch{
   680  		ConfRequest: ntfn.ConfRequest,
   681  		StartHeight: startHeight,
   682  		EndHeight:   n.currentHeight,
   683  	}
   684  
   685  	// Set this confSet's status to pending, ensuring subsequent
   686  	// registrations don't also attempt a dispatch.
   687  	confSet.rescanStatus = rescanPending
   688  
   689  	return &ConfRegistration{
   690  		Event:              ntfn.Event,
   691  		HistoricalDispatch: dispatch,
   692  		Height:             n.currentHeight,
   693  	}, nil
   694  }
   695  
   696  // CancelConf cancels an existing request for a spend notification of an
   697  // outpoint/output script. The request is identified by its spend ID.
   698  func (n *TxNotifier) CancelConf(confRequest ConfRequest, confID uint64) {
   699  	select {
   700  	case <-n.quit:
   701  		return
   702  	default:
   703  	}
   704  
   705  	n.Lock()
   706  	defer n.Unlock()
   707  
   708  	confSet, ok := n.confNotifications[confRequest]
   709  	if !ok {
   710  		return
   711  	}
   712  	ntfn, ok := confSet.ntfns[confID]
   713  	if !ok {
   714  		return
   715  	}
   716  
   717  	Log.Infof("Canceling confirmation notification: conf_id=%d, %v", confID,
   718  		confRequest)
   719  
   720  	// We'll close all the notification channels to let the client know
   721  	// their cancel request has been fulfilled.
   722  	close(ntfn.Event.Confirmed)
   723  	close(ntfn.Event.Updates)
   724  	close(ntfn.Event.NegativeConf)
   725  
   726  	// Finally, we'll clean up any lingering references to this
   727  	// notification.
   728  	delete(confSet.ntfns, confID)
   729  
   730  	// Remove the queued confirmation notification if the transaction has
   731  	// already confirmed, but hasn't met its required number of
   732  	// confirmations.
   733  	if confSet.details != nil {
   734  		confHeight := confSet.details.BlockHeight +
   735  			ntfn.NumConfirmations - 1
   736  		delete(n.ntfnsByConfirmHeight[confHeight], ntfn)
   737  	}
   738  }
   739  
   740  // UpdateConfDetails attempts to update the confirmation details for an active
   741  // notification within the notifier. This should only be used in the case of a
   742  // transaction/output script that has confirmed before the notifier's current
   743  // height.
   744  //
   745  // NOTE: The notification should be registered first to ensure notifications are
   746  // dispatched correctly.
   747  func (n *TxNotifier) UpdateConfDetails(confRequest ConfRequest,
   748  	details *TxConfirmation) error {
   749  
   750  	select {
   751  	case <-n.quit:
   752  		return ErrTxNotifierExiting
   753  	default:
   754  	}
   755  
   756  	// Ensure we hold the lock throughout handling the notification to
   757  	// prevent the notifier from advancing its height underneath us.
   758  	n.Lock()
   759  	defer n.Unlock()
   760  
   761  	// First, we'll determine whether we have an active confirmation
   762  	// notification for the given txid/script.
   763  	confSet, ok := n.confNotifications[confRequest]
   764  	if !ok {
   765  		return fmt.Errorf("confirmation notification for %v not found",
   766  			confRequest)
   767  	}
   768  
   769  	// If the confirmation details were already found at tip, all existing
   770  	// notifications will have been dispatched or queued for dispatch. We
   771  	// can exit early to avoid sending too many notifications on the
   772  	// buffered channels.
   773  	if confSet.details != nil {
   774  		return nil
   775  	}
   776  
   777  	// The historical dispatch has been completed for this confSet. We'll
   778  	// update the rescan status and cache any details that were found. If
   779  	// the details are nil, that implies we did not find them and will
   780  	// continue to watch for them at tip.
   781  	confSet.rescanStatus = rescanComplete
   782  
   783  	// The notifier has yet to reach the height at which the
   784  	// transaction/output script was included in a block, so we should defer
   785  	// until handling it then within ConnectTip.
   786  	if details == nil {
   787  		Log.Debugf("Confirmation details for %v not found during "+
   788  			"historical dispatch, waiting to dispatch at tip",
   789  			confRequest)
   790  
   791  		// We'll commit the current height as the confirm hint to
   792  		// prevent another potentially long rescan if we restart before
   793  		// a new block comes in.
   794  		err := n.confirmHintCache.CommitConfirmHint(
   795  			n.currentHeight, confRequest,
   796  		)
   797  		if err != nil {
   798  			// The error is not fatal as this is an optimistic
   799  			// optimization, so we'll avoid returning an error.
   800  			Log.Debugf("Unable to update confirm hint to %d for "+
   801  				"%v: %v", n.currentHeight, confRequest, err)
   802  		}
   803  
   804  		return nil
   805  	}
   806  
   807  	if details.BlockHeight > n.currentHeight {
   808  		Log.Debugf("Confirmation details for %v found above current "+
   809  			"height, waiting to dispatch at tip", confRequest)
   810  
   811  		return nil
   812  	}
   813  
   814  	Log.Debugf("Updating confirmation details for %v", confRequest)
   815  
   816  	err := n.confirmHintCache.CommitConfirmHint(
   817  		details.BlockHeight, confRequest,
   818  	)
   819  	if err != nil {
   820  		// The error is not fatal, so we should not return an error to
   821  		// the caller.
   822  		Log.Errorf("Unable to update confirm hint to %d for %v: %v",
   823  			details.BlockHeight, confRequest, err)
   824  	}
   825  
   826  	// Cache the details found in the rescan and attempt to dispatch any
   827  	// notifications that have not yet been delivered.
   828  	confSet.details = details
   829  	for _, ntfn := range confSet.ntfns {
   830  		err = n.dispatchConfDetails(ntfn, details)
   831  		if err != nil {
   832  			return err
   833  		}
   834  	}
   835  
   836  	return nil
   837  }
   838  
   839  // dispatchConfDetails attempts to cache and dispatch details to a particular
   840  // client if the transaction/output script has sufficiently confirmed. If the
   841  // provided details are nil, this method will be a no-op.
   842  func (n *TxNotifier) dispatchConfDetails(
   843  	ntfn *ConfNtfn, details *TxConfirmation) error {
   844  
   845  	// If there are no conf details to dispatch or if the notification has
   846  	// already been dispatched, then we can skip dispatching to this
   847  	// client.
   848  	if details == nil || ntfn.dispatched {
   849  		Log.Debugf("Skipping dispatch of conf details(%v) for "+
   850  			"request %v, dispatched=%v", details, ntfn.ConfRequest,
   851  			ntfn.dispatched)
   852  
   853  		return nil
   854  	}
   855  
   856  	// Now, we'll examine whether the transaction/output script of this
   857  	// request has reached its required number of confirmations. If it has,
   858  	// we'll dispatch a confirmation notification to the caller.
   859  	confHeight := details.BlockHeight + ntfn.NumConfirmations - 1
   860  	if confHeight <= n.currentHeight {
   861  		Log.Infof("Dispatching %v confirmation notification for %v",
   862  			ntfn.NumConfirmations, ntfn.ConfRequest)
   863  
   864  		// We'll send a 0 value to the Updates channel,
   865  		// indicating that the transaction/output script has already
   866  		// been confirmed.
   867  		select {
   868  		case ntfn.Event.Updates <- 0:
   869  		case <-n.quit:
   870  			return ErrTxNotifierExiting
   871  		}
   872  
   873  		select {
   874  		case ntfn.Event.Confirmed <- details:
   875  			ntfn.dispatched = true
   876  		case <-n.quit:
   877  			return ErrTxNotifierExiting
   878  		}
   879  	} else {
   880  		Log.Debugf("Queueing %v confirmation notification for %v at tip ",
   881  			ntfn.NumConfirmations, ntfn.ConfRequest)
   882  
   883  		// Otherwise, we'll keep track of the notification
   884  		// request by the height at which we should dispatch the
   885  		// confirmation notification.
   886  		ntfnSet, exists := n.ntfnsByConfirmHeight[confHeight]
   887  		if !exists {
   888  			ntfnSet = make(map[*ConfNtfn]struct{})
   889  			n.ntfnsByConfirmHeight[confHeight] = ntfnSet
   890  		}
   891  		ntfnSet[ntfn] = struct{}{}
   892  
   893  		// We'll also send an update to the client of how many
   894  		// confirmations are left for the transaction/output script to
   895  		// be confirmed.
   896  		numConfsLeft := confHeight - n.currentHeight
   897  		select {
   898  		case ntfn.Event.Updates <- numConfsLeft:
   899  		case <-n.quit:
   900  			return ErrTxNotifierExiting
   901  		}
   902  	}
   903  
   904  	// As a final check, we'll also watch the transaction/output script if
   905  	// it's still possible for it to get reorged out of the chain.
   906  	reorgSafeHeight := details.BlockHeight + n.reorgSafetyLimit
   907  	if reorgSafeHeight > n.currentHeight {
   908  		txSet, exists := n.confsByInitialHeight[details.BlockHeight]
   909  		if !exists {
   910  			txSet = make(map[ConfRequest]struct{})
   911  			n.confsByInitialHeight[details.BlockHeight] = txSet
   912  		}
   913  		txSet[ntfn.ConfRequest] = struct{}{}
   914  	}
   915  
   916  	return nil
   917  }
   918  
   919  // newSpendNtfn validates all of the parameters required to successfully create
   920  // and register a spend notification.
   921  func (n *TxNotifier) newSpendNtfn(outpoint *wire.OutPoint,
   922  	pkScript []byte, heightHint uint32) (*SpendNtfn, error) {
   923  
   924  	// An accompanying output script must always be provided.
   925  	if len(pkScript) == 0 {
   926  		return nil, ErrNoScript
   927  	}
   928  
   929  	// A height hint must be provided to prevent scanning from the genesis
   930  	// block.
   931  	if heightHint == 0 {
   932  		return nil, ErrNoHeightHint
   933  	}
   934  
   935  	// Ensure the output script is of a supported type.
   936  	spendRequest, err := NewSpendRequest(outpoint, pkScript)
   937  	if err != nil {
   938  		return nil, err
   939  	}
   940  
   941  	spendID := atomic.AddUint64(&n.spendClientCounter, 1)
   942  	return &SpendNtfn{
   943  		SpendID:      spendID,
   944  		SpendRequest: spendRequest,
   945  		Event: NewSpendEvent(func() {
   946  			n.CancelSpend(spendRequest, spendID)
   947  		}),
   948  		HeightHint: heightHint,
   949  	}, nil
   950  }
   951  
   952  // RegisterSpend handles a new spend notification request. The client will be
   953  // notified once the outpoint/output script is detected as spent within the
   954  // chain.
   955  //
   956  // NOTE: If the outpoint/output script has already been spent within the chain
   957  // before the notifier's current tip, the spend details must be provided with
   958  // the UpdateSpendDetails method, otherwise we will wait for the outpoint/output
   959  // script to be spent at tip, even though it already has.
   960  func (n *TxNotifier) RegisterSpend(outpoint *wire.OutPoint, pkScript []byte,
   961  	heightHint uint32) (*SpendRegistration, error) {
   962  
   963  	select {
   964  	case <-n.quit:
   965  		return nil, ErrTxNotifierExiting
   966  	default:
   967  	}
   968  
   969  	// We'll start by performing a series of validation checks.
   970  	ntfn, err := n.newSpendNtfn(outpoint, pkScript, heightHint)
   971  	if err != nil {
   972  		return nil, err
   973  	}
   974  
   975  	// Before proceeding to register the notification, we'll query our spend
   976  	// hint cache to determine whether a better one exists.
   977  	startHeight := ntfn.HeightHint
   978  	hint, err := n.spendHintCache.QuerySpendHint(ntfn.SpendRequest)
   979  	if err == nil {
   980  		if hint > startHeight {
   981  			Log.Debugf("Using height hint %d retrieved from cache "+
   982  				"for %v instead of %d for spend subscription",
   983  				hint, ntfn.SpendRequest, startHeight)
   984  			startHeight = hint
   985  		}
   986  	} else if err != ErrSpendHintNotFound {
   987  		Log.Errorf("Unable to query spend hint for %v: %v",
   988  			ntfn.SpendRequest, err)
   989  	}
   990  
   991  	n.Lock()
   992  	defer n.Unlock()
   993  
   994  	Log.Infof("New spend subscription: spend_id=%d, %v, height_hint=%d",
   995  		ntfn.SpendID, ntfn.SpendRequest, startHeight)
   996  
   997  	// Keep track of the notification request so that we can properly
   998  	// dispatch a spend notification later on.
   999  	spendSet, ok := n.spendNotifications[ntfn.SpendRequest]
  1000  	if !ok {
  1001  		// If this is the first registration for the request, we'll
  1002  		// construct a spendNtfnSet to coalesce all notifications.
  1003  		spendSet = newSpendNtfnSet()
  1004  		n.spendNotifications[ntfn.SpendRequest] = spendSet
  1005  	}
  1006  	spendSet.ntfns[ntfn.SpendID] = ntfn
  1007  
  1008  	// We'll now let the caller know whether a historical rescan is needed
  1009  	// depending on the current rescan status.
  1010  	switch spendSet.rescanStatus {
  1011  
  1012  	// If the spending details for this request have already been determined
  1013  	// and cached, then we can use them to immediately dispatch the spend
  1014  	// notification to the client.
  1015  	case rescanComplete:
  1016  		Log.Debugf("Attempting to dispatch spend for %v on "+
  1017  			"registration since rescan has finished",
  1018  			ntfn.SpendRequest)
  1019  
  1020  		err := n.dispatchSpendDetails(ntfn, spendSet.details)
  1021  		if err != nil {
  1022  			return nil, err
  1023  		}
  1024  
  1025  		return &SpendRegistration{
  1026  			Event:              ntfn.Event,
  1027  			HistoricalDispatch: nil,
  1028  			Height:             n.currentHeight,
  1029  		}, nil
  1030  
  1031  	// If there is an active rescan to determine whether the request has
  1032  	// been spent, then we won't trigger another one.
  1033  	case rescanPending:
  1034  		Log.Debugf("Waiting for pending rescan to finish before "+
  1035  			"notifying %v at tip", ntfn.SpendRequest)
  1036  
  1037  		return &SpendRegistration{
  1038  			Event:              ntfn.Event,
  1039  			HistoricalDispatch: nil,
  1040  			Height:             n.currentHeight,
  1041  		}, nil
  1042  
  1043  	// Otherwise, we'll fall through and let the caller know that a rescan
  1044  	// should be dispatched to determine whether the request has already
  1045  	// been spent.
  1046  	case rescanNotStarted:
  1047  	}
  1048  
  1049  	// However, if the spend hint, either provided by the caller or
  1050  	// retrieved from the cache, is found to be at a later height than the
  1051  	// TxNotifier is aware of, then we'll refrain from dispatching a
  1052  	// historical rescan and wait for the spend to come in at tip.
  1053  	if startHeight > n.currentHeight {
  1054  		Log.Debugf("Spend hint of %d for %v is above current height %d",
  1055  			startHeight, ntfn.SpendRequest, n.currentHeight)
  1056  
  1057  		// We'll also set the rescan status as complete to ensure that
  1058  		// spend hints for this request get updated upon
  1059  		// connected/disconnected blocks.
  1060  		spendSet.rescanStatus = rescanComplete
  1061  		return &SpendRegistration{
  1062  			Event:              ntfn.Event,
  1063  			HistoricalDispatch: nil,
  1064  			Height:             n.currentHeight,
  1065  		}, nil
  1066  	}
  1067  
  1068  	// We'll set the rescan status to pending to ensure subsequent
  1069  	// notifications don't also attempt a historical dispatch.
  1070  	spendSet.rescanStatus = rescanPending
  1071  
  1072  	Log.Infof("Dispatching historical spend rescan for %v, start=%d, "+
  1073  		"end=%d", ntfn.SpendRequest, startHeight, n.currentHeight)
  1074  
  1075  	return &SpendRegistration{
  1076  		Event: ntfn.Event,
  1077  		HistoricalDispatch: &HistoricalSpendDispatch{
  1078  			SpendRequest: ntfn.SpendRequest,
  1079  			StartHeight:  startHeight,
  1080  			EndHeight:    n.currentHeight,
  1081  		},
  1082  		Height: n.currentHeight,
  1083  	}, nil
  1084  }
  1085  
  1086  // CancelSpend cancels an existing request for a spend notification of an
  1087  // outpoint/output script. The request is identified by its spend ID.
  1088  func (n *TxNotifier) CancelSpend(spendRequest SpendRequest, spendID uint64) {
  1089  	select {
  1090  	case <-n.quit:
  1091  		return
  1092  	default:
  1093  	}
  1094  
  1095  	n.Lock()
  1096  	defer n.Unlock()
  1097  
  1098  	spendSet, ok := n.spendNotifications[spendRequest]
  1099  	if !ok {
  1100  		return
  1101  	}
  1102  	ntfn, ok := spendSet.ntfns[spendID]
  1103  	if !ok {
  1104  		return
  1105  	}
  1106  
  1107  	Log.Infof("Canceling spend notification: spend_id=%d, %v", spendID,
  1108  		spendRequest)
  1109  
  1110  	// We'll close all the notification channels to let the client know
  1111  	// their cancel request has been fulfilled.
  1112  	close(ntfn.Event.Spend)
  1113  	close(ntfn.Event.Reorg)
  1114  	close(ntfn.Event.Done)
  1115  	delete(spendSet.ntfns, spendID)
  1116  }
  1117  
  1118  // ProcessRelevantSpendTx processes a transaction provided externally. This will
  1119  // check whether the transaction is relevant to the notifier if it spends any
  1120  // outpoints/output scripts for which we currently have registered notifications
  1121  // for. If it is relevant, spend notifications will be dispatched to the caller.
  1122  func (n *TxNotifier) ProcessRelevantSpendTx(tx *dcrutil.Tx,
  1123  	blockHeight uint32) error {
  1124  
  1125  	select {
  1126  	case <-n.quit:
  1127  		return ErrTxNotifierExiting
  1128  	default:
  1129  	}
  1130  
  1131  	// Ensure we hold the lock throughout handling the notification to
  1132  	// prevent the notifier from advancing its height underneath us.
  1133  	n.Lock()
  1134  	defer n.Unlock()
  1135  
  1136  	// We'll use a channel to coalesce all the spend requests that this
  1137  	// transaction fulfills.
  1138  	type spend struct {
  1139  		request *SpendRequest
  1140  		details *SpendDetail
  1141  	}
  1142  
  1143  	// We'll set up the onSpend filter callback to gather all the fulfilled
  1144  	// spends requests within this transaction.
  1145  	var spends []spend
  1146  	onSpend := func(request SpendRequest, details *SpendDetail) {
  1147  		spends = append(spends, spend{&request, details})
  1148  	}
  1149  	n.filterTx(tx, nil, blockHeight, nil, onSpend)
  1150  
  1151  	// After the transaction has been filtered, we can finally dispatch
  1152  	// notifications for each request.
  1153  	for _, spend := range spends {
  1154  		err := n.updateSpendDetails(*spend.request, spend.details)
  1155  		if err != nil {
  1156  			return err
  1157  		}
  1158  	}
  1159  
  1160  	return nil
  1161  }
  1162  
  1163  // UpdateSpendDetails attempts to update the spend details for all active spend
  1164  // notification requests for an outpoint/output script. This method should be
  1165  // used once a historical scan of the chain has finished. If the historical scan
  1166  // did not find a spending transaction for it, the spend details may be nil.
  1167  //
  1168  // NOTE: A notification request for the outpoint/output script must be
  1169  // registered first to ensure notifications are delivered.
  1170  func (n *TxNotifier) UpdateSpendDetails(spendRequest SpendRequest,
  1171  	details *SpendDetail) error {
  1172  
  1173  	select {
  1174  	case <-n.quit:
  1175  		return ErrTxNotifierExiting
  1176  	default:
  1177  	}
  1178  
  1179  	// Ensure we hold the lock throughout handling the notification to
  1180  	// prevent the notifier from advancing its height underneath us.
  1181  	n.Lock()
  1182  	defer n.Unlock()
  1183  
  1184  	return n.updateSpendDetails(spendRequest, details)
  1185  }
  1186  
  1187  // updateSpendDetails attempts to update the spend details for all active spend
  1188  // notification requests for an outpoint/output script. This method should be
  1189  // used once a historical scan of the chain has finished. If the historical scan
  1190  // did not find a spending transaction for it, the spend details may be nil.
  1191  //
  1192  // NOTE: This method must be called with the TxNotifier's lock held.
  1193  func (n *TxNotifier) updateSpendDetails(spendRequest SpendRequest,
  1194  	details *SpendDetail) error {
  1195  
  1196  	// Mark the ongoing historical rescan for this request as finished. This
  1197  	// will allow us to update the spend hints for it at tip.
  1198  	spendSet, ok := n.spendNotifications[spendRequest]
  1199  	if !ok {
  1200  		return fmt.Errorf("spend notification for %v not found",
  1201  			spendRequest)
  1202  	}
  1203  
  1204  	// If the spend details have already been found either at tip, then the
  1205  	// notifications should have already been dispatched, so we can exit
  1206  	// early to prevent sending duplicate notifications.
  1207  	if spendSet.details != nil {
  1208  		return nil
  1209  	}
  1210  
  1211  	// Since the historical rescan has completed for this request, we'll
  1212  	// mark its rescan status as complete in order to ensure that the
  1213  	// TxNotifier can properly update its spend hints upon
  1214  	// connected/disconnected blocks.
  1215  	spendSet.rescanStatus = rescanComplete
  1216  
  1217  	// If the historical rescan was not able to find a spending transaction
  1218  	// for this request, then we can track the spend at tip.
  1219  	if details == nil {
  1220  		// We'll commit the current height as the spend hint to prevent
  1221  		// another potentially long rescan if we restart before a new
  1222  		// block comes in.
  1223  		err := n.spendHintCache.CommitSpendHint(
  1224  			n.currentHeight, spendRequest,
  1225  		)
  1226  		if err != nil {
  1227  			// The error is not fatal as this is an optimistic
  1228  			// optimization, so we'll avoid returning an error.
  1229  			Log.Debugf("Unable to update spend hint to %d for %v: %v",
  1230  				n.currentHeight, spendRequest, err)
  1231  		}
  1232  
  1233  		Log.Debugf("Updated spend hint to height=%v for unconfirmed "+
  1234  			"spend request %v", n.currentHeight, spendRequest)
  1235  		return nil
  1236  	}
  1237  
  1238  	// If the historical rescan found the spending transaction for this
  1239  	// request, but it's at a later height than the notifier (this can
  1240  	// happen due to latency with the backend during a reorg), then we'll
  1241  	// defer handling the notification until the notifier has caught up to
  1242  	// such height.
  1243  	if uint32(details.SpendingHeight) > n.currentHeight {
  1244  		return nil
  1245  	}
  1246  
  1247  	// Now that we've determined the request has been spent, we'll commit
  1248  	// its spending height as its hint in the cache and dispatch
  1249  	// notifications to all of its respective clients.
  1250  	err := n.spendHintCache.CommitSpendHint(
  1251  		uint32(details.SpendingHeight), spendRequest,
  1252  	)
  1253  	if err != nil {
  1254  		// The error is not fatal as this is an optimistic optimization,
  1255  		// so we'll avoid returning an error.
  1256  		Log.Debugf("Unable to update spend hint to %d for %v: %v",
  1257  			details.SpendingHeight, spendRequest, err)
  1258  	}
  1259  
  1260  	Log.Debugf("Updated spend hint to height=%v for confirmed spend "+
  1261  		"request %v", details.SpendingHeight, spendRequest)
  1262  
  1263  	spendSet.details = details
  1264  	for _, ntfn := range spendSet.ntfns {
  1265  		err := n.dispatchSpendDetails(ntfn, spendSet.details)
  1266  		if err != nil {
  1267  			return err
  1268  		}
  1269  	}
  1270  
  1271  	return nil
  1272  }
  1273  
  1274  // dispatchSpendDetails dispatches a spend notification to the client.
  1275  //
  1276  // NOTE: This must be called with the TxNotifier's lock held.
  1277  func (n *TxNotifier) dispatchSpendDetails(ntfn *SpendNtfn, details *SpendDetail) error {
  1278  	// If there are no spend details to dispatch or if the notification has
  1279  	// already been dispatched, then we can skip dispatching to this client.
  1280  	if details == nil || ntfn.dispatched {
  1281  		Log.Debugf("Skipping dispatch of spend details(%v) for "+
  1282  			"request %v, dispatched=%v", details, ntfn.SpendRequest,
  1283  			ntfn.dispatched)
  1284  		return nil
  1285  	}
  1286  
  1287  	Log.Infof("Dispatching confirmed spend notification for %v at "+
  1288  		"current height=%d: %v", ntfn.SpendRequest, n.currentHeight,
  1289  		details)
  1290  
  1291  	select {
  1292  	case ntfn.Event.Spend <- details:
  1293  		ntfn.dispatched = true
  1294  	case <-n.quit:
  1295  		return ErrTxNotifierExiting
  1296  	}
  1297  
  1298  	spendHeight := uint32(details.SpendingHeight)
  1299  
  1300  	// We also add to spendsByHeight to notify on chain reorgs.
  1301  	reorgSafeHeight := spendHeight + n.reorgSafetyLimit
  1302  	if reorgSafeHeight > n.currentHeight {
  1303  		txSet, exists := n.spendsByHeight[spendHeight]
  1304  		if !exists {
  1305  			txSet = make(map[SpendRequest]struct{})
  1306  			n.spendsByHeight[spendHeight] = txSet
  1307  		}
  1308  		txSet[ntfn.SpendRequest] = struct{}{}
  1309  	}
  1310  
  1311  	return nil
  1312  }
  1313  
  1314  // ConnectTip handles a new block extending the current chain. It will go
  1315  // through every transaction and determine if it is relevant to any of its
  1316  // clients. A transaction can be relevant in either of the following two ways:
  1317  //
  1318  //  1. One of the inputs in the transaction spends an outpoint/output script
  1319  //     for which we currently have an active spend registration for.
  1320  //
  1321  //  2. The transaction has a txid or output script for which we currently have
  1322  //     an active confirmation registration for.
  1323  //
  1324  // In the event that the transaction is relevant, a confirmation/spend
  1325  // notification will be queued for dispatch to the relevant clients.
  1326  // Confirmation notifications will only be dispatched for transactions/output
  1327  // scripts that have met the required number of confirmations required by the
  1328  // client.
  1329  //
  1330  // NOTE: In order to actually dispatch the relevant transaction notifications to
  1331  // clients, NotifyHeight must be called with the same block height in order to
  1332  // maintain correctness.
  1333  func (n *TxNotifier) ConnectTip(blockHash *chainhash.Hash, blockHeight uint32,
  1334  	txns []*dcrutil.Tx) error {
  1335  
  1336  	select {
  1337  	case <-n.quit:
  1338  		return ErrTxNotifierExiting
  1339  	default:
  1340  	}
  1341  
  1342  	n.Lock()
  1343  	defer n.Unlock()
  1344  
  1345  	if blockHeight != n.currentHeight+1 {
  1346  		return fmt.Errorf("received blocks out of order: "+
  1347  			"current height=%d, new height=%d",
  1348  			n.currentHeight, blockHeight)
  1349  	}
  1350  	n.currentHeight++
  1351  	n.reorgDepth = 0
  1352  
  1353  	// First, we'll iterate over all the transactions found in this block to
  1354  	// determine if it includes any relevant transactions to the TxNotifier.
  1355  	Log.Debugf("Filtering %d txns for %d spend requests at height %d",
  1356  		len(txns), len(n.spendNotifications), blockHeight)
  1357  	for _, tx := range txns {
  1358  		n.filterTx(
  1359  			tx, blockHash, blockHeight, n.handleConfDetailsAtTip,
  1360  			n.handleSpendDetailsAtTip,
  1361  		)
  1362  	}
  1363  
  1364  	// Now that we've determined which requests were confirmed and spent
  1365  	// within the new block, we can update their entries in their respective
  1366  	// caches, along with all of our unconfirmed and unspent requests.
  1367  	n.updateHints(blockHeight)
  1368  
  1369  	// Finally, we'll clear the entries from our set of notifications for
  1370  	// requests that are no longer under the risk of being reorged out of
  1371  	// the chain.
  1372  	if blockHeight >= n.reorgSafetyLimit {
  1373  		matureBlockHeight := blockHeight - n.reorgSafetyLimit
  1374  		for confRequest := range n.confsByInitialHeight[matureBlockHeight] {
  1375  			confSet := n.confNotifications[confRequest]
  1376  			for _, ntfn := range confSet.ntfns {
  1377  				select {
  1378  				case ntfn.Event.Done <- struct{}{}:
  1379  				case <-n.quit:
  1380  					return ErrTxNotifierExiting
  1381  				}
  1382  			}
  1383  
  1384  			delete(n.confNotifications, confRequest)
  1385  		}
  1386  		delete(n.confsByInitialHeight, matureBlockHeight)
  1387  
  1388  		for spendRequest := range n.spendsByHeight[matureBlockHeight] {
  1389  			spendSet := n.spendNotifications[spendRequest]
  1390  			for _, ntfn := range spendSet.ntfns {
  1391  				select {
  1392  				case ntfn.Event.Done <- struct{}{}:
  1393  				case <-n.quit:
  1394  					return ErrTxNotifierExiting
  1395  				}
  1396  			}
  1397  
  1398  			Log.Debugf("Deleting mature spend request %v at "+
  1399  				"height=%d", spendRequest, blockHeight)
  1400  			delete(n.spendNotifications, spendRequest)
  1401  		}
  1402  		delete(n.spendsByHeight, matureBlockHeight)
  1403  	}
  1404  
  1405  	return nil
  1406  }
  1407  
  1408  // filterTx determines whether the transaction spends or confirms any
  1409  // outstanding pending requests. The onConf and onSpend callbacks can be used to
  1410  // retrieve all the requests fulfilled by this transaction as they occur.
  1411  func (n *TxNotifier) filterTx(tx *dcrutil.Tx, blockHash *chainhash.Hash,
  1412  	blockHeight uint32, onConf func(ConfRequest, *TxConfirmation),
  1413  	onSpend func(SpendRequest, *SpendDetail)) {
  1414  
  1415  	// In order to determine if this transaction is relevant to the
  1416  	// notifier, we'll check its inputs for any outstanding spend
  1417  	// requests.
  1418  	txHash := tx.Hash()
  1419  	if onSpend != nil {
  1420  		// notifyDetails is a helper closure that will construct the
  1421  		// spend details of a request and hand them off to the onSpend
  1422  		// callback.
  1423  		notifyDetails := func(spendRequest SpendRequest,
  1424  			prevOut wire.OutPoint, inputIdx uint32) {
  1425  
  1426  			Log.Debugf("Found spend of %v: spend_tx=%v, "+
  1427  				"block_height=%d", spendRequest, txHash,
  1428  				blockHeight)
  1429  
  1430  			onSpend(spendRequest, &SpendDetail{
  1431  				SpentOutPoint:     &prevOut,
  1432  				SpenderTxHash:     txHash,
  1433  				SpendingTx:        tx.MsgTx(),
  1434  				SpenderInputIndex: inputIdx,
  1435  				SpendingHeight:    int32(blockHeight),
  1436  			})
  1437  		}
  1438  
  1439  		for i, txIn := range tx.MsgTx().TxIn {
  1440  			// We'll re-derive the script of the output being spent
  1441  			// to determine if the inputs spends any registered
  1442  			// requests.
  1443  			prevOut := txIn.PreviousOutPoint
  1444  			pkScript, err := chainscan.ComputePkScript(
  1445  				0, txIn.SignatureScript,
  1446  			)
  1447  			if err != nil {
  1448  				continue
  1449  			}
  1450  			spendRequest := SpendRequest{
  1451  				OutPoint: prevOut,
  1452  				PkScript: pkScript,
  1453  			}
  1454  
  1455  			// If we have any, we'll record their spend height so
  1456  			// that notifications get dispatched to the respective
  1457  			// clients.
  1458  			if _, ok := n.spendNotifications[spendRequest]; ok {
  1459  				notifyDetails(spendRequest, prevOut, uint32(i))
  1460  			}
  1461  			spendRequest.OutPoint = ZeroOutPoint
  1462  			if _, ok := n.spendNotifications[spendRequest]; ok {
  1463  				notifyDetails(spendRequest, prevOut, uint32(i))
  1464  			}
  1465  		}
  1466  	}
  1467  
  1468  	// We'll also check its outputs to determine if there are any
  1469  	// outstanding confirmation requests.
  1470  	if onConf != nil {
  1471  		// notifyDetails is a helper closure that will construct the
  1472  		// confirmation details of a request and hand them off to the
  1473  		// onConf callback.
  1474  		notifyDetails := func(confRequest ConfRequest) {
  1475  			Log.Debugf("Found initial confirmation of %v: "+
  1476  				"height=%d, hash=%v", confRequest,
  1477  				blockHeight, blockHash)
  1478  
  1479  			details := &TxConfirmation{
  1480  				Tx:          tx.MsgTx(),
  1481  				BlockHash:   blockHash,
  1482  				BlockHeight: blockHeight,
  1483  				TxIndex:     uint32(tx.Index()),
  1484  			}
  1485  
  1486  			onConf(confRequest, details)
  1487  		}
  1488  
  1489  		for _, txOut := range tx.MsgTx().TxOut {
  1490  			// We'll parse the script of the output to determine if
  1491  			// we have any registered requests for it or the
  1492  			// transaction itself.
  1493  			pkScript, err := chainscan.ParsePkScript(0, txOut.PkScript)
  1494  			if err != nil {
  1495  				continue
  1496  			}
  1497  			confRequest := ConfRequest{
  1498  				TxID:     *txHash,
  1499  				PkScript: pkScript,
  1500  			}
  1501  
  1502  			// If we have any, we'll record their confirmed height
  1503  			// so that notifications get dispatched when they
  1504  			// reaches the clients' desired number of confirmations.
  1505  			if _, ok := n.confNotifications[confRequest]; ok {
  1506  				notifyDetails(confRequest)
  1507  			}
  1508  			confRequest.TxID = ZeroHash
  1509  			if _, ok := n.confNotifications[confRequest]; ok {
  1510  				notifyDetails(confRequest)
  1511  			}
  1512  		}
  1513  	}
  1514  }
  1515  
  1516  // handleConfDetailsAtTip tracks the confirmation height of the txid/output
  1517  // script in order to properly dispatch a confirmation notification after
  1518  // meeting each request's desired number of confirmations for all current and
  1519  // future registered clients.
  1520  func (n *TxNotifier) handleConfDetailsAtTip(confRequest ConfRequest,
  1521  	details *TxConfirmation) {
  1522  
  1523  	// TODO(wilmer): cancel pending historical rescans if any?
  1524  	confSet := n.confNotifications[confRequest]
  1525  
  1526  	// If we already have details for this request, we don't want to add it
  1527  	// again since we have already dispatched notifications for it.
  1528  	if confSet.details != nil {
  1529  		Log.Warnf("Ignoring address reuse for %s at height %d.",
  1530  			confRequest, details.BlockHeight)
  1531  		return
  1532  	}
  1533  
  1534  	confSet.rescanStatus = rescanComplete
  1535  	confSet.details = details
  1536  
  1537  	for _, ntfn := range confSet.ntfns {
  1538  		// In the event that this notification was aware that the
  1539  		// transaction/output script was reorged out of the chain, we'll
  1540  		// consume the reorg notification if it hasn't been done yet
  1541  		// already.
  1542  		select {
  1543  		case <-ntfn.Event.NegativeConf:
  1544  		default:
  1545  		}
  1546  
  1547  		// We'll note this client's required number of confirmations so
  1548  		// that we can notify them when expected.
  1549  		confHeight := details.BlockHeight + ntfn.NumConfirmations - 1
  1550  		ntfnSet, exists := n.ntfnsByConfirmHeight[confHeight]
  1551  		if !exists {
  1552  			ntfnSet = make(map[*ConfNtfn]struct{})
  1553  			n.ntfnsByConfirmHeight[confHeight] = ntfnSet
  1554  		}
  1555  		ntfnSet[ntfn] = struct{}{}
  1556  	}
  1557  
  1558  	// We'll also note the initial confirmation height in order to correctly
  1559  	// handle dispatching notifications when the transaction/output script
  1560  	// gets reorged out of the chain.
  1561  	txSet, exists := n.confsByInitialHeight[details.BlockHeight]
  1562  	if !exists {
  1563  		txSet = make(map[ConfRequest]struct{})
  1564  		n.confsByInitialHeight[details.BlockHeight] = txSet
  1565  	}
  1566  	txSet[confRequest] = struct{}{}
  1567  }
  1568  
  1569  // handleSpendDetailsAtTip tracks the spend height of the outpoint/output script
  1570  // in order to properly dispatch a spend notification for all current and future
  1571  // registered clients.
  1572  func (n *TxNotifier) handleSpendDetailsAtTip(spendRequest SpendRequest,
  1573  	details *SpendDetail) {
  1574  
  1575  	// TODO(wilmer): cancel pending historical rescans if any?
  1576  	spendSet := n.spendNotifications[spendRequest]
  1577  	spendSet.rescanStatus = rescanComplete
  1578  	spendSet.details = details
  1579  
  1580  	for _, ntfn := range spendSet.ntfns {
  1581  		// In the event that this notification was aware that the
  1582  		// spending transaction of its outpoint/output script was
  1583  		// reorged out of the chain, we'll consume the reorg
  1584  		// notification if it hasn't been done yet already.
  1585  		select {
  1586  		case <-ntfn.Event.Reorg:
  1587  		default:
  1588  		}
  1589  	}
  1590  
  1591  	// We'll note the spending height of the request in order to correctly
  1592  	// handle dispatching notifications when the spending transactions gets
  1593  	// reorged out of the chain.
  1594  	spendHeight := uint32(details.SpendingHeight)
  1595  	opSet, exists := n.spendsByHeight[spendHeight]
  1596  	if !exists {
  1597  		opSet = make(map[SpendRequest]struct{})
  1598  		n.spendsByHeight[spendHeight] = opSet
  1599  	}
  1600  	opSet[spendRequest] = struct{}{}
  1601  
  1602  	Log.Debugf("Spend request %v spent at tip=%d", spendRequest,
  1603  		spendHeight)
  1604  }
  1605  
  1606  // NotifyHeight dispatches confirmation and spend notifications to the clients
  1607  // who registered for a notification which has been fulfilled at the passed
  1608  // height.
  1609  func (n *TxNotifier) NotifyHeight(height uint32) error {
  1610  	n.Lock()
  1611  	defer n.Unlock()
  1612  
  1613  	// First, we'll dispatch an update to all of the notification clients
  1614  	// for our watched requests with the number of confirmations left at
  1615  	// this new height.
  1616  	for _, confRequests := range n.confsByInitialHeight {
  1617  		for confRequest := range confRequests {
  1618  			confSet := n.confNotifications[confRequest]
  1619  			for _, ntfn := range confSet.ntfns {
  1620  				txConfHeight := confSet.details.BlockHeight +
  1621  					ntfn.NumConfirmations - 1
  1622  				numConfsLeft := txConfHeight - height
  1623  
  1624  				// Since we don't clear notifications until
  1625  				// transactions/output scripts are no longer
  1626  				// under the risk of being reorganized out of
  1627  				// the chain, we'll skip sending updates for
  1628  				// those that have already been confirmed.
  1629  				if int32(numConfsLeft) < 0 {
  1630  					continue
  1631  				}
  1632  
  1633  				// ConnectTip() and NotifyHeight are not atomic
  1634  				// and may race with historical searches, if a
  1635  				// search is slow. So it could happen that the
  1636  				// sema conf is found during the historical
  1637  				// search and the tip. Therefore, prevent sending
  1638  				// the updated if the conf has already been sent
  1639  				// or there are pending updates to be fetched.
  1640  				// This is triggered during the
  1641  				// historical_conf_dispatch_with_script_dispatch
  1642  				// test for the chainscan driver.
  1643  				if ntfn.dispatched || len(ntfn.Event.Updates) == cap(ntfn.Event.Updates) {
  1644  					continue
  1645  				}
  1646  
  1647  				select {
  1648  				case ntfn.Event.Updates <- numConfsLeft:
  1649  				case <-n.quit:
  1650  					return ErrTxNotifierExiting
  1651  				}
  1652  			}
  1653  		}
  1654  	}
  1655  
  1656  	// Then, we'll dispatch notifications for all the requests that have
  1657  	// become confirmed at this new block height.
  1658  	for ntfn := range n.ntfnsByConfirmHeight[height] {
  1659  		confSet := n.confNotifications[ntfn.ConfRequest]
  1660  
  1661  		Log.Infof("Dispatching %v confirmation notification for %v",
  1662  			ntfn.NumConfirmations, ntfn.ConfRequest)
  1663  
  1664  		select {
  1665  		case ntfn.Event.Confirmed <- confSet.details:
  1666  			ntfn.dispatched = true
  1667  		case <-n.quit:
  1668  			return ErrTxNotifierExiting
  1669  		}
  1670  	}
  1671  	delete(n.ntfnsByConfirmHeight, height)
  1672  
  1673  	// Finally, we'll dispatch spend notifications for all the requests that
  1674  	// were spent at this new block height.
  1675  	for spendRequest := range n.spendsByHeight[height] {
  1676  		spendSet := n.spendNotifications[spendRequest]
  1677  		for _, ntfn := range spendSet.ntfns {
  1678  			err := n.dispatchSpendDetails(ntfn, spendSet.details)
  1679  			if err != nil {
  1680  				return err
  1681  			}
  1682  		}
  1683  	}
  1684  
  1685  	return nil
  1686  }
  1687  
  1688  // DisconnectTip handles the tip of the current chain being disconnected during
  1689  // a chain reorganization. If any watched requests were included in this block,
  1690  // internal structures are updated to ensure confirmation/spend notifications
  1691  // are consumed (if not already), and reorg notifications are dispatched
  1692  // instead. Confirmation/spend notifications will be dispatched again upon block
  1693  // inclusion.
  1694  func (n *TxNotifier) DisconnectTip(blockHeight uint32) error {
  1695  	select {
  1696  	case <-n.quit:
  1697  		return ErrTxNotifierExiting
  1698  	default:
  1699  	}
  1700  
  1701  	n.Lock()
  1702  	defer n.Unlock()
  1703  
  1704  	if blockHeight != n.currentHeight {
  1705  		return fmt.Errorf("received blocks out of order: "+
  1706  			"current height=%d, disconnected height=%d",
  1707  			n.currentHeight, blockHeight)
  1708  	}
  1709  	n.currentHeight--
  1710  	n.reorgDepth++
  1711  
  1712  	// With the block disconnected, we'll update the confirm and spend hints
  1713  	// for our notification requests to reflect the new height, except for
  1714  	// those that have confirmed/spent at previous heights.
  1715  	n.updateHints(blockHeight)
  1716  
  1717  	// We'll go through all of our watched confirmation requests and attempt
  1718  	// to drain their notification channels to ensure sending notifications
  1719  	// to the clients is always non-blocking.
  1720  	for initialHeight, txHashes := range n.confsByInitialHeight {
  1721  		for txHash := range txHashes {
  1722  			// If the transaction/output script has been reorged out
  1723  			// of the chain, we'll make sure to remove the cached
  1724  			// confirmation details to prevent notifying clients
  1725  			// with old information.
  1726  			confSet := n.confNotifications[txHash]
  1727  			if initialHeight == blockHeight {
  1728  				confSet.details = nil
  1729  			}
  1730  
  1731  			for _, ntfn := range confSet.ntfns {
  1732  				// First, we'll attempt to drain an update
  1733  				// from each notification to ensure sends to the
  1734  				// Updates channel are always non-blocking.
  1735  				select {
  1736  				case <-ntfn.Event.Updates:
  1737  				case <-n.quit:
  1738  					return ErrTxNotifierExiting
  1739  				default:
  1740  				}
  1741  
  1742  				// Then, we'll check if the current
  1743  				// transaction/output script was included in the
  1744  				// block currently being disconnected. If it
  1745  				// was, we'll need to dispatch a reorg
  1746  				// notification to the client.
  1747  				if initialHeight == blockHeight {
  1748  					err := n.dispatchConfReorg(
  1749  						ntfn, blockHeight,
  1750  					)
  1751  					if err != nil {
  1752  						return err
  1753  					}
  1754  				}
  1755  			}
  1756  		}
  1757  	}
  1758  
  1759  	// We'll also go through our watched spend requests and attempt to drain
  1760  	// their dispatched notifications to ensure dispatching notifications to
  1761  	// clients later on is always non-blocking. We're only interested in
  1762  	// requests whose spending transaction was included at the height being
  1763  	// disconnected.
  1764  	for op := range n.spendsByHeight[blockHeight] {
  1765  		// Since the spending transaction is being reorged out of the
  1766  		// chain, we'll need to clear out the spending details of the
  1767  		// request.
  1768  		spendSet := n.spendNotifications[op]
  1769  		spendSet.details = nil
  1770  
  1771  		// For all requests which have had a spend notification
  1772  		// dispatched, we'll attempt to drain it and send a reorg
  1773  		// notification instead.
  1774  		for _, ntfn := range spendSet.ntfns {
  1775  			if err := n.dispatchSpendReorg(ntfn); err != nil {
  1776  				return err
  1777  			}
  1778  		}
  1779  	}
  1780  
  1781  	// Finally, we can remove the requests that were confirmed and/or spent
  1782  	// at the height being disconnected. We'll still continue to track them
  1783  	// until they have been confirmed/spent and are no longer under the risk
  1784  	// of being reorged out of the chain again.
  1785  	delete(n.confsByInitialHeight, blockHeight)
  1786  	delete(n.spendsByHeight, blockHeight)
  1787  
  1788  	return nil
  1789  }
  1790  
  1791  // updateHints attempts to update the confirm and spend hints for all relevant
  1792  // requests respectively. The height parameter is used to determine which
  1793  // requests we should update based on whether a new block is being
  1794  // connected/disconnected.
  1795  //
  1796  // NOTE: This must be called with the TxNotifier's lock held and after its
  1797  // height has already been reflected by a block being connected/disconnected.
  1798  func (n *TxNotifier) updateHints(height uint32) {
  1799  	// TODO(wilmer): update under one database transaction.
  1800  	//
  1801  	// To update the height hint for all the required confirmation requests
  1802  	// under one database transaction, we'll gather the set of unconfirmed
  1803  	// requests along with the ones that confirmed at the height being
  1804  	// connected/disconnected.
  1805  	confRequests := n.unconfirmedRequests()
  1806  	for confRequest := range n.confsByInitialHeight[height] {
  1807  		confRequests = append(confRequests, confRequest)
  1808  	}
  1809  	err := n.confirmHintCache.CommitConfirmHint(
  1810  		n.currentHeight, confRequests...,
  1811  	)
  1812  	if err != nil {
  1813  		// The error is not fatal as this is an optimistic optimization,
  1814  		// so we'll avoid returning an error.
  1815  		Log.Debugf("Unable to update confirm hints to %d for "+
  1816  			"%v: %v", n.currentHeight, confRequests, err)
  1817  	}
  1818  
  1819  	// Similarly, to update the height hint for all the required spend
  1820  	// requests under one database transaction, we'll gather the set of
  1821  	// unspent requests along with the ones that were spent at the height
  1822  	// being connected/disconnected.
  1823  	spendRequests := n.unspentRequests()
  1824  	for spendRequest := range n.spendsByHeight[height] {
  1825  		spendRequests = append(spendRequests, spendRequest)
  1826  	}
  1827  	err = n.spendHintCache.CommitSpendHint(n.currentHeight, spendRequests...)
  1828  	if err != nil {
  1829  		// The error is not fatal as this is an optimistic optimization,
  1830  		// so we'll avoid returning an error.
  1831  		Log.Debugf("Unable to update spend hints to %d for "+
  1832  			"%v: %v", n.currentHeight, spendRequests, err)
  1833  	}
  1834  }
  1835  
  1836  // unconfirmedRequests returns the set of confirmation requests that are
  1837  // still seen as unconfirmed by the TxNotifier.
  1838  //
  1839  // NOTE: This method must be called with the TxNotifier's lock held.
  1840  func (n *TxNotifier) unconfirmedRequests() []ConfRequest {
  1841  	var unconfirmed []ConfRequest
  1842  	for confRequest, confNtfnSet := range n.confNotifications {
  1843  		// If the notification is already aware of its confirmation
  1844  		// details, or it's in the process of learning them, we'll skip
  1845  		// it as we can't yet determine if it's confirmed or not.
  1846  		if confNtfnSet.rescanStatus != rescanComplete ||
  1847  			confNtfnSet.details != nil {
  1848  			continue
  1849  		}
  1850  
  1851  		unconfirmed = append(unconfirmed, confRequest)
  1852  	}
  1853  
  1854  	return unconfirmed
  1855  }
  1856  
  1857  // unspentRequests returns the set of spend requests that are still seen as
  1858  // unspent by the TxNotifier.
  1859  //
  1860  // NOTE: This method must be called with the TxNotifier's lock held.
  1861  func (n *TxNotifier) unspentRequests() []SpendRequest {
  1862  	var unspent []SpendRequest
  1863  	for spendRequest, spendNtfnSet := range n.spendNotifications {
  1864  		// If the notification is already aware of its spend details, or
  1865  		// it's in the process of learning them, we'll skip it as we
  1866  		// can't yet determine if it's unspent or not.
  1867  		if spendNtfnSet.rescanStatus != rescanComplete ||
  1868  			spendNtfnSet.details != nil {
  1869  			continue
  1870  		}
  1871  
  1872  		unspent = append(unspent, spendRequest)
  1873  	}
  1874  
  1875  	return unspent
  1876  }
  1877  
  1878  // dispatchConfReorg dispatches a reorg notification to the client if the
  1879  // confirmation notification was already delivered.
  1880  //
  1881  // NOTE: This must be called with the TxNotifier's lock held.
  1882  func (n *TxNotifier) dispatchConfReorg(ntfn *ConfNtfn,
  1883  	heightDisconnected uint32) error {
  1884  
  1885  	// If the request's confirmation notification has yet to be dispatched,
  1886  	// we'll need to clear its entry within the ntfnsByConfirmHeight index
  1887  	// to prevent from notifying the client once the notifier reaches the
  1888  	// confirmation height.
  1889  	if !ntfn.dispatched {
  1890  		confHeight := heightDisconnected + ntfn.NumConfirmations - 1
  1891  		ntfnSet, exists := n.ntfnsByConfirmHeight[confHeight]
  1892  		if exists {
  1893  			delete(ntfnSet, ntfn)
  1894  		}
  1895  		return nil
  1896  	}
  1897  
  1898  	// Otherwise, the entry within the ntfnsByConfirmHeight has already been
  1899  	// deleted, so we'll attempt to drain the confirmation notification to
  1900  	// ensure sends to the Confirmed channel are always non-blocking.
  1901  	select {
  1902  	case <-ntfn.Event.Confirmed:
  1903  	case <-n.quit:
  1904  		return ErrTxNotifierExiting
  1905  	default:
  1906  	}
  1907  
  1908  	ntfn.dispatched = false
  1909  
  1910  	// Send a negative confirmation notification to the client indicating
  1911  	// how many blocks have been disconnected successively.
  1912  	select {
  1913  	case ntfn.Event.NegativeConf <- int32(n.reorgDepth):
  1914  	case <-n.quit:
  1915  		return ErrTxNotifierExiting
  1916  	}
  1917  
  1918  	return nil
  1919  }
  1920  
  1921  // dispatchSpendReorg dispatches a reorg notification to the client if a spend
  1922  // notiification was already delivered.
  1923  //
  1924  // NOTE: This must be called with the TxNotifier's lock held.
  1925  func (n *TxNotifier) dispatchSpendReorg(ntfn *SpendNtfn) error {
  1926  	if !ntfn.dispatched {
  1927  		return nil
  1928  	}
  1929  
  1930  	// Attempt to drain the spend notification to ensure sends to the Spend
  1931  	// channel are always non-blocking.
  1932  	select {
  1933  	case <-ntfn.Event.Spend:
  1934  	default:
  1935  	}
  1936  
  1937  	// Send a reorg notification to the client in order for them to
  1938  	// correctly handle reorgs.
  1939  	select {
  1940  	case ntfn.Event.Reorg <- struct{}{}:
  1941  	case <-n.quit:
  1942  		return ErrTxNotifierExiting
  1943  	}
  1944  
  1945  	ntfn.dispatched = false
  1946  
  1947  	return nil
  1948  }
  1949  
  1950  // TearDown is to be called when the owner of the TxNotifier is exiting. This
  1951  // closes the event channels of all registered notifications that have not been
  1952  // dispatched yet.
  1953  func (n *TxNotifier) TearDown() {
  1954  	close(n.quit)
  1955  
  1956  	n.Lock()
  1957  	defer n.Unlock()
  1958  
  1959  	for _, confSet := range n.confNotifications {
  1960  		for confID, ntfn := range confSet.ntfns {
  1961  			close(ntfn.Event.Confirmed)
  1962  			close(ntfn.Event.Updates)
  1963  			close(ntfn.Event.NegativeConf)
  1964  			close(ntfn.Event.Done)
  1965  			delete(confSet.ntfns, confID)
  1966  		}
  1967  	}
  1968  
  1969  	for _, spendSet := range n.spendNotifications {
  1970  		for spendID, ntfn := range spendSet.ntfns {
  1971  			close(ntfn.Event.Spend)
  1972  			close(ntfn.Event.Reorg)
  1973  			close(ntfn.Event.Done)
  1974  			delete(spendSet.ntfns, spendID)
  1975  		}
  1976  	}
  1977  }