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 }