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

     1  package chainntnfs_test
     2  
     3  import (
     4  	"bytes"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/decred/dcrd/chaincfg/chainhash"
    10  	"github.com/decred/dcrd/chaincfg/v3"
    11  	"github.com/decred/dcrd/dcrutil/v4"
    12  	"github.com/decred/dcrd/wire"
    13  	"github.com/decred/dcrlnd/chainntnfs"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  var (
    18  	testRawScript = []byte{
    19  		// OP_HASH160
    20  		0xa9,
    21  		// OP_DATA_20
    22  		0x14,
    23  		// <20-byte script hash>
    24  		0x04, 0xb2, 0xdc, 0xc1, 0x14, 0xbf, 0xd3, 0xe1,
    25  		0xab, 0x15, 0xfe, 0x5e, 0x54, 0xcd, 0x40, 0x6f,
    26  		0xa4, 0x6d, 0xb9, 0x1a,
    27  		// OP_EQUAL
    28  		0x87,
    29  	}
    30  	testSigScript = []byte{
    31  		// OP_DATA_16
    32  		0x16,
    33  		// <22-byte redeem script>
    34  		0x00, 0x14, 0x1d, 0x7c, 0xd6, 0xc7, 0x5c, 0x2e,
    35  		0x86, 0xf4, 0xcb, 0xf9, 0x8e, 0xae, 0xd2, 0x21,
    36  		0xb3, 0x0b, 0xd9, 0xa0, 0xb9, 0x28,
    37  	}
    38  	testChainParams = chaincfg.RegNetParams()
    39  )
    40  
    41  type mockHintCache struct {
    42  	mu         sync.Mutex
    43  	confHints  map[chainntnfs.ConfRequest]uint32
    44  	spendHints map[chainntnfs.SpendRequest]uint32
    45  }
    46  
    47  var _ chainntnfs.SpendHintCache = (*mockHintCache)(nil)
    48  var _ chainntnfs.ConfirmHintCache = (*mockHintCache)(nil)
    49  
    50  func (c *mockHintCache) CommitSpendHint(heightHint uint32,
    51  	spendRequests ...chainntnfs.SpendRequest) error {
    52  
    53  	c.mu.Lock()
    54  	defer c.mu.Unlock()
    55  
    56  	for _, spendRequest := range spendRequests {
    57  		c.spendHints[spendRequest] = heightHint
    58  	}
    59  
    60  	return nil
    61  }
    62  
    63  func (c *mockHintCache) QuerySpendHint(spendRequest chainntnfs.SpendRequest) (uint32, error) {
    64  	c.mu.Lock()
    65  	defer c.mu.Unlock()
    66  
    67  	hint, ok := c.spendHints[spendRequest]
    68  	if !ok {
    69  		return 0, chainntnfs.ErrSpendHintNotFound
    70  	}
    71  
    72  	return hint, nil
    73  }
    74  
    75  func (c *mockHintCache) PurgeSpendHint(spendRequests ...chainntnfs.SpendRequest) error {
    76  	c.mu.Lock()
    77  	defer c.mu.Unlock()
    78  
    79  	for _, spendRequest := range spendRequests {
    80  		delete(c.spendHints, spendRequest)
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  func (c *mockHintCache) CommitConfirmHint(heightHint uint32,
    87  	confRequests ...chainntnfs.ConfRequest) error {
    88  
    89  	c.mu.Lock()
    90  	defer c.mu.Unlock()
    91  
    92  	for _, confRequest := range confRequests {
    93  		c.confHints[confRequest] = heightHint
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func (c *mockHintCache) QueryConfirmHint(confRequest chainntnfs.ConfRequest) (uint32, error) {
   100  	c.mu.Lock()
   101  	defer c.mu.Unlock()
   102  
   103  	hint, ok := c.confHints[confRequest]
   104  	if !ok {
   105  		return 0, chainntnfs.ErrConfirmHintNotFound
   106  	}
   107  
   108  	return hint, nil
   109  }
   110  
   111  func (c *mockHintCache) PurgeConfirmHint(confRequests ...chainntnfs.ConfRequest) error {
   112  	c.mu.Lock()
   113  	defer c.mu.Unlock()
   114  
   115  	for _, confRequest := range confRequests {
   116  		delete(c.confHints, confRequest)
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  func newMockHintCache() *mockHintCache {
   123  	return &mockHintCache{
   124  		confHints:  make(map[chainntnfs.ConfRequest]uint32),
   125  		spendHints: make(map[chainntnfs.SpendRequest]uint32),
   126  	}
   127  }
   128  
   129  // newWireTxWithVersion returns a new wire transaction with a full
   130  // serialization type and the provided transaction version set.
   131  func newWireTxWithVersion(version uint16) *wire.MsgTx {
   132  	tx := wire.NewMsgTx()
   133  	tx.Version = version
   134  	return tx
   135  }
   136  
   137  // TestTxNotifierRegistrationValidation ensures that we are not able to
   138  // register requests with invalid parameters.
   139  func TestTxNotifierRegistrationValidation(t *testing.T) {
   140  	t.Parallel()
   141  
   142  	testCases := []struct {
   143  		name       string
   144  		pkScript   []byte
   145  		numConfs   uint32
   146  		heightHint uint32
   147  		checkSpend bool
   148  		err        error
   149  	}{
   150  		{
   151  			name:       "empty output script",
   152  			pkScript:   nil,
   153  			numConfs:   1,
   154  			heightHint: 1,
   155  			checkSpend: true,
   156  			err:        chainntnfs.ErrNoScript,
   157  		},
   158  		{
   159  			name:       "zero num confs",
   160  			pkScript:   testRawScript,
   161  			numConfs:   0,
   162  			heightHint: 1,
   163  			err:        chainntnfs.ErrNumConfsOutOfRange,
   164  		},
   165  		{
   166  			name:       "exceed max num confs",
   167  			pkScript:   testRawScript,
   168  			numConfs:   chainntnfs.MaxNumConfs + 1,
   169  			heightHint: 1,
   170  			err:        chainntnfs.ErrNumConfsOutOfRange,
   171  		},
   172  		{
   173  			name:       "empty height hint",
   174  			pkScript:   testRawScript,
   175  			numConfs:   1,
   176  			heightHint: 0,
   177  			checkSpend: true,
   178  			err:        chainntnfs.ErrNoHeightHint,
   179  		},
   180  	}
   181  
   182  	for _, testCase := range testCases {
   183  		testCase := testCase
   184  		t.Run(testCase.name, func(t *testing.T) {
   185  			hintCache := newMockHintCache()
   186  			n := chainntnfs.NewTxNotifier(
   187  				10, chainntnfs.ReorgSafetyLimit, hintCache,
   188  				hintCache, testChainParams,
   189  			)
   190  
   191  			_, err := n.RegisterConf(
   192  				&chainntnfs.ZeroHash, testCase.pkScript,
   193  				testCase.numConfs, testCase.heightHint,
   194  			)
   195  			if err != testCase.err {
   196  				t.Fatalf("conf registration expected error "+
   197  					"\"%v\", got \"%v\"", testCase.err, err)
   198  			}
   199  
   200  			if !testCase.checkSpend {
   201  				return
   202  			}
   203  
   204  			_, err = n.RegisterSpend(
   205  				&chainntnfs.ZeroOutPoint, testCase.pkScript,
   206  				testCase.heightHint,
   207  			)
   208  			if err != testCase.err {
   209  				t.Fatalf("spend registration expected error "+
   210  					"\"%v\", got \"%v\"", testCase.err, err)
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  // TestTxNotifierFutureConfDispatch tests that the TxNotifier dispatches
   217  // registered notifications when a transaction confirms after registration.
   218  func TestTxNotifierFutureConfDispatch(t *testing.T) {
   219  	t.Parallel()
   220  
   221  	const (
   222  		tx1NumConfs uint32 = 1
   223  		tx2NumConfs uint32 = 2
   224  	)
   225  
   226  	hintCache := newMockHintCache()
   227  	n := chainntnfs.NewTxNotifier(
   228  		10, chainntnfs.ReorgSafetyLimit, hintCache, hintCache,
   229  		testChainParams,
   230  	)
   231  
   232  	// Create the test transactions and register them with the TxNotifier
   233  	// before including them in a block to receive future
   234  	// notifications.
   235  	tx1 := *newWireTxWithVersion(1)
   236  	tx1.AddTxOut(&wire.TxOut{PkScript: testRawScript})
   237  	tx1Hash := tx1.TxHash()
   238  	ntfn1, err := n.RegisterConf(&tx1Hash, testRawScript, tx1NumConfs, 1)
   239  	if err != nil {
   240  		t.Fatalf("unable to register ntfn: %v", err)
   241  	}
   242  
   243  	tx2 := *newWireTxWithVersion(2)
   244  	tx2.AddTxOut(&wire.TxOut{PkScript: testRawScript})
   245  	tx2Hash := tx2.TxHash()
   246  	ntfn2, err := n.RegisterConf(&tx2Hash, testRawScript, tx2NumConfs, 1)
   247  	if err != nil {
   248  		t.Fatalf("unable to register ntfn: %v", err)
   249  	}
   250  
   251  	// We should not receive any notifications from both transactions
   252  	// since they have not been included in a block yet.
   253  	select {
   254  	case <-ntfn1.Event.Updates:
   255  		t.Fatal("Received unexpected confirmation update for tx1")
   256  	case txConf := <-ntfn1.Event.Confirmed:
   257  		t.Fatalf("Received unexpected confirmation for tx1: %v", txConf)
   258  	default:
   259  	}
   260  
   261  	select {
   262  	case <-ntfn2.Event.Updates:
   263  		t.Fatal("Received unexpected confirmation update for tx2")
   264  	case txConf := <-ntfn2.Event.Confirmed:
   265  		t.Fatalf("Received unexpected confirmation for tx2: %v", txConf)
   266  	default:
   267  	}
   268  
   269  	// Include the transactions in a block and add it to the TxNotifier.
   270  	// This should confirm tx1, but not tx2.
   271  	block1 := dcrutil.NewBlock(&wire.MsgBlock{
   272  		Transactions: []*wire.MsgTx{&tx1, &tx2},
   273  	})
   274  
   275  	err = n.ConnectTip(block1.Hash(), 11, block1.Transactions())
   276  	if err != nil {
   277  		t.Fatalf("Failed to connect block: %v", err)
   278  	}
   279  	if err := n.NotifyHeight(11); err != nil {
   280  		t.Fatalf("unable to dispatch notifications: %v", err)
   281  	}
   282  
   283  	// We should only receive one update for tx1 since it only requires
   284  	// one confirmation and it already met it.
   285  	select {
   286  	case numConfsLeft := <-ntfn1.Event.Updates:
   287  		const expected = 0
   288  		if numConfsLeft != expected {
   289  			t.Fatalf("Received incorrect confirmation update: tx1 "+
   290  				"expected %d confirmations left, got %d",
   291  				expected, numConfsLeft)
   292  		}
   293  	default:
   294  		t.Fatal("Expected confirmation update for tx1")
   295  	}
   296  
   297  	// A confirmation notification for this tranaction should be dispatched,
   298  	// as it only required one confirmation.
   299  	select {
   300  	case txConf := <-ntfn1.Event.Confirmed:
   301  		expectedConf := chainntnfs.TxConfirmation{
   302  			BlockHash:   block1.Hash(),
   303  			BlockHeight: 11,
   304  			TxIndex:     0,
   305  			Tx:          &tx1,
   306  		}
   307  		assertConfDetails(t, txConf, &expectedConf)
   308  	default:
   309  		t.Fatalf("Expected confirmation for tx1")
   310  	}
   311  
   312  	// We should only receive one update for tx2 since it only has one
   313  	// confirmation so far and it requires two.
   314  	select {
   315  	case numConfsLeft := <-ntfn2.Event.Updates:
   316  		const expected = 1
   317  		if numConfsLeft != expected {
   318  			t.Fatalf("Received incorrect confirmation update: tx2 "+
   319  				"expected %d confirmations left, got %d",
   320  				expected, numConfsLeft)
   321  		}
   322  	default:
   323  		t.Fatal("Expected confirmation update for tx2")
   324  	}
   325  
   326  	// A confirmation notification for tx2 should not be dispatched yet, as
   327  	// it requires one more confirmation.
   328  	select {
   329  	case txConf := <-ntfn2.Event.Confirmed:
   330  		t.Fatalf("Received unexpected confirmation for tx2: %v", txConf)
   331  	default:
   332  	}
   333  
   334  	// Create a new block and add it to the TxNotifier at the next height.
   335  	// This should confirm tx2.
   336  	block2 := dcrutil.NewBlock(&wire.MsgBlock{})
   337  	err = n.ConnectTip(block2.Hash(), 12, block2.Transactions())
   338  	if err != nil {
   339  		t.Fatalf("Failed to connect block: %v", err)
   340  	}
   341  	if err := n.NotifyHeight(12); err != nil {
   342  		t.Fatalf("unable to dispatch notifications: %v", err)
   343  	}
   344  
   345  	// We should not receive any event notifications for tx1 since it has
   346  	// already been confirmed.
   347  	select {
   348  	case <-ntfn1.Event.Updates:
   349  		t.Fatal("Received unexpected confirmation update for tx1")
   350  	case txConf := <-ntfn1.Event.Confirmed:
   351  		t.Fatalf("Received unexpected confirmation for tx1: %v", txConf)
   352  	default:
   353  	}
   354  
   355  	// We should only receive one update since the last at the new height,
   356  	// indicating how many confirmations are still left.
   357  	select {
   358  	case numConfsLeft := <-ntfn2.Event.Updates:
   359  		const expected = 0
   360  		if numConfsLeft != expected {
   361  			t.Fatalf("Received incorrect confirmation update: tx2 "+
   362  				"expected %d confirmations left, got %d",
   363  				expected, numConfsLeft)
   364  		}
   365  	default:
   366  		t.Fatal("Expected confirmation update for tx2")
   367  	}
   368  
   369  	// A confirmation notification for tx2 should be dispatched, since it
   370  	// now meets its required number of confirmations.
   371  	select {
   372  	case txConf := <-ntfn2.Event.Confirmed:
   373  		expectedConf := chainntnfs.TxConfirmation{
   374  			BlockHash:   block1.Hash(),
   375  			BlockHeight: 11,
   376  			TxIndex:     1,
   377  			Tx:          &tx2,
   378  		}
   379  		assertConfDetails(t, txConf, &expectedConf)
   380  	default:
   381  		t.Fatalf("Expected confirmation for tx2")
   382  	}
   383  }
   384  
   385  // TestTxNotifierHistoricalConfDispatch tests that the TxNotifier dispatches
   386  // registered notifications when the transaction is confirmed before
   387  // registration.
   388  func TestTxNotifierHistoricalConfDispatch(t *testing.T) {
   389  	t.Parallel()
   390  
   391  	const (
   392  		tx1NumConfs uint32 = 1
   393  		tx2NumConfs uint32 = 3
   394  	)
   395  
   396  	var (
   397  		tx1 = *newWireTxWithVersion(1)
   398  		tx2 = *newWireTxWithVersion(2)
   399  		tx3 = *newWireTxWithVersion(3)
   400  	)
   401  
   402  	hintCache := newMockHintCache()
   403  	n := chainntnfs.NewTxNotifier(
   404  		10, chainntnfs.ReorgSafetyLimit, hintCache, hintCache,
   405  		testChainParams,
   406  	)
   407  
   408  	// Create the test transactions at a height before the TxNotifier's
   409  	// starting height so that they are confirmed once registering them.
   410  	tx1Hash := tx1.TxHash()
   411  	ntfn1, err := n.RegisterConf(&tx1Hash, testRawScript, tx1NumConfs, 1)
   412  	if err != nil {
   413  		t.Fatalf("unable to register ntfn: %v", err)
   414  	}
   415  
   416  	tx2Hash := tx2.TxHash()
   417  	ntfn2, err := n.RegisterConf(&tx2Hash, testRawScript, tx2NumConfs, 1)
   418  	if err != nil {
   419  		t.Fatalf("unable to register ntfn: %v", err)
   420  	}
   421  
   422  	// Update tx1 with its confirmation details. We should only receive one
   423  	// update since it only requires one confirmation and it already met it.
   424  	txConf1 := chainntnfs.TxConfirmation{
   425  		BlockHash:   &chainntnfs.ZeroHash,
   426  		BlockHeight: 9,
   427  		TxIndex:     1,
   428  		Tx:          &tx1,
   429  	}
   430  	err = n.UpdateConfDetails(ntfn1.HistoricalDispatch.ConfRequest, &txConf1)
   431  	if err != nil {
   432  		t.Fatalf("unable to update conf details: %v", err)
   433  	}
   434  	select {
   435  	case numConfsLeft := <-ntfn1.Event.Updates:
   436  		const expected = 0
   437  		if numConfsLeft != expected {
   438  			t.Fatalf("Received incorrect confirmation update: tx1 "+
   439  				"expected %d confirmations left, got %d",
   440  				expected, numConfsLeft)
   441  		}
   442  	default:
   443  		t.Fatal("Expected confirmation update for tx1")
   444  	}
   445  
   446  	// A confirmation notification for tx1 should also be dispatched.
   447  	select {
   448  	case txConf := <-ntfn1.Event.Confirmed:
   449  		assertConfDetails(t, txConf, &txConf1)
   450  	default:
   451  		t.Fatalf("Expected confirmation for tx1")
   452  	}
   453  
   454  	// Update tx2 with its confirmation details. This should not trigger a
   455  	// confirmation notification since it hasn't reached its required number
   456  	// of confirmations, but we should receive a confirmation update
   457  	// indicating how many confirmation are left.
   458  	txConf2 := chainntnfs.TxConfirmation{
   459  		BlockHash:   &chainntnfs.ZeroHash,
   460  		BlockHeight: 9,
   461  		TxIndex:     2,
   462  		Tx:          &tx2,
   463  	}
   464  	err = n.UpdateConfDetails(ntfn2.HistoricalDispatch.ConfRequest, &txConf2)
   465  	if err != nil {
   466  		t.Fatalf("unable to update conf details: %v", err)
   467  	}
   468  	select {
   469  	case numConfsLeft := <-ntfn2.Event.Updates:
   470  		const expected = 1
   471  		if numConfsLeft != expected {
   472  			t.Fatalf("Received incorrect confirmation update: tx2 "+
   473  				"expected %d confirmations left, got %d",
   474  				expected, numConfsLeft)
   475  		}
   476  	default:
   477  		t.Fatal("Expected confirmation update for tx2")
   478  	}
   479  
   480  	select {
   481  	case txConf := <-ntfn2.Event.Confirmed:
   482  		t.Fatalf("Received unexpected confirmation for tx2: %v", txConf)
   483  	default:
   484  	}
   485  
   486  	// Create a new block and add it to the TxNotifier at the next height.
   487  	// This should confirm tx2.
   488  	block := dcrutil.NewBlock(&wire.MsgBlock{
   489  		Transactions: []*wire.MsgTx{&tx3},
   490  	})
   491  
   492  	err = n.ConnectTip(block.Hash(), 11, block.Transactions())
   493  	if err != nil {
   494  		t.Fatalf("Failed to connect block: %v", err)
   495  	}
   496  	if err := n.NotifyHeight(11); err != nil {
   497  		t.Fatalf("unable to dispatch notifications: %v", err)
   498  	}
   499  
   500  	// We should not receive any event notifications for tx1 since it has
   501  	// already been confirmed.
   502  	select {
   503  	case <-ntfn1.Event.Updates:
   504  		t.Fatal("Received unexpected confirmation update for tx1")
   505  	case txConf := <-ntfn1.Event.Confirmed:
   506  		t.Fatalf("Received unexpected confirmation for tx1: %v", txConf)
   507  	default:
   508  	}
   509  
   510  	// We should only receive one update for tx2 since the last one,
   511  	// indicating how many confirmations are still left.
   512  	select {
   513  	case numConfsLeft := <-ntfn2.Event.Updates:
   514  		const expected = 0
   515  		if numConfsLeft != expected {
   516  			t.Fatalf("Received incorrect confirmation update: tx2 "+
   517  				"expected %d confirmations left, got %d",
   518  				expected, numConfsLeft)
   519  		}
   520  	default:
   521  		t.Fatal("Expected confirmation update for tx2")
   522  	}
   523  
   524  	// A confirmation notification for tx2 should be dispatched, as it met
   525  	// its required number of confirmations.
   526  	select {
   527  	case txConf := <-ntfn2.Event.Confirmed:
   528  		assertConfDetails(t, txConf, &txConf2)
   529  	default:
   530  		t.Fatalf("Expected confirmation for tx2")
   531  	}
   532  }
   533  
   534  // TestTxNotifierFutureSpendDispatch tests that the TxNotifier dispatches
   535  // registered notifications when an outpoint is spent after registration.
   536  func TestTxNotifierFutureSpendDispatch(t *testing.T) {
   537  	t.Parallel()
   538  
   539  	hintCache := newMockHintCache()
   540  	n := chainntnfs.NewTxNotifier(
   541  		10, chainntnfs.ReorgSafetyLimit, hintCache, hintCache,
   542  		testChainParams,
   543  	)
   544  
   545  	// We'll start off by registering for a spend notification of an
   546  	// outpoint.
   547  	op := wire.OutPoint{Index: 1}
   548  	ntfn, err := n.RegisterSpend(&op, testRawScript, 1)
   549  	if err != nil {
   550  		t.Fatalf("unable to register spend ntfn: %v", err)
   551  	}
   552  
   553  	// We should not receive a notification as the outpoint has not been
   554  	// spent yet.
   555  	select {
   556  	case <-ntfn.Event.Spend:
   557  		t.Fatal("received unexpected spend notification")
   558  	default:
   559  	}
   560  
   561  	// Construct the details of the spending transaction of the outpoint
   562  	// above. We'll include it in the next block, which should trigger a
   563  	// spend notification.
   564  	spendTx := newWireTxWithVersion(2)
   565  	spendTx.AddTxIn(&wire.TxIn{
   566  		PreviousOutPoint: op,
   567  		SignatureScript:  testSigScript,
   568  	})
   569  	spendTxHash := spendTx.TxHash()
   570  	block := dcrutil.NewBlock(&wire.MsgBlock{
   571  		Transactions: []*wire.MsgTx{spendTx},
   572  	})
   573  	err = n.ConnectTip(block.Hash(), 11, block.Transactions())
   574  	if err != nil {
   575  		t.Fatalf("unable to connect block: %v", err)
   576  	}
   577  	if err := n.NotifyHeight(11); err != nil {
   578  		t.Fatalf("unable to dispatch notifications: %v", err)
   579  	}
   580  
   581  	expectedSpendDetails := &chainntnfs.SpendDetail{
   582  		SpentOutPoint:     &op,
   583  		SpenderTxHash:     &spendTxHash,
   584  		SpendingTx:        spendTx,
   585  		SpenderInputIndex: 0,
   586  		SpendingHeight:    11,
   587  	}
   588  
   589  	// Ensure that the details of the notification match as expected.
   590  	select {
   591  	case spendDetails := <-ntfn.Event.Spend:
   592  		assertSpendDetails(t, spendDetails, expectedSpendDetails)
   593  	default:
   594  		t.Fatal("expected to receive spend details")
   595  	}
   596  
   597  	// Finally, we'll ensure that if the spending transaction has also been
   598  	// spent, then we don't receive another spend notification.
   599  	prevOut := wire.OutPoint{Hash: spendTxHash, Index: 0}
   600  	spendOfSpend := newWireTxWithVersion(2)
   601  	spendOfSpend.AddTxIn(&wire.TxIn{
   602  		PreviousOutPoint: prevOut,
   603  		SignatureScript:  testSigScript,
   604  	})
   605  	block = dcrutil.NewBlock(&wire.MsgBlock{
   606  		Transactions: []*wire.MsgTx{spendOfSpend},
   607  	})
   608  	err = n.ConnectTip(block.Hash(), 12, block.Transactions())
   609  	if err != nil {
   610  		t.Fatalf("unable to connect block: %v", err)
   611  	}
   612  	if err := n.NotifyHeight(12); err != nil {
   613  		t.Fatalf("unable to dispatch notifications: %v", err)
   614  	}
   615  
   616  	select {
   617  	case <-ntfn.Event.Spend:
   618  		t.Fatal("received unexpected spend notification")
   619  	default:
   620  	}
   621  }
   622  
   623  // TestTxNotifierFutureConfDispatchReuseSafe tests that the notifier does not
   624  // misbehave even if two confirmation requests for the same script are issued
   625  // at different block heights (which means funds are being sent to the same
   626  // script multiple times).
   627  func TestTxNotifierFutureConfDispatchReuseSafe(t *testing.T) {
   628  	t.Parallel()
   629  
   630  	currentBlock := uint32(10)
   631  	hintCache := newMockHintCache()
   632  	n := chainntnfs.NewTxNotifier(
   633  		currentBlock, 2, hintCache, hintCache,
   634  		testChainParams,
   635  	)
   636  
   637  	// We'll register a TX that sends to our test script and put it into a
   638  	// block. Additionally we register a notification request for just the
   639  	// script which should also be confirmed with that block.
   640  	tx1 := wire.MsgTx{Version: 1}
   641  	tx1.AddTxOut(&wire.TxOut{PkScript: testRawScript})
   642  	tx1Hash := tx1.TxHash()
   643  	ntfn1, err := n.RegisterConf(&tx1Hash, testRawScript, 1, 1)
   644  	if err != nil {
   645  		t.Fatalf("unable to register ntfn: %v", err)
   646  	}
   647  	scriptNtfn1, err := n.RegisterConf(nil, testRawScript, 1, 1)
   648  	if err != nil {
   649  		t.Fatalf("unable to register ntfn: %v", err)
   650  	}
   651  	block := dcrutil.NewBlock(&wire.MsgBlock{
   652  		Transactions: []*wire.MsgTx{&tx1},
   653  	})
   654  	currentBlock++
   655  	err = n.ConnectTip(block.Hash(), currentBlock, block.Transactions())
   656  	if err != nil {
   657  		t.Fatalf("unable to connect block: %v", err)
   658  	}
   659  	if err := n.NotifyHeight(currentBlock); err != nil {
   660  		t.Fatalf("unable to dispatch notifications: %v", err)
   661  	}
   662  
   663  	// Expect an update and confirmation of TX 1 at this point. We save the
   664  	// confirmation details because we expect to receive the same details
   665  	// for all further registrations.
   666  	var confDetails *chainntnfs.TxConfirmation
   667  	select {
   668  	case <-ntfn1.Event.Updates:
   669  	default:
   670  		t.Fatal("expected update of TX 1")
   671  	}
   672  	select {
   673  	case confDetails = <-ntfn1.Event.Confirmed:
   674  		if confDetails.BlockHeight != currentBlock {
   675  			t.Fatalf("expected TX to be confirmed in latest block")
   676  		}
   677  	default:
   678  		t.Fatal("expected confirmation of TX 1")
   679  	}
   680  
   681  	// The notification for the script should also have received a
   682  	// confirmation.
   683  	select {
   684  	case <-scriptNtfn1.Event.Updates:
   685  	default:
   686  		t.Fatal("expected update of script ntfn")
   687  	}
   688  	select {
   689  	case details := <-scriptNtfn1.Event.Confirmed:
   690  		assertConfDetails(t, details, confDetails)
   691  	default:
   692  		t.Fatal("expected update of script ntfn")
   693  	}
   694  
   695  	// Now register a second TX that spends to two outputs with the same
   696  	// script so we have a different TXID. And again register a confirmation
   697  	// for just the script.
   698  	tx2 := wire.MsgTx{Version: 1}
   699  	tx2.AddTxOut(&wire.TxOut{PkScript: testRawScript})
   700  	tx2.AddTxOut(&wire.TxOut{PkScript: testRawScript})
   701  	tx2Hash := tx2.TxHash()
   702  	ntfn2, err := n.RegisterConf(&tx2Hash, testRawScript, 1, 1)
   703  	if err != nil {
   704  		t.Fatalf("unable to register ntfn: %v", err)
   705  	}
   706  	scriptNtfn2, err := n.RegisterConf(nil, testRawScript, 1, 1)
   707  	if err != nil {
   708  		t.Fatalf("unable to register ntfn: %v", err)
   709  	}
   710  	block2 := dcrutil.NewBlock(&wire.MsgBlock{
   711  		Transactions: []*wire.MsgTx{&tx2},
   712  	})
   713  	currentBlock++
   714  	err = n.ConnectTip(block2.Hash(), currentBlock, block2.Transactions())
   715  	if err != nil {
   716  		t.Fatalf("unable to connect block: %v", err)
   717  	}
   718  	if err := n.NotifyHeight(currentBlock); err != nil {
   719  		t.Fatalf("unable to dispatch notifications: %v", err)
   720  	}
   721  
   722  	// Transaction 2 should get a confirmation here too. Since it was
   723  	// a different TXID we wouldn't get the cached details here but the TX
   724  	// should be confirmed right away still.
   725  	select {
   726  	case <-ntfn2.Event.Updates:
   727  	default:
   728  		t.Fatal("expected update of TX 2")
   729  	}
   730  	select {
   731  	case details := <-ntfn2.Event.Confirmed:
   732  		if details.BlockHeight != currentBlock {
   733  			t.Fatalf("expected TX to be confirmed in latest block")
   734  		}
   735  	default:
   736  		t.Fatal("expected update of TX 2")
   737  	}
   738  
   739  	// The second notification for the script should also have received a
   740  	// confirmation. Since it's the same script, we expect to get the cached
   741  	// details from the first TX back immediately. Nothing should be
   742  	// registered at the notifier for the current block height for that
   743  	// script any more.
   744  	select {
   745  	case <-scriptNtfn2.Event.Updates:
   746  	default:
   747  		t.Fatal("expected update of script ntfn")
   748  	}
   749  	select {
   750  	case details := <-scriptNtfn2.Event.Confirmed:
   751  		assertConfDetails(t, details, confDetails)
   752  	default:
   753  		t.Fatal("expected update of script ntfn")
   754  	}
   755  
   756  	// Finally, mine a few empty blocks and expect both TXs to be confirmed.
   757  	for currentBlock < 15 {
   758  		block := dcrutil.NewBlock(&wire.MsgBlock{})
   759  		currentBlock++
   760  		err = n.ConnectTip(
   761  			block.Hash(), currentBlock, block.Transactions(),
   762  		)
   763  		if err != nil {
   764  			t.Fatalf("unable to connect block: %v", err)
   765  		}
   766  		if err := n.NotifyHeight(currentBlock); err != nil {
   767  			t.Fatalf("unable to dispatch notifications: %v", err)
   768  		}
   769  	}
   770  
   771  	// Events for both confirmation requests should have been dispatched.
   772  	select {
   773  	case <-ntfn1.Event.Done:
   774  	default:
   775  		t.Fatal("expected notifications for TX 1 to be done")
   776  	}
   777  	select {
   778  	case <-ntfn2.Event.Done:
   779  	default:
   780  		t.Fatal("expected notifications for TX 2 to be done")
   781  	}
   782  }
   783  
   784  // TestTxNotifierHistoricalSpendDispatch tests that the TxNotifier dispatches
   785  // registered notifications when an outpoint is spent before registration.
   786  func TestTxNotifierHistoricalSpendDispatch(t *testing.T) {
   787  	t.Parallel()
   788  
   789  	const startingHeight = 10
   790  
   791  	hintCache := newMockHintCache()
   792  	n := chainntnfs.NewTxNotifier(
   793  		startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
   794  		hintCache, testChainParams,
   795  	)
   796  
   797  	// We'll start by constructing the spending details of the outpoint
   798  	// below.
   799  	spentOutpoint := wire.OutPoint{Index: 1}
   800  	spendTx := newWireTxWithVersion(2)
   801  	spendTx.AddTxIn(&wire.TxIn{
   802  		PreviousOutPoint: spentOutpoint,
   803  		SignatureScript:  testSigScript,
   804  	})
   805  	spendTxHash := spendTx.TxHash()
   806  
   807  	expectedSpendDetails := &chainntnfs.SpendDetail{
   808  		SpentOutPoint:     &spentOutpoint,
   809  		SpenderTxHash:     &spendTxHash,
   810  		SpendingTx:        spendTx,
   811  		SpenderInputIndex: 0,
   812  		SpendingHeight:    startingHeight - 1,
   813  	}
   814  
   815  	// We'll register for a spend notification of the outpoint and ensure
   816  	// that a notification isn't dispatched.
   817  	ntfn, err := n.RegisterSpend(&spentOutpoint, testRawScript, 1)
   818  	if err != nil {
   819  		t.Fatalf("unable to register spend ntfn: %v", err)
   820  	}
   821  
   822  	select {
   823  	case <-ntfn.Event.Spend:
   824  		t.Fatal("received unexpected spend notification")
   825  	default:
   826  	}
   827  
   828  	// Because we're interested in testing the case of a historical spend,
   829  	// we'll hand off the spending details of the outpoint to the notifier
   830  	// as it is not possible for it to view historical events in the chain.
   831  	// By doing this, we replicate the functionality of the ChainNotifier.
   832  	err = n.UpdateSpendDetails(
   833  		ntfn.HistoricalDispatch.SpendRequest, expectedSpendDetails,
   834  	)
   835  	if err != nil {
   836  		t.Fatalf("unable to update spend details: %v", err)
   837  	}
   838  
   839  	// Now that we have the spending details, we should receive a spend
   840  	// notification. We'll ensure that the details match as intended.
   841  	select {
   842  	case spendDetails := <-ntfn.Event.Spend:
   843  		assertSpendDetails(t, spendDetails, expectedSpendDetails)
   844  	default:
   845  		t.Fatalf("expected to receive spend details")
   846  	}
   847  
   848  	// Finally, we'll ensure that if the spending transaction has also been
   849  	// spent, then we don't receive another spend notification.
   850  	prevOut := wire.OutPoint{Hash: spendTxHash, Index: 0}
   851  	spendOfSpend := newWireTxWithVersion(2)
   852  	spendOfSpend.AddTxIn(&wire.TxIn{
   853  		PreviousOutPoint: prevOut,
   854  		SignatureScript:  testSigScript,
   855  	})
   856  	block := dcrutil.NewBlock(&wire.MsgBlock{
   857  		Transactions: []*wire.MsgTx{spendOfSpend},
   858  	})
   859  	err = n.ConnectTip(block.Hash(), startingHeight+1, block.Transactions())
   860  	if err != nil {
   861  		t.Fatalf("unable to connect block: %v", err)
   862  	}
   863  	if err := n.NotifyHeight(startingHeight + 1); err != nil {
   864  		t.Fatalf("unable to dispatch notifications: %v", err)
   865  	}
   866  
   867  	select {
   868  	case <-ntfn.Event.Spend:
   869  		t.Fatal("received unexpected spend notification")
   870  	default:
   871  	}
   872  }
   873  
   874  // TestTxNotifierMultipleHistoricalRescans ensures that we don't attempt to
   875  // request multiple historical confirmation rescans per transactions.
   876  func TestTxNotifierMultipleHistoricalConfRescans(t *testing.T) {
   877  	t.Parallel()
   878  
   879  	const startingHeight = 10
   880  	hintCache := newMockHintCache()
   881  	n := chainntnfs.NewTxNotifier(
   882  		startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
   883  		hintCache, testChainParams,
   884  	)
   885  
   886  	// The first registration for a transaction in the notifier should
   887  	// request a historical confirmation rescan as it does not have a
   888  	// historical view of the chain.
   889  	ntfn1, err := n.RegisterConf(&chainntnfs.ZeroHash, testRawScript, 1, 1)
   890  	if err != nil {
   891  		t.Fatalf("unable to register spend ntfn: %v", err)
   892  	}
   893  	if ntfn1.HistoricalDispatch == nil {
   894  		t.Fatal("expected to receive historical dispatch request")
   895  	}
   896  
   897  	// We'll register another confirmation notification for the same
   898  	// transaction. This should not request a historical confirmation rescan
   899  	// since the first one is still pending.
   900  	ntfn2, err := n.RegisterConf(&chainntnfs.ZeroHash, testRawScript, 1, 1)
   901  	if err != nil {
   902  		t.Fatalf("unable to register spend ntfn: %v", err)
   903  	}
   904  	if ntfn2.HistoricalDispatch != nil {
   905  		t.Fatal("received unexpected historical rescan request")
   906  	}
   907  
   908  	// Finally, we'll mark the ongoing historical rescan as complete and
   909  	// register another notification. We should also expect not to see a
   910  	// historical rescan request since the confirmation details should be
   911  	// cached.
   912  	confDetails := &chainntnfs.TxConfirmation{
   913  		BlockHeight: startingHeight - 1,
   914  	}
   915  	err = n.UpdateConfDetails(ntfn1.HistoricalDispatch.ConfRequest, confDetails)
   916  	if err != nil {
   917  		t.Fatalf("unable to update conf details: %v", err)
   918  	}
   919  
   920  	ntfn3, err := n.RegisterConf(&chainntnfs.ZeroHash, testRawScript, 1, 1)
   921  	if err != nil {
   922  		t.Fatalf("unable to register spend ntfn: %v", err)
   923  	}
   924  	if ntfn3.HistoricalDispatch != nil {
   925  		t.Fatal("received unexpected historical rescan request")
   926  	}
   927  }
   928  
   929  // TestTxNotifierMultipleHistoricalRescans ensures that we don't attempt to
   930  // request multiple historical spend rescans per outpoints.
   931  func TestTxNotifierMultipleHistoricalSpendRescans(t *testing.T) {
   932  	t.Parallel()
   933  
   934  	const startingHeight = 10
   935  	hintCache := newMockHintCache()
   936  	n := chainntnfs.NewTxNotifier(
   937  		startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
   938  		hintCache, testChainParams,
   939  	)
   940  
   941  	// The first registration for an outpoint in the notifier should request
   942  	// a historical spend rescan as it does not have a historical view of
   943  	// the chain.
   944  	op := wire.OutPoint{Index: 1}
   945  	ntfn1, err := n.RegisterSpend(&op, testRawScript, 1)
   946  	if err != nil {
   947  		t.Fatalf("unable to register spend ntfn: %v", err)
   948  	}
   949  	if ntfn1.HistoricalDispatch == nil {
   950  		t.Fatal("expected to receive historical dispatch request")
   951  	}
   952  
   953  	// We'll register another spend notification for the same outpoint. This
   954  	// should not request a historical spend rescan since the first one is
   955  	// still pending.
   956  	ntfn2, err := n.RegisterSpend(&op, testRawScript, 1)
   957  	if err != nil {
   958  		t.Fatalf("unable to register spend ntfn: %v", err)
   959  	}
   960  	if ntfn2.HistoricalDispatch != nil {
   961  		t.Fatal("received unexpected historical rescan request")
   962  	}
   963  
   964  	// Finally, we'll mark the ongoing historical rescan as complete and
   965  	// register another notification. We should also expect not to see a
   966  	// historical rescan request since the confirmation details should be
   967  	// cached.
   968  	spendDetails := &chainntnfs.SpendDetail{
   969  		SpentOutPoint:     &op,
   970  		SpenderTxHash:     &chainntnfs.ZeroHash,
   971  		SpendingTx:        newWireTxWithVersion(2),
   972  		SpenderInputIndex: 0,
   973  		SpendingHeight:    startingHeight - 1,
   974  	}
   975  	err = n.UpdateSpendDetails(
   976  		ntfn1.HistoricalDispatch.SpendRequest, spendDetails,
   977  	)
   978  	if err != nil {
   979  		t.Fatalf("unable to update spend details: %v", err)
   980  	}
   981  
   982  	ntfn3, err := n.RegisterSpend(&op, testRawScript, 1)
   983  	if err != nil {
   984  		t.Fatalf("unable to register spend ntfn: %v", err)
   985  	}
   986  	if ntfn3.HistoricalDispatch != nil {
   987  		t.Fatal("received unexpected historical rescan request")
   988  	}
   989  }
   990  
   991  // TestTxNotifierMultipleHistoricalNtfns ensures that the TxNotifier will only
   992  // request one rescan for a transaction/outpoint when having multiple client
   993  // registrations. Once the rescan has completed and retrieved the
   994  // confirmation/spend details, a notification should be dispatched to _all_
   995  // clients.
   996  func TestTxNotifierMultipleHistoricalNtfns(t *testing.T) {
   997  	t.Parallel()
   998  
   999  	const (
  1000  		numNtfns       = 5
  1001  		startingHeight = 10
  1002  	)
  1003  
  1004  	hintCache := newMockHintCache()
  1005  	n := chainntnfs.NewTxNotifier(
  1006  		startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
  1007  		hintCache, testChainParams,
  1008  	)
  1009  
  1010  	var txid chainhash.Hash
  1011  	copy(txid[:], bytes.Repeat([]byte{0x01}, 32))
  1012  
  1013  	// We'll start off by registered 5 clients for a confirmation
  1014  	// notification on the same transaction.
  1015  	confNtfns := make([]*chainntnfs.ConfRegistration, numNtfns)
  1016  	for i := uint64(0); i < numNtfns; i++ {
  1017  		ntfn, err := n.RegisterConf(&txid, testRawScript, 1, 1)
  1018  		if err != nil {
  1019  			t.Fatalf("unable to register conf ntfn #%d: %v", i, err)
  1020  		}
  1021  		confNtfns[i] = ntfn
  1022  	}
  1023  
  1024  	// Ensure none of them have received the confirmation details.
  1025  	for i, ntfn := range confNtfns {
  1026  		select {
  1027  		case <-ntfn.Event.Confirmed:
  1028  			t.Fatalf("request #%d received unexpected confirmation "+
  1029  				"notification", i)
  1030  		default:
  1031  		}
  1032  	}
  1033  
  1034  	// We'll assume a historical rescan was dispatched and found the
  1035  	// following confirmation details. We'll let the notifier know so that
  1036  	// it can stop watching at tip.
  1037  	expectedConfDetails := &chainntnfs.TxConfirmation{
  1038  		BlockHeight: startingHeight - 1,
  1039  		Tx:          newWireTxWithVersion(1),
  1040  	}
  1041  	err := n.UpdateConfDetails(
  1042  		confNtfns[0].HistoricalDispatch.ConfRequest, expectedConfDetails,
  1043  	)
  1044  	if err != nil {
  1045  		t.Fatalf("unable to update conf details: %v", err)
  1046  	}
  1047  
  1048  	// With the confirmation details retrieved, each client should now have
  1049  	// been notified of the confirmation.
  1050  	for i, ntfn := range confNtfns {
  1051  		select {
  1052  		case confDetails := <-ntfn.Event.Confirmed:
  1053  			assertConfDetails(t, confDetails, expectedConfDetails)
  1054  		default:
  1055  			t.Fatalf("request #%d expected to received "+
  1056  				"confirmation notification", i)
  1057  		}
  1058  	}
  1059  
  1060  	// In order to ensure that the confirmation details are properly cached,
  1061  	// we'll register another client for the same transaction. We should not
  1062  	// see a historical rescan request and the confirmation notification
  1063  	// should come through immediately.
  1064  	extraConfNtfn, err := n.RegisterConf(&txid, testRawScript, 1, 1)
  1065  	if err != nil {
  1066  		t.Fatalf("unable to register conf ntfn: %v", err)
  1067  	}
  1068  	if extraConfNtfn.HistoricalDispatch != nil {
  1069  		t.Fatal("received unexpected historical rescan request")
  1070  	}
  1071  
  1072  	select {
  1073  	case confDetails := <-extraConfNtfn.Event.Confirmed:
  1074  		assertConfDetails(t, confDetails, expectedConfDetails)
  1075  	default:
  1076  		t.Fatal("expected to receive spend notification")
  1077  	}
  1078  
  1079  	// Similarly, we'll do the same thing but for spend notifications.
  1080  	op := wire.OutPoint{Index: 1}
  1081  	spendNtfns := make([]*chainntnfs.SpendRegistration, numNtfns)
  1082  	for i := uint64(0); i < numNtfns; i++ {
  1083  		ntfn, err := n.RegisterSpend(&op, testRawScript, 1)
  1084  		if err != nil {
  1085  			t.Fatalf("unable to register spend ntfn #%d: %v", i, err)
  1086  		}
  1087  		spendNtfns[i] = ntfn
  1088  	}
  1089  
  1090  	// Ensure none of them have received the spend details.
  1091  	for i, ntfn := range spendNtfns {
  1092  		select {
  1093  		case <-ntfn.Event.Spend:
  1094  			t.Fatalf("request #%d received unexpected spend "+
  1095  				"notification", i)
  1096  		default:
  1097  		}
  1098  	}
  1099  
  1100  	// We'll assume a historical rescan was dispatched and found the
  1101  	// following spend details. We'll let the notifier know so that it can
  1102  	// stop watching at tip.
  1103  	expectedSpendDetails := &chainntnfs.SpendDetail{
  1104  		SpentOutPoint:     &op,
  1105  		SpenderTxHash:     &chainntnfs.ZeroHash,
  1106  		SpendingTx:        newWireTxWithVersion(2),
  1107  		SpenderInputIndex: 0,
  1108  		SpendingHeight:    startingHeight - 1,
  1109  	}
  1110  	err = n.UpdateSpendDetails(
  1111  		spendNtfns[0].HistoricalDispatch.SpendRequest, expectedSpendDetails,
  1112  	)
  1113  	if err != nil {
  1114  		t.Fatalf("unable to update spend details: %v", err)
  1115  	}
  1116  
  1117  	// With the spend details retrieved, each client should now have been
  1118  	// notified of the spend.
  1119  	for i, ntfn := range spendNtfns {
  1120  		select {
  1121  		case spendDetails := <-ntfn.Event.Spend:
  1122  			assertSpendDetails(t, spendDetails, expectedSpendDetails)
  1123  		default:
  1124  			t.Fatalf("request #%d expected to received spend "+
  1125  				"notification", i)
  1126  		}
  1127  	}
  1128  
  1129  	// Finally, in order to ensure that the spend details are properly
  1130  	// cached, we'll register another client for the same outpoint. We
  1131  	// should not see a historical rescan request and the spend notification
  1132  	// should come through immediately.
  1133  	extraSpendNtfn, err := n.RegisterSpend(&op, testRawScript, 1)
  1134  	if err != nil {
  1135  		t.Fatalf("unable to register spend ntfn: %v", err)
  1136  	}
  1137  	if extraSpendNtfn.HistoricalDispatch != nil {
  1138  		t.Fatal("received unexpected historical rescan request")
  1139  	}
  1140  
  1141  	select {
  1142  	case spendDetails := <-extraSpendNtfn.Event.Spend:
  1143  		assertSpendDetails(t, spendDetails, expectedSpendDetails)
  1144  	default:
  1145  		t.Fatal("expected to receive spend notification")
  1146  	}
  1147  }
  1148  
  1149  // TestTxNotifierCancelConf ensures that a confirmation notification after a
  1150  // client has canceled their intent to receive one.
  1151  func TestTxNotifierCancelConf(t *testing.T) {
  1152  	t.Parallel()
  1153  
  1154  	const startingHeight = 10
  1155  	hintCache := newMockHintCache()
  1156  	n := chainntnfs.NewTxNotifier(
  1157  		startingHeight, 100, hintCache, hintCache, testChainParams,
  1158  	)
  1159  
  1160  	// We'll register four notification requests. The last three will be
  1161  	// canceled.
  1162  	tx1 := newWireTxWithVersion(1)
  1163  	tx1.AddTxOut(&wire.TxOut{PkScript: testRawScript})
  1164  	tx1Hash := tx1.TxHash()
  1165  	ntfn1, err := n.RegisterConf(&tx1Hash, testRawScript, 1, 1)
  1166  	if err != nil {
  1167  		t.Fatalf("unable to register spend ntfn: %v", err)
  1168  	}
  1169  
  1170  	tx2 := newWireTxWithVersion(2)
  1171  	tx2.AddTxOut(&wire.TxOut{PkScript: testRawScript})
  1172  	tx2Hash := tx2.TxHash()
  1173  	ntfn2, err := n.RegisterConf(&tx2Hash, testRawScript, 1, 1)
  1174  	if err != nil {
  1175  		t.Fatalf("unable to register spend ntfn: %v", err)
  1176  	}
  1177  	ntfn3, err := n.RegisterConf(&tx2Hash, testRawScript, 1, 1)
  1178  	if err != nil {
  1179  		t.Fatalf("unable to register spend ntfn: %v", err)
  1180  	}
  1181  
  1182  	// This request will have a three block num confs.
  1183  	ntfn4, err := n.RegisterConf(&tx2Hash, testRawScript, 3, 1)
  1184  	if err != nil {
  1185  		t.Fatalf("unable to register spend ntfn: %v", err)
  1186  	}
  1187  
  1188  	// Extend the chain with a block that will confirm both transactions.
  1189  	// This will queue confirmation notifications to dispatch once their
  1190  	// respective heights have been met.
  1191  	block := dcrutil.NewBlock(&wire.MsgBlock{
  1192  		Transactions: []*wire.MsgTx{tx1, tx2},
  1193  	})
  1194  	tx1ConfDetails := &chainntnfs.TxConfirmation{
  1195  		BlockHeight: startingHeight + 1,
  1196  		BlockHash:   block.Hash(),
  1197  		TxIndex:     0,
  1198  		Tx:          tx1,
  1199  	}
  1200  
  1201  	// Cancel the second notification before connecting the block.
  1202  	ntfn2.Event.Cancel()
  1203  
  1204  	err = n.ConnectTip(block.Hash(), startingHeight+1, block.Transactions())
  1205  	if err != nil {
  1206  		t.Fatalf("unable to connect block: %v", err)
  1207  	}
  1208  
  1209  	// Cancel the third notification before notifying to ensure its queued
  1210  	// confirmation notification gets removed as well.
  1211  	ntfn3.Event.Cancel()
  1212  
  1213  	if err := n.NotifyHeight(startingHeight + 1); err != nil {
  1214  		t.Fatalf("unable to dispatch notifications: %v", err)
  1215  	}
  1216  
  1217  	// The first request should still be active, so we should receive a
  1218  	// confirmation notification with the correct details.
  1219  	select {
  1220  	case confDetails := <-ntfn1.Event.Confirmed:
  1221  		assertConfDetails(t, confDetails, tx1ConfDetails)
  1222  	default:
  1223  		t.Fatalf("expected to receive confirmation notification")
  1224  	}
  1225  
  1226  	// The second and third, however, should not have. The event's Confirmed
  1227  	// channel must have also been closed to indicate the caller that the
  1228  	// TxNotifier can no longer fulfill their canceled request.
  1229  	select {
  1230  	case _, ok := <-ntfn2.Event.Confirmed:
  1231  		if ok {
  1232  			t.Fatal("expected Confirmed channel to be closed")
  1233  		}
  1234  	default:
  1235  		t.Fatal("expected Confirmed channel to be closed")
  1236  	}
  1237  	select {
  1238  	case _, ok := <-ntfn3.Event.Confirmed:
  1239  		if ok {
  1240  			t.Fatal("expected Confirmed channel to be closed")
  1241  		}
  1242  	default:
  1243  		t.Fatal("expected Confirmed channel to be closed")
  1244  	}
  1245  
  1246  	// Connect yet another block.
  1247  	block1 := dcrutil.NewBlock(&wire.MsgBlock{
  1248  		Transactions: []*wire.MsgTx{},
  1249  	})
  1250  
  1251  	err = n.ConnectTip(block1.Hash(), startingHeight+2, block1.Transactions())
  1252  	if err != nil {
  1253  		t.Fatalf("unable to connect block: %v", err)
  1254  	}
  1255  
  1256  	if err := n.NotifyHeight(startingHeight + 2); err != nil {
  1257  		t.Fatalf("unable to dispatch notifications: %v", err)
  1258  	}
  1259  
  1260  	// Since neither it reached the set confirmation height or was
  1261  	// canceled, nothing should happen to ntfn4 in this block.
  1262  	select {
  1263  	case <-ntfn4.Event.Confirmed:
  1264  		t.Fatal("expected nothing to happen")
  1265  	case <-time.After(10 * time.Millisecond):
  1266  	}
  1267  
  1268  	// Now cancel the notification.
  1269  	ntfn4.Event.Cancel()
  1270  	select {
  1271  	case _, ok := <-ntfn4.Event.Confirmed:
  1272  		if ok {
  1273  			t.Fatal("expected Confirmed channel to be closed")
  1274  		}
  1275  	default:
  1276  		t.Fatal("expected Confirmed channel to be closed")
  1277  	}
  1278  
  1279  	// Finally, confirm a block that would trigger ntfn4 confirmation
  1280  	// hadn't it already been canceled.
  1281  	block2 := dcrutil.NewBlock(&wire.MsgBlock{
  1282  		Transactions: []*wire.MsgTx{},
  1283  	})
  1284  
  1285  	err = n.ConnectTip(block2.Hash(), startingHeight+3, block2.Transactions())
  1286  	if err != nil {
  1287  		t.Fatalf("unable to connect block: %v", err)
  1288  	}
  1289  
  1290  	if err := n.NotifyHeight(startingHeight + 3); err != nil {
  1291  		t.Fatalf("unable to dispatch notifications: %v", err)
  1292  	}
  1293  }
  1294  
  1295  // TestTxNotifierCancelSpend ensures that a spend notification after a client
  1296  // has canceled their intent to receive one.
  1297  func TestTxNotifierCancelSpend(t *testing.T) {
  1298  	t.Parallel()
  1299  
  1300  	const startingHeight = 10
  1301  	hintCache := newMockHintCache()
  1302  	n := chainntnfs.NewTxNotifier(
  1303  		startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
  1304  		hintCache, testChainParams,
  1305  	)
  1306  
  1307  	// We'll register two notification requests. Only the second one will be
  1308  	// canceled.
  1309  	op1 := wire.OutPoint{Index: 1}
  1310  	ntfn1, err := n.RegisterSpend(&op1, testRawScript, 1)
  1311  	if err != nil {
  1312  		t.Fatalf("unable to register spend ntfn: %v", err)
  1313  	}
  1314  
  1315  	op2 := wire.OutPoint{Index: 2}
  1316  	ntfn2, err := n.RegisterSpend(&op2, testRawScript, 1)
  1317  	if err != nil {
  1318  		t.Fatalf("unable to register spend ntfn: %v", err)
  1319  	}
  1320  
  1321  	// Construct the spending details of the outpoint and create a dummy
  1322  	// block containing it.
  1323  	spendTx := newWireTxWithVersion(2)
  1324  	spendTx.AddTxIn(&wire.TxIn{
  1325  		PreviousOutPoint: op1,
  1326  		SignatureScript:  testSigScript,
  1327  	})
  1328  	spendTxHash := spendTx.TxHash()
  1329  	expectedSpendDetails := &chainntnfs.SpendDetail{
  1330  		SpentOutPoint:     &op1,
  1331  		SpenderTxHash:     &spendTxHash,
  1332  		SpendingTx:        spendTx,
  1333  		SpenderInputIndex: 0,
  1334  		SpendingHeight:    startingHeight + 1,
  1335  	}
  1336  
  1337  	block := dcrutil.NewBlock(&wire.MsgBlock{
  1338  		Transactions: []*wire.MsgTx{spendTx},
  1339  	})
  1340  
  1341  	// Before extending the notifier's tip with the dummy block above, we'll
  1342  	// cancel the second request.
  1343  	n.CancelSpend(ntfn2.HistoricalDispatch.SpendRequest, 2)
  1344  
  1345  	err = n.ConnectTip(block.Hash(), startingHeight+1, block.Transactions())
  1346  	if err != nil {
  1347  		t.Fatalf("unable to connect block: %v", err)
  1348  	}
  1349  	if err := n.NotifyHeight(startingHeight + 1); err != nil {
  1350  		t.Fatalf("unable to dispatch notifications: %v", err)
  1351  	}
  1352  
  1353  	// The first request should still be active, so we should receive a
  1354  	// spend notification with the correct spending details.
  1355  	select {
  1356  	case spendDetails := <-ntfn1.Event.Spend:
  1357  		assertSpendDetails(t, spendDetails, expectedSpendDetails)
  1358  	default:
  1359  		t.Fatalf("expected to receive spend notification")
  1360  	}
  1361  
  1362  	// The second one, however, should not have. The event's Spend channel
  1363  	// must have also been closed to indicate the caller that the TxNotifier
  1364  	// can no longer fulfill their canceled request.
  1365  	select {
  1366  	case _, ok := <-ntfn2.Event.Spend:
  1367  		if ok {
  1368  			t.Fatal("expected Spend channel to be closed")
  1369  		}
  1370  	default:
  1371  		t.Fatal("expected Spend channel to be closed")
  1372  	}
  1373  }
  1374  
  1375  // TestTxNotifierConfReorg ensures that clients are notified of a reorg when a
  1376  // transaction for which they registered a confirmation notification has been
  1377  // reorged out of the chain.
  1378  func TestTxNotifierConfReorg(t *testing.T) {
  1379  	t.Parallel()
  1380  
  1381  	const (
  1382  		tx1NumConfs uint32 = 2
  1383  		tx2NumConfs uint32 = 1
  1384  		tx3NumConfs uint32 = 2
  1385  	)
  1386  
  1387  	hintCache := newMockHintCache()
  1388  	n := chainntnfs.NewTxNotifier(
  1389  		7, chainntnfs.ReorgSafetyLimit, hintCache, hintCache,
  1390  		testChainParams,
  1391  	)
  1392  
  1393  	// Tx 1 will be confirmed in block 9 and requires 2 confs.
  1394  	tx1 := *newWireTxWithVersion(1)
  1395  	tx1.AddTxOut(&wire.TxOut{PkScript: testRawScript})
  1396  	tx1Hash := tx1.TxHash()
  1397  	ntfn1, err := n.RegisterConf(&tx1Hash, testRawScript, tx1NumConfs, 1)
  1398  	if err != nil {
  1399  		t.Fatalf("unable to register ntfn: %v", err)
  1400  	}
  1401  
  1402  	err = n.UpdateConfDetails(ntfn1.HistoricalDispatch.ConfRequest, nil)
  1403  	if err != nil {
  1404  		t.Fatalf("unable to deliver conf details: %v", err)
  1405  	}
  1406  
  1407  	// Tx 2 will be confirmed in block 10 and requires 1 conf.
  1408  	tx2 := *newWireTxWithVersion(2)
  1409  	tx2.AddTxOut(&wire.TxOut{PkScript: testRawScript})
  1410  	tx2Hash := tx2.TxHash()
  1411  	ntfn2, err := n.RegisterConf(&tx2Hash, testRawScript, tx2NumConfs, 1)
  1412  	if err != nil {
  1413  		t.Fatalf("unable to register ntfn: %v", err)
  1414  	}
  1415  
  1416  	err = n.UpdateConfDetails(ntfn2.HistoricalDispatch.ConfRequest, nil)
  1417  	if err != nil {
  1418  		t.Fatalf("unable to deliver conf details: %v", err)
  1419  	}
  1420  
  1421  	// Tx 3 will be confirmed in block 10 and requires 2 confs.
  1422  	tx3 := *newWireTxWithVersion(3)
  1423  	tx3.AddTxOut(&wire.TxOut{PkScript: testRawScript})
  1424  	tx3Hash := tx3.TxHash()
  1425  	ntfn3, err := n.RegisterConf(&tx3Hash, testRawScript, tx3NumConfs, 1)
  1426  	if err != nil {
  1427  		t.Fatalf("unable to register ntfn: %v", err)
  1428  	}
  1429  
  1430  	err = n.UpdateConfDetails(ntfn3.HistoricalDispatch.ConfRequest, nil)
  1431  	if err != nil {
  1432  		t.Fatalf("unable to deliver conf details: %v", err)
  1433  	}
  1434  
  1435  	// Sync chain to block 10. Txs 1 & 2 should be confirmed.
  1436  	block1 := dcrutil.NewBlock(&wire.MsgBlock{
  1437  		Transactions: []*wire.MsgTx{&tx1},
  1438  	})
  1439  	if err := n.ConnectTip(nil, 8, block1.Transactions()); err != nil {
  1440  		t.Fatalf("Failed to connect block: %v", err)
  1441  	}
  1442  	if err := n.NotifyHeight(8); err != nil {
  1443  		t.Fatalf("unable to dispatch notifications: %v", err)
  1444  	}
  1445  	if err := n.ConnectTip(nil, 9, nil); err != nil {
  1446  		t.Fatalf("Failed to connect block: %v", err)
  1447  	}
  1448  	if err := n.NotifyHeight(9); err != nil {
  1449  		t.Fatalf("unable to dispatch notifications: %v", err)
  1450  	}
  1451  
  1452  	block2 := dcrutil.NewBlock(&wire.MsgBlock{
  1453  		Transactions: []*wire.MsgTx{&tx2, &tx3},
  1454  	})
  1455  	if err := n.ConnectTip(nil, 10, block2.Transactions()); err != nil {
  1456  		t.Fatalf("Failed to connect block: %v", err)
  1457  	}
  1458  	if err := n.NotifyHeight(10); err != nil {
  1459  		t.Fatalf("unable to dispatch notifications: %v", err)
  1460  	}
  1461  
  1462  	// We should receive two updates for tx1 since it requires two
  1463  	// confirmations and it has already met them.
  1464  	for i := 0; i < 2; i++ {
  1465  		select {
  1466  		case <-ntfn1.Event.Updates:
  1467  		default:
  1468  			t.Fatal("Expected confirmation update for tx1")
  1469  		}
  1470  	}
  1471  
  1472  	// A confirmation notification for tx1 should be dispatched, as it met
  1473  	// its required number of confirmations.
  1474  	select {
  1475  	case <-ntfn1.Event.Confirmed:
  1476  	default:
  1477  		t.Fatalf("Expected confirmation for tx1")
  1478  	}
  1479  
  1480  	// We should only receive one update for tx2 since it only requires
  1481  	// one confirmation and it already met it.
  1482  	select {
  1483  	case <-ntfn2.Event.Updates:
  1484  	default:
  1485  		t.Fatal("Expected confirmation update for tx2")
  1486  	}
  1487  
  1488  	// A confirmation notification for tx2 should be dispatched, as it met
  1489  	// its required number of confirmations.
  1490  	select {
  1491  	case <-ntfn2.Event.Confirmed:
  1492  	default:
  1493  		t.Fatalf("Expected confirmation for tx2")
  1494  	}
  1495  
  1496  	// We should only receive one update for tx3 since it only has one
  1497  	// confirmation so far and it requires two.
  1498  	select {
  1499  	case <-ntfn3.Event.Updates:
  1500  	default:
  1501  		t.Fatal("Expected confirmation update for tx3")
  1502  	}
  1503  
  1504  	// A confirmation notification for tx3 should not be dispatched yet, as
  1505  	// it requires one more confirmation.
  1506  	select {
  1507  	case txConf := <-ntfn3.Event.Confirmed:
  1508  		t.Fatalf("Received unexpected confirmation for tx3: %v", txConf)
  1509  	default:
  1510  	}
  1511  
  1512  	// The block that included tx2 and tx3 is disconnected and two next
  1513  	// blocks without them are connected.
  1514  	if err := n.DisconnectTip(10); err != nil {
  1515  		t.Fatalf("Failed to connect block: %v", err)
  1516  	}
  1517  
  1518  	if err := n.ConnectTip(nil, 10, nil); err != nil {
  1519  		t.Fatalf("Failed to connect block: %v", err)
  1520  	}
  1521  	if err := n.NotifyHeight(10); err != nil {
  1522  		t.Fatalf("unable to dispatch notifications: %v", err)
  1523  	}
  1524  
  1525  	if err := n.ConnectTip(nil, 11, nil); err != nil {
  1526  		t.Fatalf("Failed to connect block: %v", err)
  1527  	}
  1528  	if err := n.NotifyHeight(11); err != nil {
  1529  		t.Fatalf("unable to dispatch notifications: %v", err)
  1530  	}
  1531  
  1532  	select {
  1533  	case reorgDepth := <-ntfn2.Event.NegativeConf:
  1534  		if reorgDepth != 1 {
  1535  			t.Fatalf("Incorrect value for negative conf notification: "+
  1536  				"expected %d, got %d", 1, reorgDepth)
  1537  		}
  1538  	default:
  1539  		t.Fatalf("Expected negative conf notification for tx1")
  1540  	}
  1541  
  1542  	// We should not receive any event notifications from all of the
  1543  	// transactions because tx1 has already been confirmed and tx2 and tx3
  1544  	// have not been included in the chain since the reorg.
  1545  	select {
  1546  	case <-ntfn1.Event.Updates:
  1547  		t.Fatal("Received unexpected confirmation update for tx1")
  1548  	case txConf := <-ntfn1.Event.Confirmed:
  1549  		t.Fatalf("Received unexpected confirmation for tx1: %v", txConf)
  1550  	default:
  1551  	}
  1552  
  1553  	select {
  1554  	case <-ntfn2.Event.Updates:
  1555  		t.Fatal("Received unexpected confirmation update for tx2")
  1556  	case txConf := <-ntfn2.Event.Confirmed:
  1557  		t.Fatalf("Received unexpected confirmation for tx2: %v", txConf)
  1558  	default:
  1559  	}
  1560  
  1561  	select {
  1562  	case <-ntfn3.Event.Updates:
  1563  		t.Fatal("Received unexpected confirmation update for tx3")
  1564  	case txConf := <-ntfn3.Event.Confirmed:
  1565  		t.Fatalf("Received unexpected confirmation for tx3: %v", txConf)
  1566  	default:
  1567  	}
  1568  
  1569  	// Now transactions 2 & 3 are re-included in a new block.
  1570  	block3 := dcrutil.NewBlock(&wire.MsgBlock{
  1571  		Transactions: []*wire.MsgTx{&tx2, &tx3},
  1572  	})
  1573  	block4 := dcrutil.NewBlock(&wire.MsgBlock{})
  1574  
  1575  	err = n.ConnectTip(block3.Hash(), 12, block3.Transactions())
  1576  	if err != nil {
  1577  		t.Fatalf("Failed to connect block: %v", err)
  1578  	}
  1579  	if err := n.NotifyHeight(12); err != nil {
  1580  		t.Fatalf("unable to dispatch notifications: %v", err)
  1581  	}
  1582  
  1583  	err = n.ConnectTip(block4.Hash(), 13, block4.Transactions())
  1584  	if err != nil {
  1585  		t.Fatalf("Failed to connect block: %v", err)
  1586  	}
  1587  	if err := n.NotifyHeight(13); err != nil {
  1588  		t.Fatalf("unable to dispatch notifications: %v", err)
  1589  	}
  1590  
  1591  	// We should only receive one update for tx2 since it only requires
  1592  	// one confirmation and it already met it.
  1593  	select {
  1594  	case numConfsLeft := <-ntfn2.Event.Updates:
  1595  		const expected = 0
  1596  		if numConfsLeft != expected {
  1597  			t.Fatalf("Received incorrect confirmation update: tx2 "+
  1598  				"expected %d confirmations left, got %d",
  1599  				expected, numConfsLeft)
  1600  		}
  1601  	default:
  1602  		t.Fatal("Expected confirmation update for tx2")
  1603  	}
  1604  
  1605  	// A confirmation notification for tx2 should be dispatched, as it met
  1606  	// its required number of confirmations.
  1607  	select {
  1608  	case txConf := <-ntfn2.Event.Confirmed:
  1609  		expectedConf := chainntnfs.TxConfirmation{
  1610  			BlockHash:   block3.Hash(),
  1611  			BlockHeight: 12,
  1612  			TxIndex:     0,
  1613  			Tx:          &tx2,
  1614  		}
  1615  		assertConfDetails(t, txConf, &expectedConf)
  1616  	default:
  1617  		t.Fatalf("Expected confirmation for tx2")
  1618  	}
  1619  
  1620  	// We should receive two updates for tx3 since it requires two
  1621  	// confirmations and it has already met them.
  1622  	for i := uint32(1); i <= 2; i++ {
  1623  		select {
  1624  		case numConfsLeft := <-ntfn3.Event.Updates:
  1625  			expected := tx3NumConfs - i
  1626  			if numConfsLeft != expected {
  1627  				t.Fatalf("Received incorrect confirmation update: tx3 "+
  1628  					"expected %d confirmations left, got %d",
  1629  					expected, numConfsLeft)
  1630  			}
  1631  		default:
  1632  			t.Fatal("Expected confirmation update for tx2")
  1633  		}
  1634  	}
  1635  
  1636  	// A confirmation notification for tx3 should be dispatched, as it met
  1637  	// its required number of confirmations.
  1638  	select {
  1639  	case txConf := <-ntfn3.Event.Confirmed:
  1640  		expectedConf := chainntnfs.TxConfirmation{
  1641  			BlockHash:   block3.Hash(),
  1642  			BlockHeight: 12,
  1643  			TxIndex:     1,
  1644  			Tx:          &tx3,
  1645  		}
  1646  		assertConfDetails(t, txConf, &expectedConf)
  1647  	default:
  1648  		t.Fatalf("Expected confirmation for tx3")
  1649  	}
  1650  }
  1651  
  1652  // TestTxNotifierSpendReorg ensures that clients are notified of a reorg when
  1653  // the spending transaction of an outpoint for which they registered a spend
  1654  // notification for has been reorged out of the chain.
  1655  func TestTxNotifierSpendReorg(t *testing.T) {
  1656  	t.Parallel()
  1657  
  1658  	const startingHeight = 10
  1659  	hintCache := newMockHintCache()
  1660  	n := chainntnfs.NewTxNotifier(
  1661  		startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
  1662  		hintCache, testChainParams,
  1663  	)
  1664  
  1665  	// We'll have two outpoints that will be spent throughout the test. The
  1666  	// first will be spent and will not experience a reorg, while the second
  1667  	// one will.
  1668  	op1 := wire.OutPoint{Index: 1}
  1669  	spendTx1 := newWireTxWithVersion(2)
  1670  	spendTx1.AddTxIn(&wire.TxIn{
  1671  		PreviousOutPoint: op1,
  1672  		SignatureScript:  testSigScript,
  1673  	})
  1674  	spendTxHash1 := spendTx1.TxHash()
  1675  	expectedSpendDetails1 := &chainntnfs.SpendDetail{
  1676  		SpentOutPoint:     &op1,
  1677  		SpenderTxHash:     &spendTxHash1,
  1678  		SpendingTx:        spendTx1,
  1679  		SpenderInputIndex: 0,
  1680  		SpendingHeight:    startingHeight + 1,
  1681  	}
  1682  
  1683  	op2 := wire.OutPoint{Index: 2}
  1684  	spendTx2 := newWireTxWithVersion(2)
  1685  	spendTx2.AddTxIn(&wire.TxIn{
  1686  		PreviousOutPoint: chainntnfs.ZeroOutPoint,
  1687  		SignatureScript:  testSigScript,
  1688  	})
  1689  	spendTx2.AddTxIn(&wire.TxIn{
  1690  		PreviousOutPoint: op2,
  1691  		SignatureScript:  testSigScript,
  1692  	})
  1693  	spendTxHash2 := spendTx2.TxHash()
  1694  
  1695  	// The second outpoint will experience a reorg and get re-spent at a
  1696  	// different height, so we'll need to construct the spend details for
  1697  	// before and after the reorg.
  1698  	expectedSpendDetails2BeforeReorg := chainntnfs.SpendDetail{
  1699  		SpentOutPoint:     &op2,
  1700  		SpenderTxHash:     &spendTxHash2,
  1701  		SpendingTx:        spendTx2,
  1702  		SpenderInputIndex: 1,
  1703  		SpendingHeight:    startingHeight + 2,
  1704  	}
  1705  
  1706  	// The spend details after the reorg will be exactly the same, except
  1707  	// for the spend confirming at the next height.
  1708  	expectedSpendDetails2AfterReorg := expectedSpendDetails2BeforeReorg
  1709  	expectedSpendDetails2AfterReorg.SpendingHeight++
  1710  
  1711  	// We'll register for a spend notification for each outpoint above.
  1712  	ntfn1, err := n.RegisterSpend(&op1, testRawScript, 1)
  1713  	if err != nil {
  1714  		t.Fatalf("unable to register spend ntfn: %v", err)
  1715  	}
  1716  
  1717  	ntfn2, err := n.RegisterSpend(&op2, testRawScript, 1)
  1718  	if err != nil {
  1719  		t.Fatalf("unable to register spend ntfn: %v", err)
  1720  	}
  1721  
  1722  	// We'll extend the chain by connecting a new block at tip. This block
  1723  	// will only contain the spending transaction of the first outpoint.
  1724  	block1 := dcrutil.NewBlock(&wire.MsgBlock{
  1725  		Transactions: []*wire.MsgTx{spendTx1},
  1726  	})
  1727  	err = n.ConnectTip(block1.Hash(), startingHeight+1, block1.Transactions())
  1728  	if err != nil {
  1729  		t.Fatalf("unable to connect block: %v", err)
  1730  	}
  1731  	if err := n.NotifyHeight(startingHeight + 1); err != nil {
  1732  		t.Fatalf("unable to dispatch notifications: %v", err)
  1733  	}
  1734  
  1735  	// We should receive a spend notification for the first outpoint with
  1736  	// its correct spending details.
  1737  	select {
  1738  	case spendDetails := <-ntfn1.Event.Spend:
  1739  		assertSpendDetails(t, spendDetails, expectedSpendDetails1)
  1740  	default:
  1741  		t.Fatal("expected to receive spend details")
  1742  	}
  1743  
  1744  	// We should not, however, receive one for the second outpoint as it has
  1745  	// yet to be spent.
  1746  	select {
  1747  	case <-ntfn2.Event.Spend:
  1748  		t.Fatal("received unexpected spend notification")
  1749  	default:
  1750  	}
  1751  
  1752  	// Now, we'll extend the chain again, this time with a block containing
  1753  	// the spending transaction of the second outpoint.
  1754  	block2 := dcrutil.NewBlock(&wire.MsgBlock{
  1755  		Transactions: []*wire.MsgTx{spendTx2},
  1756  	})
  1757  	err = n.ConnectTip(block2.Hash(), startingHeight+2, block2.Transactions())
  1758  	if err != nil {
  1759  		t.Fatalf("unable to connect block: %v", err)
  1760  	}
  1761  	if err := n.NotifyHeight(startingHeight + 2); err != nil {
  1762  		t.Fatalf("unable to dispatch notifications: %v", err)
  1763  	}
  1764  
  1765  	// We should not receive another spend notification for the first
  1766  	// outpoint.
  1767  	select {
  1768  	case <-ntfn1.Event.Spend:
  1769  		t.Fatal("received unexpected spend notification")
  1770  	default:
  1771  	}
  1772  
  1773  	// We should receive one for the second outpoint.
  1774  	select {
  1775  	case spendDetails := <-ntfn2.Event.Spend:
  1776  		assertSpendDetails(
  1777  			t, spendDetails, &expectedSpendDetails2BeforeReorg,
  1778  		)
  1779  	default:
  1780  		t.Fatal("expected to receive spend details")
  1781  	}
  1782  
  1783  	// Now, to replicate a chain reorg, we'll disconnect the block that
  1784  	// contained the spending transaction of the second outpoint.
  1785  	if err := n.DisconnectTip(startingHeight + 2); err != nil {
  1786  		t.Fatalf("unable to disconnect block: %v", err)
  1787  	}
  1788  
  1789  	// No notifications should be dispatched for the first outpoint as it
  1790  	// was spent at a previous height.
  1791  	select {
  1792  	case <-ntfn1.Event.Spend:
  1793  		t.Fatal("received unexpected spend notification")
  1794  	case <-ntfn1.Event.Reorg:
  1795  		t.Fatal("received unexpected spend reorg notification")
  1796  	default:
  1797  	}
  1798  
  1799  	// We should receive a reorg notification for the second outpoint.
  1800  	select {
  1801  	case <-ntfn2.Event.Spend:
  1802  		t.Fatal("received unexpected spend notification")
  1803  	case <-ntfn2.Event.Reorg:
  1804  	default:
  1805  		t.Fatal("expected spend reorg notification")
  1806  	}
  1807  
  1808  	// We'll now extend the chain with an empty block, to ensure that we can
  1809  	// properly detect when an outpoint has been re-spent at a later height.
  1810  	emptyBlock := dcrutil.NewBlock(&wire.MsgBlock{})
  1811  	err = n.ConnectTip(
  1812  		emptyBlock.Hash(), startingHeight+2, emptyBlock.Transactions(),
  1813  	)
  1814  	if err != nil {
  1815  		t.Fatalf("unable to disconnect block: %v", err)
  1816  	}
  1817  	if err := n.NotifyHeight(startingHeight + 2); err != nil {
  1818  		t.Fatalf("unable to dispatch notifications: %v", err)
  1819  	}
  1820  
  1821  	// We shouldn't receive notifications for either of the outpoints.
  1822  	select {
  1823  	case <-ntfn1.Event.Spend:
  1824  		t.Fatal("received unexpected spend notification")
  1825  	case <-ntfn1.Event.Reorg:
  1826  		t.Fatal("received unexpected spend reorg notification")
  1827  	case <-ntfn2.Event.Spend:
  1828  		t.Fatal("received unexpected spend notification")
  1829  	case <-ntfn2.Event.Reorg:
  1830  		t.Fatal("received unexpected spend reorg notification")
  1831  	default:
  1832  	}
  1833  
  1834  	// Finally, extend the chain with another block containing the same
  1835  	// spending transaction of the second outpoint.
  1836  	err = n.ConnectTip(
  1837  		block2.Hash(), startingHeight+3, block2.Transactions(),
  1838  	)
  1839  	if err != nil {
  1840  		t.Fatalf("unable to connect block: %v", err)
  1841  	}
  1842  	if err := n.NotifyHeight(startingHeight + 3); err != nil {
  1843  		t.Fatalf("unable to dispatch notifications: %v", err)
  1844  	}
  1845  
  1846  	// We should now receive a spend notification once again for the second
  1847  	// outpoint containing the new spend details.
  1848  	select {
  1849  	case spendDetails := <-ntfn2.Event.Spend:
  1850  		assertSpendDetails(
  1851  			t, spendDetails, &expectedSpendDetails2AfterReorg,
  1852  		)
  1853  	default:
  1854  		t.Fatalf("expected to receive spend notification")
  1855  	}
  1856  
  1857  	// Once again, we should not receive one for the first outpoint.
  1858  	select {
  1859  	case <-ntfn1.Event.Spend:
  1860  		t.Fatal("received unexpected spend notification")
  1861  	default:
  1862  	}
  1863  }
  1864  
  1865  // TestTxNotifierUpdateSpendReorg tests that a call to RegisterSpend after the
  1866  // spend has been confirmed, and then UpdateSpendDetails (called by historical
  1867  // dispatch), followed by a chain re-org will notify on the Reorg channel. This
  1868  // was not always the case and has since been fixed.
  1869  func TestTxNotifierSpendReorgMissed(t *testing.T) {
  1870  	t.Parallel()
  1871  
  1872  	const startingHeight = 10
  1873  	hintCache := newMockHintCache()
  1874  	n := chainntnfs.NewTxNotifier(
  1875  		startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
  1876  		hintCache, testChainParams,
  1877  	)
  1878  
  1879  	// We'll create a spending transaction that spends the outpoint we'll
  1880  	// watch.
  1881  	op := wire.OutPoint{Index: 1}
  1882  	spendTx := newWireTxWithVersion(2)
  1883  	spendTx.AddTxIn(&wire.TxIn{
  1884  		PreviousOutPoint: op,
  1885  		SignatureScript:  testSigScript,
  1886  	})
  1887  	spendTxHash := spendTx.TxHash()
  1888  
  1889  	// Create the spend details that we'll call UpdateSpendDetails with.
  1890  	spendDetails := &chainntnfs.SpendDetail{
  1891  		SpentOutPoint:     &op,
  1892  		SpenderTxHash:     &spendTxHash,
  1893  		SpendingTx:        spendTx,
  1894  		SpenderInputIndex: 0,
  1895  		SpendingHeight:    startingHeight + 1,
  1896  	}
  1897  
  1898  	// Now confirm the spending transaction.
  1899  	block := dcrutil.NewBlock(&wire.MsgBlock{
  1900  		Transactions: []*wire.MsgTx{spendTx},
  1901  	})
  1902  	err := n.ConnectTip(block.Hash(), startingHeight+1, block.Transactions())
  1903  	if err != nil {
  1904  		t.Fatalf("unable to connect block: %v", err)
  1905  	}
  1906  	if err := n.NotifyHeight(startingHeight + 1); err != nil {
  1907  		t.Fatalf("unable to dispatch notifications: %v", err)
  1908  	}
  1909  
  1910  	// We register for the spend now and will not get a spend notification
  1911  	// until we call UpdateSpendDetails.
  1912  	ntfn, err := n.RegisterSpend(&op, testRawScript, 1)
  1913  	if err != nil {
  1914  		t.Fatalf("unable to register spend: %v", err)
  1915  	}
  1916  
  1917  	// Assert that the HistoricalDispatch variable is non-nil. We'll use
  1918  	// the SpendRequest member to update the spend details.
  1919  	require.NotEmpty(t, ntfn.HistoricalDispatch)
  1920  
  1921  	select {
  1922  	case <-ntfn.Event.Spend:
  1923  		t.Fatalf("did not expect to receive spend ntfn")
  1924  	default:
  1925  	}
  1926  
  1927  	// We now call UpdateSpendDetails with our generated spend details to
  1928  	// simulate a historical spend dispatch being performed. This should
  1929  	// result in a notification being received on the Spend channel.
  1930  	err = n.UpdateSpendDetails(
  1931  		ntfn.HistoricalDispatch.SpendRequest, spendDetails,
  1932  	)
  1933  	require.Empty(t, err)
  1934  
  1935  	// Assert that we receive a Spend notification.
  1936  	select {
  1937  	case <-ntfn.Event.Spend:
  1938  	default:
  1939  		t.Fatalf("expected to receive spend ntfn")
  1940  	}
  1941  
  1942  	// We will now re-org the spending transaction out of the chain, and we
  1943  	// should receive a notification on the Reorg channel.
  1944  	err = n.DisconnectTip(startingHeight + 1)
  1945  	require.Empty(t, err)
  1946  
  1947  	select {
  1948  	case <-ntfn.Event.Spend:
  1949  		t.Fatalf("received unexpected spend ntfn")
  1950  	case <-ntfn.Event.Reorg:
  1951  	default:
  1952  		t.Fatalf("expected spend reorg ntfn")
  1953  	}
  1954  }
  1955  
  1956  // TestTxNotifierConfirmHintCache ensures that the height hints for transactions
  1957  // are kept track of correctly with each new block connected/disconnected. This
  1958  // test also asserts that the height hints are not updated until the simulated
  1959  // historical dispatches have returned, and we know the transactions aren't
  1960  // already in the chain.
  1961  func TestTxNotifierConfirmHintCache(t *testing.T) {
  1962  	t.Parallel()
  1963  
  1964  	const (
  1965  		startingHeight = 200
  1966  		txDummyHeight  = 201
  1967  		tx1Height      = 202
  1968  		tx2Height      = 203
  1969  	)
  1970  
  1971  	// Initialize our TxNotifier instance backed by a height hint cache.
  1972  	hintCache := newMockHintCache()
  1973  	n := chainntnfs.NewTxNotifier(
  1974  		startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
  1975  		hintCache, testChainParams,
  1976  	)
  1977  
  1978  	// Create two test transactions and register them for notifications.
  1979  	tx1 := *newWireTxWithVersion(1)
  1980  	tx1.AddTxOut(&wire.TxOut{PkScript: testRawScript})
  1981  	tx1Hash := tx1.TxHash()
  1982  	ntfn1, err := n.RegisterConf(&tx1Hash, testRawScript, 1, 1)
  1983  	if err != nil {
  1984  		t.Fatalf("unable to register tx1: %v", err)
  1985  	}
  1986  
  1987  	tx2 := *newWireTxWithVersion(2)
  1988  	tx2.AddTxOut(&wire.TxOut{PkScript: testRawScript})
  1989  	tx2Hash := tx2.TxHash()
  1990  	ntfn2, err := n.RegisterConf(&tx2Hash, testRawScript, 2, 1)
  1991  	if err != nil {
  1992  		t.Fatalf("unable to register tx2: %v", err)
  1993  	}
  1994  
  1995  	// Both transactions should not have a height hint set, as RegisterConf
  1996  	// should not alter the cache state.
  1997  	_, err = hintCache.QueryConfirmHint(ntfn1.HistoricalDispatch.ConfRequest)
  1998  	if err != chainntnfs.ErrConfirmHintNotFound {
  1999  		t.Fatalf("unexpected error when querying for height hint "+
  2000  			"want: %v, got %v",
  2001  			chainntnfs.ErrConfirmHintNotFound, err)
  2002  	}
  2003  
  2004  	_, err = hintCache.QueryConfirmHint(ntfn2.HistoricalDispatch.ConfRequest)
  2005  	if err != chainntnfs.ErrConfirmHintNotFound {
  2006  		t.Fatalf("unexpected error when querying for height hint "+
  2007  			"want: %v, got %v",
  2008  			chainntnfs.ErrConfirmHintNotFound, err)
  2009  	}
  2010  
  2011  	// Create a new block that will include the dummy transaction and extend
  2012  	// the chain.
  2013  	txDummy := *newWireTxWithVersion(3)
  2014  	block1 := dcrutil.NewBlock(&wire.MsgBlock{
  2015  		Transactions: []*wire.MsgTx{&txDummy},
  2016  	})
  2017  
  2018  	err = n.ConnectTip(block1.Hash(), txDummyHeight, block1.Transactions())
  2019  	if err != nil {
  2020  		t.Fatalf("Failed to connect block: %v", err)
  2021  	}
  2022  	if err := n.NotifyHeight(txDummyHeight); err != nil {
  2023  		t.Fatalf("unable to dispatch notifications: %v", err)
  2024  	}
  2025  
  2026  	// Since UpdateConfDetails has not been called for either transaction,
  2027  	// the height hints should remain unchanged. This simulates blocks
  2028  	// confirming while the historical dispatch is processing the
  2029  	// registration.
  2030  	_, err = hintCache.QueryConfirmHint(ntfn1.HistoricalDispatch.ConfRequest)
  2031  	if err != chainntnfs.ErrConfirmHintNotFound {
  2032  		t.Fatalf("unexpected error when querying for height hint "+
  2033  			"want: %v, got %v",
  2034  			chainntnfs.ErrConfirmHintNotFound, err)
  2035  	}
  2036  
  2037  	_, err = hintCache.QueryConfirmHint(ntfn2.HistoricalDispatch.ConfRequest)
  2038  	if err != chainntnfs.ErrConfirmHintNotFound {
  2039  		t.Fatalf("unexpected error when querying for height hint "+
  2040  			"want: %v, got %v",
  2041  			chainntnfs.ErrConfirmHintNotFound, err)
  2042  	}
  2043  
  2044  	// Now, update the conf details reporting that the neither txn was found
  2045  	// in the historical dispatch.
  2046  	err = n.UpdateConfDetails(ntfn1.HistoricalDispatch.ConfRequest, nil)
  2047  	if err != nil {
  2048  		t.Fatalf("unable to update conf details: %v", err)
  2049  	}
  2050  	err = n.UpdateConfDetails(ntfn2.HistoricalDispatch.ConfRequest, nil)
  2051  	if err != nil {
  2052  		t.Fatalf("unable to update conf details: %v", err)
  2053  	}
  2054  
  2055  	// We'll create another block that will include the first transaction
  2056  	// and extend the chain.
  2057  	block2 := dcrutil.NewBlock(&wire.MsgBlock{
  2058  		Transactions: []*wire.MsgTx{&tx1},
  2059  	})
  2060  
  2061  	err = n.ConnectTip(block2.Hash(), tx1Height, block2.Transactions())
  2062  	if err != nil {
  2063  		t.Fatalf("Failed to connect block: %v", err)
  2064  	}
  2065  	if err := n.NotifyHeight(tx1Height); err != nil {
  2066  		t.Fatalf("unable to dispatch notifications: %v", err)
  2067  	}
  2068  
  2069  	// Now that both notifications are waiting at tip for confirmations,
  2070  	// they should have their height hints updated to the latest block
  2071  	// height.
  2072  	hint, err := hintCache.QueryConfirmHint(ntfn1.HistoricalDispatch.ConfRequest)
  2073  	if err != nil {
  2074  		t.Fatalf("unable to query for hint: %v", err)
  2075  	}
  2076  	if hint != tx1Height {
  2077  		t.Fatalf("expected hint %d, got %d",
  2078  			tx1Height, hint)
  2079  	}
  2080  
  2081  	hint, err = hintCache.QueryConfirmHint(ntfn2.HistoricalDispatch.ConfRequest)
  2082  	if err != nil {
  2083  		t.Fatalf("unable to query for hint: %v", err)
  2084  	}
  2085  	if hint != tx1Height {
  2086  		t.Fatalf("expected hint %d, got %d",
  2087  			tx2Height, hint)
  2088  	}
  2089  
  2090  	// Next, we'll create another block that will include the second
  2091  	// transaction and extend the chain.
  2092  	block3 := dcrutil.NewBlock(&wire.MsgBlock{
  2093  		Transactions: []*wire.MsgTx{&tx2},
  2094  	})
  2095  
  2096  	err = n.ConnectTip(block3.Hash(), tx2Height, block3.Transactions())
  2097  	if err != nil {
  2098  		t.Fatalf("Failed to connect block: %v", err)
  2099  	}
  2100  	if err := n.NotifyHeight(tx2Height); err != nil {
  2101  		t.Fatalf("unable to dispatch notifications: %v", err)
  2102  	}
  2103  
  2104  	// The height hint for the first transaction should remain the same.
  2105  	hint, err = hintCache.QueryConfirmHint(ntfn1.HistoricalDispatch.ConfRequest)
  2106  	if err != nil {
  2107  		t.Fatalf("unable to query for hint: %v", err)
  2108  	}
  2109  	if hint != tx1Height {
  2110  		t.Fatalf("expected hint %d, got %d",
  2111  			tx1Height, hint)
  2112  	}
  2113  
  2114  	// The height hint for the second transaction should now be updated to
  2115  	// reflect its confirmation.
  2116  	hint, err = hintCache.QueryConfirmHint(ntfn2.HistoricalDispatch.ConfRequest)
  2117  	if err != nil {
  2118  		t.Fatalf("unable to query for hint: %v", err)
  2119  	}
  2120  	if hint != tx2Height {
  2121  		t.Fatalf("expected hint %d, got %d",
  2122  			tx2Height, hint)
  2123  	}
  2124  
  2125  	// Finally, we'll attempt do disconnect the last block in order to
  2126  	// simulate a chain reorg.
  2127  	if err := n.DisconnectTip(tx2Height); err != nil {
  2128  		t.Fatalf("Failed to disconnect block: %v", err)
  2129  	}
  2130  
  2131  	// This should update the second transaction's height hint within the
  2132  	// cache to the previous height.
  2133  	hint, err = hintCache.QueryConfirmHint(ntfn2.HistoricalDispatch.ConfRequest)
  2134  	if err != nil {
  2135  		t.Fatalf("unable to query for hint: %v", err)
  2136  	}
  2137  	if hint != tx1Height {
  2138  		t.Fatalf("expected hint %d, got %d",
  2139  			tx1Height, hint)
  2140  	}
  2141  
  2142  	// The first transaction's height hint should remain at the original
  2143  	// confirmation height.
  2144  	hint, err = hintCache.QueryConfirmHint(ntfn2.HistoricalDispatch.ConfRequest)
  2145  	if err != nil {
  2146  		t.Fatalf("unable to query for hint: %v", err)
  2147  	}
  2148  	if hint != tx1Height {
  2149  		t.Fatalf("expected hint %d, got %d",
  2150  			tx1Height, hint)
  2151  	}
  2152  }
  2153  
  2154  // TestTxNotifierSpendHintCache ensures that the height hints for outpoints are
  2155  // kept track of correctly with each new block connected/disconnected. This test
  2156  // also asserts that the height hints are not updated until the simulated
  2157  // historical dispatches have returned, and we know the outpoints haven't
  2158  // already been spent in the chain.
  2159  func TestTxNotifierSpendHintCache(t *testing.T) {
  2160  	t.Parallel()
  2161  
  2162  	const (
  2163  		startingHeight = 200
  2164  		dummyHeight    = 201
  2165  		op1Height      = 202
  2166  		op2Height      = 203
  2167  	)
  2168  
  2169  	// Intiialize our TxNotifier instance backed by a height hint cache.
  2170  	hintCache := newMockHintCache()
  2171  	n := chainntnfs.NewTxNotifier(
  2172  		startingHeight, chainntnfs.ReorgSafetyLimit, hintCache,
  2173  		hintCache, testChainParams,
  2174  	)
  2175  
  2176  	// Create two test outpoints and register them for spend notifications.
  2177  	op1 := wire.OutPoint{Index: 1}
  2178  	ntfn1, err := n.RegisterSpend(&op1, testRawScript, 1)
  2179  	if err != nil {
  2180  		t.Fatalf("unable to register spend for op1: %v", err)
  2181  	}
  2182  	op2 := wire.OutPoint{Index: 2}
  2183  	ntfn2, err := n.RegisterSpend(&op2, testRawScript, 1)
  2184  	if err != nil {
  2185  		t.Fatalf("unable to register spend for op2: %v", err)
  2186  	}
  2187  
  2188  	// Both outpoints should not have a spend hint set upon registration, as
  2189  	// we must first determine whether they have already been spent in the
  2190  	// chain.
  2191  	_, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
  2192  	if err != chainntnfs.ErrSpendHintNotFound {
  2193  		t.Fatalf("unexpected error when querying for height hint "+
  2194  			"expected: %v, got %v", chainntnfs.ErrSpendHintNotFound,
  2195  			err)
  2196  	}
  2197  	_, err = hintCache.QuerySpendHint(ntfn2.HistoricalDispatch.SpendRequest)
  2198  	if err != chainntnfs.ErrSpendHintNotFound {
  2199  		t.Fatalf("unexpected error when querying for height hint "+
  2200  			"expected: %v, got %v", chainntnfs.ErrSpendHintNotFound,
  2201  			err)
  2202  	}
  2203  
  2204  	// Create a new empty block and extend the chain.
  2205  	emptyBlock := dcrutil.NewBlock(&wire.MsgBlock{})
  2206  	err = n.ConnectTip(
  2207  		emptyBlock.Hash(), dummyHeight, emptyBlock.Transactions(),
  2208  	)
  2209  	if err != nil {
  2210  		t.Fatalf("unable to connect block: %v", err)
  2211  	}
  2212  	if err := n.NotifyHeight(dummyHeight); err != nil {
  2213  		t.Fatalf("unable to dispatch notifications: %v", err)
  2214  	}
  2215  
  2216  	// Since we haven't called UpdateSpendDetails on any of the test
  2217  	// outpoints, this implies that there is a still a pending historical
  2218  	// rescan for them, so their spend hints should not be created/updated.
  2219  	_, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
  2220  	if err != chainntnfs.ErrSpendHintNotFound {
  2221  		t.Fatalf("unexpected error when querying for height hint "+
  2222  			"expected: %v, got %v", chainntnfs.ErrSpendHintNotFound,
  2223  			err)
  2224  	}
  2225  	_, err = hintCache.QuerySpendHint(ntfn2.HistoricalDispatch.SpendRequest)
  2226  	if err != chainntnfs.ErrSpendHintNotFound {
  2227  		t.Fatalf("unexpected error when querying for height hint "+
  2228  			"expected: %v, got %v", chainntnfs.ErrSpendHintNotFound,
  2229  			err)
  2230  	}
  2231  
  2232  	// Now, we'll simulate that their historical rescans have finished by
  2233  	// calling UpdateSpendDetails. This should allow their spend hints to be
  2234  	// updated upon every block connected/disconnected.
  2235  	err = n.UpdateSpendDetails(ntfn1.HistoricalDispatch.SpendRequest, nil)
  2236  	if err != nil {
  2237  		t.Fatalf("unable to update spend details: %v", err)
  2238  	}
  2239  	err = n.UpdateSpendDetails(ntfn2.HistoricalDispatch.SpendRequest, nil)
  2240  	if err != nil {
  2241  		t.Fatalf("unable to update spend details: %v", err)
  2242  	}
  2243  
  2244  	// We'll create a new block that only contains the spending transaction
  2245  	// of the first outpoint.
  2246  	spendTx1 := newWireTxWithVersion(2)
  2247  	spendTx1.AddTxIn(&wire.TxIn{
  2248  		PreviousOutPoint: op1,
  2249  		SignatureScript:  testSigScript,
  2250  	})
  2251  	block1 := dcrutil.NewBlock(&wire.MsgBlock{
  2252  		Transactions: []*wire.MsgTx{spendTx1},
  2253  	})
  2254  	err = n.ConnectTip(block1.Hash(), op1Height, block1.Transactions())
  2255  	if err != nil {
  2256  		t.Fatalf("unable to connect block: %v", err)
  2257  	}
  2258  	if err := n.NotifyHeight(op1Height); err != nil {
  2259  		t.Fatalf("unable to dispatch notifications: %v", err)
  2260  	}
  2261  
  2262  	// Both outpoints should have their spend hints reflect the height of
  2263  	// the new block being connected due to the first outpoint being spent
  2264  	// at this height, and the second outpoint still being unspent.
  2265  	op1Hint, err := hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
  2266  	if err != nil {
  2267  		t.Fatalf("unable to query for spend hint of op1: %v", err)
  2268  	}
  2269  	if op1Hint != op1Height {
  2270  		t.Fatalf("expected hint %d, got %d", op1Height, op1Hint)
  2271  	}
  2272  	op2Hint, err := hintCache.QuerySpendHint(ntfn2.HistoricalDispatch.SpendRequest)
  2273  	if err != nil {
  2274  		t.Fatalf("unable to query for spend hint of op2: %v", err)
  2275  	}
  2276  	if op2Hint != op1Height {
  2277  		t.Fatalf("expected hint %d, got %d", op1Height, op2Hint)
  2278  	}
  2279  
  2280  	// Then, we'll create another block that spends the second outpoint.
  2281  	spendTx2 := newWireTxWithVersion(2)
  2282  	spendTx2.AddTxIn(&wire.TxIn{
  2283  		PreviousOutPoint: op2,
  2284  		SignatureScript:  testSigScript,
  2285  	})
  2286  	block2 := dcrutil.NewBlock(&wire.MsgBlock{
  2287  		Transactions: []*wire.MsgTx{spendTx2},
  2288  	})
  2289  	err = n.ConnectTip(block2.Hash(), op2Height, block2.Transactions())
  2290  	if err != nil {
  2291  		t.Fatalf("unable to connect block: %v", err)
  2292  	}
  2293  	if err := n.NotifyHeight(op2Height); err != nil {
  2294  		t.Fatalf("unable to dispatch notifications: %v", err)
  2295  	}
  2296  
  2297  	// Only the second outpoint should have its spend hint updated due to
  2298  	// being spent within the new block. The first outpoint's spend hint
  2299  	// should remain the same as it's already been spent before.
  2300  	op1Hint, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
  2301  	if err != nil {
  2302  		t.Fatalf("unable to query for spend hint of op1: %v", err)
  2303  	}
  2304  	if op1Hint != op1Height {
  2305  		t.Fatalf("expected hint %d, got %d", op1Height, op1Hint)
  2306  	}
  2307  	op2Hint, err = hintCache.QuerySpendHint(ntfn2.HistoricalDispatch.SpendRequest)
  2308  	if err != nil {
  2309  		t.Fatalf("unable to query for spend hint of op2: %v", err)
  2310  	}
  2311  	if op2Hint != op2Height {
  2312  		t.Fatalf("expected hint %d, got %d", op2Height, op2Hint)
  2313  	}
  2314  
  2315  	// Finally, we'll attempt do disconnect the last block in order to
  2316  	// simulate a chain reorg.
  2317  	if err := n.DisconnectTip(op2Height); err != nil {
  2318  		t.Fatalf("unable to disconnect block: %v", err)
  2319  	}
  2320  
  2321  	// This should update the second outpoint's spend hint within the cache
  2322  	// to the previous height, as that's where its spending transaction was
  2323  	// included in within the chain. The first outpoint's spend hint should
  2324  	// remain the same.
  2325  	op1Hint, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
  2326  	if err != nil {
  2327  		t.Fatalf("unable to query for spend hint of op1: %v", err)
  2328  	}
  2329  	if op1Hint != op1Height {
  2330  		t.Fatalf("expected hint %d, got %d", op1Height, op1Hint)
  2331  	}
  2332  	op2Hint, err = hintCache.QuerySpendHint(ntfn2.HistoricalDispatch.SpendRequest)
  2333  	if err != nil {
  2334  		t.Fatalf("unable to query for spend hint of op2: %v", err)
  2335  	}
  2336  	if op2Hint != op1Height {
  2337  		t.Fatalf("expected hint %d, got %d", op1Height, op2Hint)
  2338  	}
  2339  }
  2340  
  2341  // TestTxNotifierSpendHinthistoricalRescan checks that the height hints and
  2342  // spend notifications behave as expected when a spend is found at tip during a
  2343  // historical rescan.
  2344  func TestTxNotifierSpendDuringHistoricalRescan(t *testing.T) {
  2345  	t.Parallel()
  2346  
  2347  	const (
  2348  		startingHeight = 200
  2349  		reorgSafety    = 10
  2350  	)
  2351  
  2352  	// Intiialize our TxNotifier instance backed by a height hint cache.
  2353  	hintCache := newMockHintCache()
  2354  	n := chainntnfs.NewTxNotifier(
  2355  		startingHeight, reorgSafety, hintCache, hintCache,
  2356  		testChainParams,
  2357  	)
  2358  
  2359  	// Create a test outpoint and register it for spend notifications.
  2360  	op1 := wire.OutPoint{Index: 1}
  2361  	ntfn1, err := n.RegisterSpend(&op1, testRawScript, 1)
  2362  	if err != nil {
  2363  		t.Fatalf("unable to register spend for op1: %v", err)
  2364  	}
  2365  
  2366  	// A historical rescan should be initiated from the height hint to the
  2367  	// current height.
  2368  	if ntfn1.HistoricalDispatch.StartHeight != 1 {
  2369  		t.Fatalf("expected historical dispatch to start at height hint")
  2370  	}
  2371  
  2372  	if ntfn1.HistoricalDispatch.EndHeight != startingHeight {
  2373  		t.Fatalf("expected historical dispatch to end at current height")
  2374  	}
  2375  
  2376  	// It should not have a spend hint set upon registration, as we must
  2377  	// first determine whether it has already been spent in the chain.
  2378  	_, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
  2379  	if err != chainntnfs.ErrSpendHintNotFound {
  2380  		t.Fatalf("unexpected error when querying for height hint "+
  2381  			"expected: %v, got %v", chainntnfs.ErrSpendHintNotFound,
  2382  			err)
  2383  	}
  2384  
  2385  	// Create a new empty block and extend the chain.
  2386  	height := uint32(startingHeight) + 1
  2387  	emptyBlock := dcrutil.NewBlock(&wire.MsgBlock{})
  2388  	err = n.ConnectTip(
  2389  		emptyBlock.Hash(), height, emptyBlock.Transactions(),
  2390  	)
  2391  	if err != nil {
  2392  		t.Fatalf("unable to connect block: %v", err)
  2393  	}
  2394  	if err := n.NotifyHeight(height); err != nil {
  2395  		t.Fatalf("unable to dispatch notifications: %v", err)
  2396  	}
  2397  
  2398  	// Since we haven't called UpdateSpendDetails yet, there should be no
  2399  	// spend hint found.
  2400  	_, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
  2401  	if err != chainntnfs.ErrSpendHintNotFound {
  2402  		t.Fatalf("unexpected error when querying for height hint "+
  2403  			"expected: %v, got %v", chainntnfs.ErrSpendHintNotFound,
  2404  			err)
  2405  	}
  2406  
  2407  	// Simulate a bunch of blocks being mined while the historical rescan
  2408  	// is still in progress. We make sure to not mine more than reorgSafety
  2409  	// blocks after the spend, since it will be forgotten then.
  2410  	var spendHeight uint32
  2411  	for i := 0; i < reorgSafety; i++ {
  2412  		height++
  2413  
  2414  		// Let the outpoint we are watching be spent midway.
  2415  		var block *dcrutil.Block
  2416  		if i == 5 {
  2417  			// We'll create a new block that only contains the
  2418  			// spending transaction of the outpoint.
  2419  			spendTx1 := newWireTxWithVersion(2)
  2420  			spendTx1.AddTxIn(&wire.TxIn{
  2421  				PreviousOutPoint: op1,
  2422  				SignatureScript:  testSigScript,
  2423  			})
  2424  			block = dcrutil.NewBlock(&wire.MsgBlock{
  2425  				Transactions: []*wire.MsgTx{spendTx1},
  2426  			})
  2427  			spendHeight = height
  2428  		} else {
  2429  			// Otherwise we just create an empty block.
  2430  			block = dcrutil.NewBlock(&wire.MsgBlock{})
  2431  		}
  2432  
  2433  		err = n.ConnectTip(
  2434  			block.Hash(), height, block.Transactions(),
  2435  		)
  2436  		if err != nil {
  2437  			t.Fatalf("unable to connect block: %v", err)
  2438  		}
  2439  		if err := n.NotifyHeight(height); err != nil {
  2440  			t.Fatalf("unable to dispatch notifications: %v", err)
  2441  		}
  2442  	}
  2443  
  2444  	// Check that the height hint was set to the spending block.
  2445  	op1Hint, err := hintCache.QuerySpendHint(
  2446  		ntfn1.HistoricalDispatch.SpendRequest,
  2447  	)
  2448  	if err != nil {
  2449  		t.Fatalf("unable to query for spend hint of op1: %v", err)
  2450  	}
  2451  	if op1Hint != spendHeight {
  2452  		t.Fatalf("expected hint %d, got %d", spendHeight, op1Hint)
  2453  	}
  2454  
  2455  	// We should be getting notified about the spend at this point.
  2456  	select {
  2457  	case <-ntfn1.Event.Spend:
  2458  	default:
  2459  		t.Fatal("expected to receive spend notification")
  2460  	}
  2461  
  2462  	// Now, we'll simulate that the historical rescan finished by
  2463  	// calling UpdateSpendDetails. Since a the spend actually happened at
  2464  	// tip while the rescan was in progress, the height hint should not be
  2465  	// updated to the latest height, but stay at the spend height.
  2466  	err = n.UpdateSpendDetails(ntfn1.HistoricalDispatch.SpendRequest, nil)
  2467  	if err != nil {
  2468  		t.Fatalf("unable to update spend details: %v", err)
  2469  	}
  2470  
  2471  	op1Hint, err = hintCache.QuerySpendHint(
  2472  		ntfn1.HistoricalDispatch.SpendRequest,
  2473  	)
  2474  	if err != nil {
  2475  		t.Fatalf("unable to query for spend hint of op1: %v", err)
  2476  	}
  2477  	if op1Hint != spendHeight {
  2478  		t.Fatalf("expected hint %d, got %d", spendHeight, op1Hint)
  2479  	}
  2480  
  2481  	// Then, we'll create another block that spends a second outpoint.
  2482  	op2 := wire.OutPoint{Index: 2}
  2483  	spendTx2 := newWireTxWithVersion(2)
  2484  	spendTx2.AddTxIn(&wire.TxIn{
  2485  		PreviousOutPoint: op2,
  2486  		SignatureScript:  testSigScript,
  2487  	})
  2488  	height++
  2489  	block2 := dcrutil.NewBlock(&wire.MsgBlock{
  2490  		Transactions: []*wire.MsgTx{spendTx2},
  2491  	})
  2492  	err = n.ConnectTip(block2.Hash(), height, block2.Transactions())
  2493  	if err != nil {
  2494  		t.Fatalf("unable to connect block: %v", err)
  2495  	}
  2496  	if err := n.NotifyHeight(height); err != nil {
  2497  		t.Fatalf("unable to dispatch notifications: %v", err)
  2498  	}
  2499  
  2500  	// The outpoint's spend hint should remain the same as it's already
  2501  	// been spent before.
  2502  	op1Hint, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
  2503  	if err != nil {
  2504  		t.Fatalf("unable to query for spend hint of op1: %v", err)
  2505  	}
  2506  	if op1Hint != spendHeight {
  2507  		t.Fatalf("expected hint %d, got %d", spendHeight, op1Hint)
  2508  	}
  2509  
  2510  	// Now mine enough blocks for the spend notification to be forgotten.
  2511  	for i := 0; i < 2*reorgSafety; i++ {
  2512  		height++
  2513  		block := dcrutil.NewBlock(&wire.MsgBlock{})
  2514  
  2515  		err := n.ConnectTip(
  2516  			block.Hash(), height, block.Transactions(),
  2517  		)
  2518  		if err != nil {
  2519  			t.Fatalf("unable to connect block: %v", err)
  2520  		}
  2521  		if err := n.NotifyHeight(height); err != nil {
  2522  			t.Fatalf("unable to dispatch notifications: %v", err)
  2523  		}
  2524  	}
  2525  
  2526  	// Attempting to update spend details at this point should fail, since
  2527  	// the spend request should be removed. This is to ensure the height
  2528  	// hint won't be overwritten if the historical rescan finishes after
  2529  	// the spend request has been notified and removed because it has
  2530  	// matured.
  2531  	err = n.UpdateSpendDetails(ntfn1.HistoricalDispatch.SpendRequest, nil)
  2532  	if err == nil {
  2533  		t.Fatalf("expcted updating spend details to fail")
  2534  	}
  2535  
  2536  	// Finally, check that the height hint is still there, unchanged.
  2537  	op1Hint, err = hintCache.QuerySpendHint(ntfn1.HistoricalDispatch.SpendRequest)
  2538  	if err != nil {
  2539  		t.Fatalf("unable to query for spend hint of op1: %v", err)
  2540  	}
  2541  	if op1Hint != spendHeight {
  2542  		t.Fatalf("expected hint %d, got %d", spendHeight, op1Hint)
  2543  	}
  2544  }
  2545  
  2546  // TestTxNotifierNtfnDone ensures that a notification is sent to registered
  2547  // clients through the Done channel once the notification request is no longer
  2548  // under the risk of being reorged out of the chain.
  2549  func TestTxNotifierNtfnDone(t *testing.T) {
  2550  	t.Parallel()
  2551  
  2552  	hintCache := newMockHintCache()
  2553  	const reorgSafetyLimit = 100
  2554  	n := chainntnfs.NewTxNotifier(
  2555  		10, reorgSafetyLimit, hintCache, hintCache, testChainParams,
  2556  	)
  2557  
  2558  	// We'll start by creating two notification requests: one confirmation
  2559  	// and one spend.
  2560  	confNtfn, err := n.RegisterConf(&chainntnfs.ZeroHash, testRawScript, 1, 1)
  2561  	if err != nil {
  2562  		t.Fatalf("unable to register conf ntfn: %v", err)
  2563  	}
  2564  	spendNtfn, err := n.RegisterSpend(&chainntnfs.ZeroOutPoint, testRawScript, 1)
  2565  	if err != nil {
  2566  		t.Fatalf("unable to register spend: %v", err)
  2567  	}
  2568  
  2569  	// We'll create two transactions that will satisfy the notification
  2570  	// requests above and include them in the next block of the chain.
  2571  	tx := newWireTxWithVersion(1)
  2572  	tx.AddTxOut(&wire.TxOut{PkScript: testRawScript})
  2573  	spendTx := newWireTxWithVersion(1)
  2574  	spendTx.AddTxIn(&wire.TxIn{
  2575  		PreviousOutPoint: wire.OutPoint{Index: 1},
  2576  		SignatureScript:  testSigScript,
  2577  	})
  2578  	block := dcrutil.NewBlock(&wire.MsgBlock{
  2579  		Transactions: []*wire.MsgTx{tx, spendTx},
  2580  	})
  2581  
  2582  	err = n.ConnectTip(block.Hash(), 11, block.Transactions())
  2583  	if err != nil {
  2584  		t.Fatalf("unable to connect block: %v", err)
  2585  	}
  2586  	if err := n.NotifyHeight(11); err != nil {
  2587  		t.Fatalf("unable to dispatch notifications: %v", err)
  2588  	}
  2589  
  2590  	// With the chain extended, we should see notifications dispatched for
  2591  	// both requests.
  2592  	select {
  2593  	case <-confNtfn.Event.Confirmed:
  2594  	default:
  2595  		t.Fatal("expected to receive confirmation notification")
  2596  	}
  2597  
  2598  	select {
  2599  	case <-spendNtfn.Event.Spend:
  2600  	default:
  2601  		t.Fatal("expected to receive spend notification")
  2602  	}
  2603  
  2604  	// The done notifications should not be dispatched yet as the requests
  2605  	// are still under the risk of being reorged out the chain.
  2606  	select {
  2607  	case <-confNtfn.Event.Done:
  2608  		t.Fatal("received unexpected done notification for confirmation")
  2609  	case <-spendNtfn.Event.Done:
  2610  		t.Fatal("received unexpected done notification for spend")
  2611  	default:
  2612  	}
  2613  
  2614  	// Now, we'll disconnect the block at tip to simulate a reorg. The reorg
  2615  	// notifications should be dispatched to the respective clients.
  2616  	if err := n.DisconnectTip(11); err != nil {
  2617  		t.Fatalf("unable to disconnect block: %v", err)
  2618  	}
  2619  
  2620  	select {
  2621  	case <-confNtfn.Event.NegativeConf:
  2622  	default:
  2623  		t.Fatal("expected to receive reorg notification for confirmation")
  2624  	}
  2625  
  2626  	select {
  2627  	case <-spendNtfn.Event.Reorg:
  2628  	default:
  2629  		t.Fatal("expected to receive reorg notification for spend")
  2630  	}
  2631  
  2632  	// We'll reconnect the block that satisfies both of these requests.
  2633  	// We should see notifications dispatched for both once again.
  2634  	err = n.ConnectTip(block.Hash(), 11, block.Transactions())
  2635  	if err != nil {
  2636  		t.Fatalf("unable to connect block: %v", err)
  2637  	}
  2638  	if err := n.NotifyHeight(11); err != nil {
  2639  		t.Fatalf("unable to dispatch notifications: %v", err)
  2640  	}
  2641  
  2642  	select {
  2643  	case <-confNtfn.Event.Confirmed:
  2644  	default:
  2645  		t.Fatal("expected to receive confirmation notification")
  2646  	}
  2647  
  2648  	select {
  2649  	case <-spendNtfn.Event.Spend:
  2650  	default:
  2651  		t.Fatal("expected to receive spend notification")
  2652  	}
  2653  
  2654  	// Finally, we'll extend the chain with blocks until the requests are no
  2655  	// longer under the risk of being reorged out of the chain. We should
  2656  	// expect the done notifications to be dispatched.
  2657  	nextHeight := uint32(12)
  2658  	for i := nextHeight; i < nextHeight+reorgSafetyLimit; i++ {
  2659  		dummyBlock := dcrutil.NewBlock(&wire.MsgBlock{})
  2660  		if err := n.ConnectTip(dummyBlock.Hash(), i, nil); err != nil {
  2661  			t.Fatalf("unable to connect block: %v", err)
  2662  		}
  2663  	}
  2664  
  2665  	select {
  2666  	case <-confNtfn.Event.Done:
  2667  	default:
  2668  		t.Fatal("expected to receive done notification for confirmation")
  2669  	}
  2670  
  2671  	select {
  2672  	case <-spendNtfn.Event.Done:
  2673  	default:
  2674  		t.Fatal("expected to receive done notification for spend")
  2675  	}
  2676  }
  2677  
  2678  // TestTxNotifierTearDown ensures that the TxNotifier properly alerts clients
  2679  // that it is shutting down and will be unable to deliver notifications.
  2680  func TestTxNotifierTearDown(t *testing.T) {
  2681  	t.Parallel()
  2682  
  2683  	hintCache := newMockHintCache()
  2684  	n := chainntnfs.NewTxNotifier(
  2685  		10, chainntnfs.ReorgSafetyLimit, hintCache, hintCache, testChainParams,
  2686  	)
  2687  
  2688  	// To begin the test, we'll register for a confirmation and spend
  2689  	// notification.
  2690  	confNtfn, err := n.RegisterConf(&chainntnfs.ZeroHash, testRawScript, 1, 1)
  2691  	if err != nil {
  2692  		t.Fatalf("unable to register conf ntfn: %v", err)
  2693  	}
  2694  	spendNtfn, err := n.RegisterSpend(&chainntnfs.ZeroOutPoint, testRawScript, 1)
  2695  	if err != nil {
  2696  		t.Fatalf("unable to register spend ntfn: %v", err)
  2697  	}
  2698  
  2699  	// With the notifications registered, we'll now tear down the notifier.
  2700  	// The notification channels should be closed for notifications, whether
  2701  	// they have been dispatched or not, so we should not expect to receive
  2702  	// any more updates.
  2703  	n.TearDown()
  2704  
  2705  	select {
  2706  	case _, ok := <-confNtfn.Event.Confirmed:
  2707  		if ok {
  2708  			t.Fatal("expected closed Confirmed channel for conf ntfn")
  2709  		}
  2710  	case _, ok := <-confNtfn.Event.Updates:
  2711  		if ok {
  2712  			t.Fatal("expected closed Updates channel for conf ntfn")
  2713  		}
  2714  	case _, ok := <-confNtfn.Event.NegativeConf:
  2715  		if ok {
  2716  			t.Fatal("expected closed NegativeConf channel for conf ntfn")
  2717  		}
  2718  	case _, ok := <-spendNtfn.Event.Spend:
  2719  		if ok {
  2720  			t.Fatal("expected closed Spend channel for spend ntfn")
  2721  		}
  2722  	case _, ok := <-spendNtfn.Event.Reorg:
  2723  		if ok {
  2724  			t.Fatalf("expected closed Reorg channel for spend ntfn")
  2725  		}
  2726  	default:
  2727  		t.Fatalf("expected closed notification channels for all ntfns")
  2728  	}
  2729  
  2730  	// Now that the notifier is torn down, we should no longer be able to
  2731  	// register notification requests.
  2732  	_, err = n.RegisterConf(&chainntnfs.ZeroHash, testRawScript, 1, 1)
  2733  	if err == nil {
  2734  		t.Fatal("expected confirmation registration to fail")
  2735  	}
  2736  	_, err = n.RegisterSpend(&chainntnfs.ZeroOutPoint, testRawScript, 1)
  2737  	if err == nil {
  2738  		t.Fatal("expected spend registration to fail")
  2739  	}
  2740  }
  2741  
  2742  func assertConfDetails(t *testing.T, result, expected *chainntnfs.TxConfirmation) {
  2743  	t.Helper()
  2744  
  2745  	if result.BlockHeight != expected.BlockHeight {
  2746  		t.Fatalf("Incorrect block height in confirmation details: "+
  2747  			"expected %d, got %d", expected.BlockHeight,
  2748  			result.BlockHeight)
  2749  	}
  2750  	if !result.BlockHash.IsEqual(expected.BlockHash) {
  2751  		t.Fatalf("Incorrect block hash in confirmation details: "+
  2752  			"expected %d, got %d", expected.BlockHash,
  2753  			result.BlockHash)
  2754  	}
  2755  	if result.TxIndex != expected.TxIndex {
  2756  		t.Fatalf("Incorrect tx index in confirmation details: "+
  2757  			"expected %d, got %d", expected.TxIndex, result.TxIndex)
  2758  	}
  2759  	if expected.Tx == nil {
  2760  		return
  2761  	}
  2762  	if result.Tx.TxHash() != expected.Tx.TxHash() {
  2763  		t.Fatalf("expected tx hash %v, got %v", expected.Tx.TxHash(),
  2764  			result.Tx.TxHash())
  2765  	}
  2766  }
  2767  
  2768  func assertSpendDetails(t *testing.T, result, expected *chainntnfs.SpendDetail) {
  2769  	t.Helper()
  2770  
  2771  	if *result.SpentOutPoint != *expected.SpentOutPoint {
  2772  		t.Fatalf("expected spent outpoint %v, got %v",
  2773  			expected.SpentOutPoint, result.SpentOutPoint)
  2774  	}
  2775  	if !result.SpenderTxHash.IsEqual(expected.SpenderTxHash) {
  2776  		t.Fatalf("expected spender tx hash %v, got %v",
  2777  			expected.SpenderTxHash, result.SpenderTxHash)
  2778  	}
  2779  	if result.SpenderInputIndex != expected.SpenderInputIndex {
  2780  		t.Fatalf("expected spender input index %d, got %d",
  2781  			expected.SpenderInputIndex, result.SpenderInputIndex)
  2782  	}
  2783  	if result.SpendingHeight != expected.SpendingHeight {
  2784  		t.Fatalf("expected spending height %d, got %d",
  2785  			expected.SpendingHeight, result.SpendingHeight)
  2786  	}
  2787  }