github.com/decred/dcrlnd@v0.7.6/sweep/sweeper_test.go (about) 1 package sweep 2 3 import ( 4 "os" 5 "reflect" 6 "runtime/debug" 7 "runtime/pprof" 8 "testing" 9 "time" 10 11 "github.com/davecgh/go-spew/spew" 12 "github.com/decred/dcrd/chaincfg/chainhash" 13 "github.com/decred/dcrd/chaincfg/v3" 14 "github.com/decred/dcrd/dcrec/secp256k1/v4" 15 "github.com/decred/dcrd/dcrutil/v4" 16 "github.com/decred/dcrd/txscript/v4" 17 "github.com/decred/dcrd/wire" 18 "github.com/decred/dcrlnd/build" 19 "github.com/decred/dcrlnd/input" 20 "github.com/decred/dcrlnd/keychain" 21 "github.com/decred/dcrlnd/lntest/mock" 22 "github.com/decred/dcrlnd/lnwallet" 23 "github.com/decred/dcrlnd/lnwallet/chainfee" 24 "github.com/stretchr/testify/require" 25 ) 26 27 var ( 28 testLog = build.NewSubLogger("SWPR_TEST", nil) 29 30 testMaxSweepAttempts = 3 31 32 testMaxInputsPerTx = 3 33 34 defaultFeePref = Params{Fee: FeePreference{ConfTarget: 1}} 35 ) 36 37 type sweeperTestContext struct { 38 t *testing.T 39 40 sweeper *UtxoSweeper 41 notifier *MockNotifier 42 estimator *mockFeeEstimator 43 backend *mockBackend 44 store *MockSweeperStore 45 46 timeoutChan chan chan time.Time 47 publishChan chan wire.MsgTx 48 } 49 50 var ( 51 spendableInputs []*input.BaseInput 52 testInputCount int 53 54 testPubKey, _ = secp256k1.ParsePubKey([]byte{ 55 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 56 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 57 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 58 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 59 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 60 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 61 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 62 0xb4, 0x12, 0xa3, 63 }) 64 ) 65 66 func createTestInput(value int64, witnessType input.WitnessType) input.BaseInput { 67 hash := chainhash.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69 byte(testInputCount + 1)} 70 71 input := input.MakeBaseInput( 72 &wire.OutPoint{ 73 Hash: hash, 74 }, 75 witnessType, 76 &input.SignDescriptor{ 77 Output: &wire.TxOut{ 78 Value: value, 79 }, 80 KeyDesc: keychain.KeyDescriptor{ 81 PubKey: testPubKey, 82 }, 83 }, 84 0, 85 nil, 86 ) 87 88 testInputCount++ 89 90 return input 91 } 92 93 func init() { 94 // Create a set of test spendable inputs. 95 for i := 0; i < 20; i++ { 96 input := createTestInput(int64(10000+i*500), 97 input.CommitmentTimeLock) 98 99 spendableInputs = append(spendableInputs, &input) 100 } 101 } 102 103 func createSweeperTestContext(t *testing.T) *sweeperTestContext { 104 notifier := NewMockNotifier(t) 105 106 store := NewMockSweeperStore() 107 108 backend := newMockBackend(t, notifier) 109 backend.walletUtxos = []*lnwallet.Utxo{ 110 { 111 Value: dcrutil.Amount(1_000_000), 112 AddressType: lnwallet.PubKeyHash, 113 }, 114 } 115 116 estimator := newMockFeeEstimator(10000, chainfee.FeePerKBFloor) 117 118 ctx := &sweeperTestContext{ 119 notifier: notifier, 120 publishChan: backend.publishChan, 121 t: t, 122 estimator: estimator, 123 backend: backend, 124 store: store, 125 timeoutChan: make(chan chan time.Time, 1), 126 } 127 128 var outputScriptCount byte 129 ctx.sweeper = New(&UtxoSweeperConfig{ 130 Notifier: notifier, 131 Wallet: backend, 132 NewBatchTimer: func() <-chan time.Time { 133 c := make(chan time.Time, 1) 134 ctx.timeoutChan <- c 135 return c 136 }, 137 Store: store, 138 Signer: &mock.DummySigner{}, 139 GenSweepScript: func() ([]byte, error) { 140 outputScriptCount++ 141 return input.GenerateP2PKH([]byte{outputScriptCount}) 142 }, 143 FeeEstimator: estimator, 144 MaxInputsPerTx: testMaxInputsPerTx, 145 MaxSweepAttempts: testMaxSweepAttempts, 146 NextAttemptDeltaFunc: func(attempts int) int32 { 147 // Use delta func without random factor. 148 return 1 << uint(attempts-1) 149 }, 150 NetParams: chaincfg.RegNetParams(), 151 MaxFeeRate: DefaultMaxFeeRate, 152 FeeRateBucketSize: DefaultFeeRateBucketSize, 153 }) 154 155 ctx.sweeper.Start() 156 157 return ctx 158 } 159 160 func (ctx *sweeperTestContext) restartSweeper() { 161 ctx.t.Helper() 162 163 ctx.sweeper.Stop() 164 ctx.sweeper = New(ctx.sweeper.cfg) 165 ctx.sweeper.Start() 166 } 167 168 func (ctx *sweeperTestContext) tick() { 169 testLog.Trace("Waiting for tick to be consumed") 170 select { 171 case c := <-ctx.timeoutChan: 172 select { 173 case c <- time.Time{}: 174 testLog.Trace("Tick") 175 case <-time.After(defaultTestTimeout): 176 debug.PrintStack() 177 ctx.t.Fatal("tick timeout - tick not consumed") 178 } 179 case <-time.After(defaultTestTimeout): 180 debug.PrintStack() 181 ctx.t.Fatal("tick timeout - no new timer created") 182 } 183 } 184 185 // assertNoTick asserts that the sweeper does not wait for a tick. 186 func (ctx *sweeperTestContext) assertNoTick() { 187 ctx.t.Helper() 188 189 select { 190 case <-ctx.timeoutChan: 191 ctx.t.Fatal("unexpected tick") 192 193 case <-time.After(processingDelay): 194 } 195 } 196 197 func (ctx *sweeperTestContext) assertNoNewTimer() { 198 select { 199 case <-ctx.timeoutChan: 200 ctx.t.Fatal("no new timer expected") 201 default: 202 } 203 } 204 205 func (ctx *sweeperTestContext) finish(expectedGoroutineCount int) { 206 // We assume that when finish is called, sweeper has finished all its 207 // goroutines. This implies that the waitgroup is empty. 208 signalChan := make(chan struct{}) 209 go func() { 210 ctx.sweeper.wg.Wait() 211 close(signalChan) 212 }() 213 214 // Simulate exits of the expected number of running goroutines. 215 for i := 0; i < expectedGoroutineCount; i++ { 216 ctx.sweeper.wg.Done() 217 } 218 219 // We now expect the Wait to succeed. 220 select { 221 case <-signalChan: 222 case <-time.After(time.Second): 223 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 224 225 ctx.t.Fatalf("lingering goroutines detected after test " + 226 "is finished") 227 } 228 229 // Restore waitgroup state to what it was before. 230 ctx.sweeper.wg.Add(expectedGoroutineCount) 231 232 // Stop sweeper. 233 ctx.sweeper.Stop() 234 235 // We should have consumed and asserted all published transactions in 236 // our unit tests. 237 ctx.assertNoTx() 238 ctx.assertNoNewTimer() 239 if !ctx.backend.isDone() { 240 ctx.t.Fatal("unconfirmed txes remaining") 241 } 242 } 243 244 func (ctx *sweeperTestContext) assertNoTx() { 245 ctx.t.Helper() 246 select { 247 case <-ctx.publishChan: 248 ctx.t.Fatalf("unexpected transactions published") 249 default: 250 } 251 } 252 253 func (ctx *sweeperTestContext) receiveTx() wire.MsgTx { 254 ctx.t.Helper() 255 var tx wire.MsgTx 256 select { 257 case tx = <-ctx.publishChan: 258 return tx 259 case <-time.After(5 * time.Second): 260 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 261 262 ctx.t.Fatalf("tx not published") 263 } 264 return tx 265 } 266 267 func (ctx *sweeperTestContext) expectResult(c chan Result, expected error) { 268 ctx.t.Helper() 269 select { 270 case result := <-c: 271 if result.Err != expected { 272 ctx.t.Fatalf("expected %v result, but got %v", 273 expected, result.Err, 274 ) 275 } 276 case <-time.After(defaultTestTimeout): 277 ctx.t.Fatalf("no result received") 278 } 279 } 280 281 func (ctx *sweeperTestContext) assertPendingInputs(inputs ...input.Input) { 282 ctx.t.Helper() 283 284 inputSet := make(map[wire.OutPoint]struct{}, len(inputs)) 285 for _, input := range inputs { 286 inputSet[*input.OutPoint()] = struct{}{} 287 } 288 289 pendingInputs, err := ctx.sweeper.PendingInputs() 290 if err != nil { 291 ctx.t.Fatal(err) 292 } 293 if len(pendingInputs) != len(inputSet) { 294 ctx.t.Fatalf("expected %d pending inputs, got %d", 295 len(inputSet), len(pendingInputs)) 296 } 297 for input := range pendingInputs { 298 if _, ok := inputSet[input]; !ok { 299 ctx.t.Fatalf("found unexpected input %v", input) 300 } 301 } 302 } 303 304 // assertTxSweepsInputs ensures that the transaction returned within the value 305 // received from resultChan spends the given inputs. 306 func assertTxSweepsInputs(t *testing.T, sweepTx *wire.MsgTx, 307 inputs ...input.Input) { 308 309 t.Helper() 310 311 if len(sweepTx.TxIn) != len(inputs) { 312 t.Fatalf("expected sweep tx to contain %d inputs, got %d", 313 len(inputs), len(sweepTx.TxIn)) 314 } 315 m := make(map[wire.OutPoint]struct{}, len(inputs)) 316 for _, input := range inputs { 317 m[*input.OutPoint()] = struct{}{} 318 } 319 for _, txIn := range sweepTx.TxIn { 320 if _, ok := m[txIn.PreviousOutPoint]; !ok { 321 t.Fatalf("expected tx %v to spend input %v", 322 txIn.PreviousOutPoint, sweepTx.TxHash()) 323 } 324 } 325 } 326 327 // assertTxFeeRate asserts that the transaction was created with the given 328 // inputs and fee rate. 329 // 330 // NOTE: This assumes that transactions only have one output, as this is the 331 // only type of transaction the UtxoSweeper can create at the moment. 332 func assertTxFeeRate(t *testing.T, tx *wire.MsgTx, 333 expectedFeeRate chainfee.AtomPerKByte, inputs ...input.Input) { 334 335 t.Helper() 336 337 if len(tx.TxIn) != len(inputs) { 338 t.Fatalf("expected %d inputs, got %d", len(tx.TxIn), len(inputs)) 339 } 340 341 m := make(map[wire.OutPoint]input.Input, len(inputs)) 342 for _, input := range inputs { 343 m[*input.OutPoint()] = input 344 } 345 346 var inputAmt int64 347 for _, txIn := range tx.TxIn { 348 input, ok := m[txIn.PreviousOutPoint] 349 if !ok { 350 t.Fatalf("expected input %v to be provided", 351 txIn.PreviousOutPoint) 352 } 353 inputAmt += input.SignDesc().Output.Value 354 } 355 outputAmt := tx.TxOut[0].Value 356 357 fee := dcrutil.Amount(inputAmt - outputAmt) 358 _, estimator := getSizeEstimate(inputs, nil, 0) 359 txSize := estimator.size() 360 361 expectedFee := expectedFeeRate.FeeForSize(int64(txSize)) 362 if fee != expectedFee { 363 t.Fatalf("expected fee rate %v results in %v fee, got %v fee", 364 expectedFeeRate, expectedFee, fee) 365 } 366 } 367 368 // TestSuccess tests the sweeper happy flow. 369 func TestSuccess(t *testing.T) { 370 ctx := createSweeperTestContext(t) 371 372 // Sweeping an input without a fee preference should result in an error. 373 _, err := ctx.sweeper.SweepInput(spendableInputs[0], Params{}) 374 if err != ErrNoFeePreference { 375 t.Fatalf("expected ErrNoFeePreference, got %v", err) 376 } 377 378 resultChan, err := ctx.sweeper.SweepInput( 379 spendableInputs[0], defaultFeePref, 380 ) 381 if err != nil { 382 t.Fatal(err) 383 } 384 385 ctx.tick() 386 387 sweepTx := ctx.receiveTx() 388 389 ctx.backend.mine() 390 391 select { 392 case result := <-resultChan: 393 if result.Err != nil { 394 t.Fatalf("expected successful spend, but received "+ 395 "error %v instead", result.Err) 396 } 397 if result.Tx.TxHash() != sweepTx.TxHash() { 398 t.Fatalf("expected sweep tx ") 399 } 400 case <-time.After(5 * time.Second): 401 t.Fatalf("no result received") 402 } 403 404 ctx.finish(1) 405 406 // Assert that last tx is stored in the database so we can republish 407 // on restart. 408 lastTx, err := ctx.store.GetLastPublishedTx() 409 if err != nil { 410 t.Fatal(err) 411 } 412 if lastTx == nil || sweepTx.TxHash() != lastTx.TxHash() { 413 t.Fatalf("last tx not stored") 414 } 415 } 416 417 // TestDust asserts that inputs that are not big enough to raise above the dust 418 // limit, are held back until the total set does surpass the limit. 419 func TestDust(t *testing.T) { 420 ctx := createSweeperTestContext(t) 421 422 // Calculate the dust limit for the test relay fee (1000 atoms/kB). 423 dustLimit := int64(lnwallet.DustThresholdForRelayFee(1000)) 424 425 // Estimate the size of a transaction that will sweep a 426 // CommitmentTimeLock output and pay to a P2PKH output. 427 var se input.TxSizeEstimator 428 se.AddP2PKHOutput() 429 se.AddCustomInput(input.ToLocalTimeoutSigScriptSize) 430 txSize := se.Size() 431 432 // Calculate the fee to relay this tx, given the test relay fee of 433 // 10000 atoms/kB. 434 txFee := int64(chainfee.AtomPerKByte(10000).FeeForSize(txSize)) 435 436 // Calculate the maximum value an output in this type of tx can have, 437 // after which it will no longer be considered dust. 438 maxDustOutputValue := txFee + dustLimit - 1 439 440 // Now create an input exactly at this dust limit. While positive, this 441 // won't be swept immediately due to the resulting tx having an output 442 // value lower than the dust limit. 443 dustInput := createTestInput(maxDustOutputValue, input.CommitmentTimeLock) 444 _, err := ctx.sweeper.SweepInput(&dustInput, defaultFeePref) 445 if err != nil { 446 t.Fatal(err) 447 } 448 449 // No sweep transaction is expected now. The sweeper should recognize 450 // that the sweep output will not be relayed and not generate the tx. 451 // It isn't possible to attach a wallet utxo either, because the added 452 // size would create a negatively yielding transaction at this fee 453 // rate. 454 455 // Sweep another input that brings the tx output above the dust limit. 456 // We'll create an output with 10 times the maximum considered dust - 457 // more than enough to sweep both the earlier dust input and this new 458 // one. 459 largeInput := createTestInput(maxDustOutputValue*10, 460 input.CommitmentTimeLock) 461 462 _, err = ctx.sweeper.SweepInput(&largeInput, defaultFeePref) 463 if err != nil { 464 t.Fatal(err) 465 } 466 467 ctx.tick() 468 469 // The second input brings the sweep output above the dust limit. We 470 // expect a sweep tx now. 471 472 sweepTx := ctx.receiveTx() 473 if len(sweepTx.TxIn) != 2 { 474 t.Fatalf("Expected tx to sweep 2 inputs, but contains %v "+ 475 "inputs instead", len(sweepTx.TxIn)) 476 } 477 478 ctx.backend.mine() 479 480 ctx.finish(1) 481 } 482 483 // TestWalletUtxo asserts that inputs that are not big enough to raise above the 484 // dust limit are accompanied by a wallet utxo to make them sweepable. 485 func TestWalletUtxo(t *testing.T) { 486 ctx := createSweeperTestContext(t) 487 488 // Sweeping a single output produces a tx of 217 bytes. At the fee 489 // floor, the sweep tx will pay 217*1e4/1000 = 2070 atoms in fees. 490 // 491 // Create an input so that the output after paying fees is still 492 // positive (1730 atoms), but less than the dust limit (6030 atoms) for 493 // the sweep tx output script (P2PKH). 494 // 495 // What we now expect is that the sweeper will attach a utxo from the 496 // wallet. This increases the tx size to 383 bytes with a fee of 3830 497 // atoms. The tx yield becomes then 3900+10000-3830 = 10070 atoms. 498 dustInput := createTestInput(3900, input.PublicKeyHash) 499 500 _, err := ctx.sweeper.SweepInput( 501 &dustInput, 502 Params{Fee: FeePreference{FeeRate: chainfee.FeePerKBFloor}}, 503 ) 504 if err != nil { 505 t.Fatal(err) 506 } 507 508 ctx.tick() 509 510 sweepTx := ctx.receiveTx() 511 if len(sweepTx.TxIn) != 2 { 512 t.Fatalf("Expected tx to sweep 2 inputs, but contains %v "+ 513 "inputs instead", len(sweepTx.TxIn)) 514 } 515 516 // Calculate expected output value based on wallet utxo of 10000 sats. 517 expectedOutputValue := int64(3900 + 1_000_000 - 3830) 518 if sweepTx.TxOut[0].Value != expectedOutputValue { 519 t.Fatalf("Expected output value of %v, but got %v", 520 expectedOutputValue, sweepTx.TxOut[0].Value) 521 } 522 523 ctx.backend.mine() 524 ctx.finish(1) 525 } 526 527 // TestNegativeInput asserts that no inputs with a negative yield are swept. 528 // Negative yield means that the value minus the added fee is negative. 529 func TestNegativeInput(t *testing.T) { 530 ctx := createSweeperTestContext(t) 531 532 // Increase the default fee rate so that we can perform the test. This 533 // is necessary in decred due to lower minimum relay fee. 534 ctx.estimator.updateFees(25000, 10000) 535 536 // Sweep an input large enough to cover fees, so in any case the tx 537 // output will be above the dust limit. 538 largeInput := createTestInput(20000000, input.CommitmentNoDelay) 539 largeInputResult, err := ctx.sweeper.SweepInput( 540 &largeInput, defaultFeePref, 541 ) 542 if err != nil { 543 t.Fatal(err) 544 } 545 546 // Sweep an additional input with a negative net yield. The size of the 547 // HtlcOfferedRemoteTimeout input type adds more in fees than its value 548 // at the current fee level. 549 negInput := createTestInput(5500, input.HtlcOfferedRemoteTimeout) 550 negInputResult, err := ctx.sweeper.SweepInput(&negInput, defaultFeePref) 551 if err != nil { 552 t.Fatal(err) 553 } 554 555 // Sweep a third input that has a smaller output than the previous one, 556 // but yields positively because of its lower size. 557 positiveInput := createTestInput(5000, input.CommitmentNoDelay) 558 positiveInputResult, err := ctx.sweeper.SweepInput( 559 &positiveInput, defaultFeePref, 560 ) 561 if err != nil { 562 t.Fatal(err) 563 } 564 565 ctx.tick() 566 567 // We expect that a sweep tx is published now, but it should only 568 // contain the large input. The negative input should stay out of sweeps 569 // until fees come down to get a positive net yield. 570 sweepTx1 := ctx.receiveTx() 571 assertTxSweepsInputs(t, &sweepTx1, &largeInput, &positiveInput) 572 573 ctx.backend.mine() 574 575 ctx.expectResult(largeInputResult, nil) 576 ctx.expectResult(positiveInputResult, nil) 577 578 // Lower fee rate so that the negative input is no longer negative. 579 ctx.estimator.updateFees(10000, 10000) 580 581 // Create another large input. 582 secondLargeInput := createTestInput(100000, input.CommitmentNoDelay) 583 secondLargeInputResult, err := ctx.sweeper.SweepInput( 584 &secondLargeInput, defaultFeePref, 585 ) 586 if err != nil { 587 t.Fatal(err) 588 } 589 590 ctx.tick() 591 592 sweepTx2 := ctx.receiveTx() 593 assertTxSweepsInputs(t, &sweepTx2, &secondLargeInput, &negInput) 594 595 ctx.backend.mine() 596 597 ctx.expectResult(secondLargeInputResult, nil) 598 ctx.expectResult(negInputResult, nil) 599 600 ctx.finish(1) 601 } 602 603 // TestChunks asserts that large sets of inputs are split into multiple txes. 604 func TestChunks(t *testing.T) { 605 ctx := createSweeperTestContext(t) 606 607 // Sweep five inputs. 608 for _, input := range spendableInputs[:5] { 609 _, err := ctx.sweeper.SweepInput(input, defaultFeePref) 610 if err != nil { 611 t.Fatal(err) 612 } 613 } 614 615 ctx.tick() 616 617 // We expect two txes to be published because of the max input count of 618 // three. 619 sweepTx1 := ctx.receiveTx() 620 if len(sweepTx1.TxIn) != 3 { 621 t.Fatalf("Expected first tx to sweep 3 inputs, but contains %v "+ 622 "inputs instead", len(sweepTx1.TxIn)) 623 } 624 625 sweepTx2 := ctx.receiveTx() 626 if len(sweepTx2.TxIn) != 2 { 627 t.Fatalf("Expected first tx to sweep 2 inputs, but contains %v "+ 628 "inputs instead", len(sweepTx1.TxIn)) 629 } 630 631 ctx.backend.mine() 632 633 ctx.finish(1) 634 } 635 636 // TestRemoteSpend asserts that remote spends are properly detected and handled 637 // both before the sweep is published as well as after. 638 func TestRemoteSpend(t *testing.T) { 639 t.Run("pre-sweep", func(t *testing.T) { 640 testRemoteSpend(t, false) 641 }) 642 t.Run("post-sweep", func(t *testing.T) { 643 testRemoteSpend(t, true) 644 }) 645 } 646 647 func testRemoteSpend(t *testing.T, postSweep bool) { 648 ctx := createSweeperTestContext(t) 649 650 resultChan1, err := ctx.sweeper.SweepInput( 651 spendableInputs[0], defaultFeePref, 652 ) 653 if err != nil { 654 t.Fatal(err) 655 } 656 657 resultChan2, err := ctx.sweeper.SweepInput( 658 spendableInputs[1], defaultFeePref, 659 ) 660 if err != nil { 661 t.Fatal(err) 662 } 663 664 // Spend the input with an unknown tx. 665 remoteTx := &wire.MsgTx{ 666 TxIn: []*wire.TxIn{ 667 { 668 PreviousOutPoint: *(spendableInputs[0].OutPoint()), 669 }, 670 }, 671 } 672 err = ctx.backend.publishTransaction(remoteTx) 673 if err != nil { 674 t.Fatal(err) 675 } 676 677 if postSweep { 678 ctx.tick() 679 680 // Tx publication by sweeper returns ErrDoubleSpend. Sweeper 681 // will retry the inputs without reporting a result. It could be 682 // spent by the remote party. 683 ctx.receiveTx() 684 } 685 686 ctx.backend.mine() 687 688 select { 689 case result := <-resultChan1: 690 if result.Err != ErrRemoteSpend { 691 t.Fatalf("expected remote spend") 692 } 693 if result.Tx.TxHash() != remoteTx.TxHash() { 694 t.Fatalf("expected remote spend tx") 695 } 696 case <-time.After(5 * time.Second): 697 t.Fatalf("no result received") 698 } 699 700 if !postSweep { 701 // Assert that the sweeper sweeps the remaining input. 702 ctx.tick() 703 sweepTx := ctx.receiveTx() 704 705 if len(sweepTx.TxIn) != 1 { 706 t.Fatal("expected sweep to only sweep the one remaining output") 707 } 708 709 ctx.backend.mine() 710 711 ctx.expectResult(resultChan2, nil) 712 713 ctx.finish(1) 714 } else { 715 // Expected sweeper to be still listening for spend of the 716 // error input. 717 ctx.finish(2) 718 719 select { 720 case <-resultChan2: 721 t.Fatalf("no result expected for error input") 722 default: 723 } 724 } 725 } 726 727 // TestIdempotency asserts that offering the same input multiple times is 728 // handled correctly. 729 func TestIdempotency(t *testing.T) { 730 ctx := createSweeperTestContext(t) 731 732 input := spendableInputs[0] 733 resultChan1, err := ctx.sweeper.SweepInput(input, defaultFeePref) 734 if err != nil { 735 t.Fatal(err) 736 } 737 738 resultChan2, err := ctx.sweeper.SweepInput(input, defaultFeePref) 739 if err != nil { 740 t.Fatal(err) 741 } 742 743 ctx.tick() 744 745 ctx.receiveTx() 746 747 resultChan3, err := ctx.sweeper.SweepInput(input, defaultFeePref) 748 if err != nil { 749 t.Fatal(err) 750 } 751 752 // Spend the input of the sweep tx. 753 ctx.backend.mine() 754 755 ctx.expectResult(resultChan1, nil) 756 ctx.expectResult(resultChan2, nil) 757 ctx.expectResult(resultChan3, nil) 758 759 // Offer the same input again. The sweeper will register a spend ntfn 760 // for this input. Because the input has already been spent, it will 761 // immediately receive the spend notification with a spending tx hash. 762 // Because the sweeper kept track of all of its sweep txes, it will 763 // recognize the spend as its own. 764 resultChan4, err := ctx.sweeper.SweepInput(input, defaultFeePref) 765 if err != nil { 766 t.Fatal(err) 767 } 768 ctx.expectResult(resultChan4, nil) 769 770 // Timer is still running, but spend notification was delivered before 771 // it expired. 772 ctx.tick() 773 774 ctx.finish(1) 775 } 776 777 // TestNoInputs asserts that nothing happens if nothing happens. 778 func TestNoInputs(t *testing.T) { 779 ctx := createSweeperTestContext(t) 780 781 // No tx should appear. This is asserted in finish(). 782 ctx.finish(1) 783 } 784 785 // TestRestart asserts that the sweeper picks up sweeping properly after 786 // a restart. 787 func TestRestart(t *testing.T) { 788 ctx := createSweeperTestContext(t) 789 790 // Sweep input and expect sweep tx. 791 input1 := spendableInputs[0] 792 if _, err := ctx.sweeper.SweepInput(input1, defaultFeePref); err != nil { 793 t.Fatal(err) 794 } 795 ctx.tick() 796 797 ctx.receiveTx() 798 799 // Restart sweeper. 800 ctx.restartSweeper() 801 802 // Expect last tx to be republished. 803 ctx.receiveTx() 804 805 // Simulate other subsystem (e.g. contract resolver) re-offering inputs. 806 spendChan1, err := ctx.sweeper.SweepInput(input1, defaultFeePref) 807 if err != nil { 808 t.Fatal(err) 809 } 810 811 input2 := spendableInputs[1] 812 spendChan2, err := ctx.sweeper.SweepInput(input2, defaultFeePref) 813 if err != nil { 814 t.Fatal(err) 815 } 816 817 // Spend inputs of sweep txes and verify that spend channels signal 818 // spends. 819 ctx.backend.mine() 820 821 // Sweeper should recognize that its sweep tx of the previous run is 822 // spending the input. 823 select { 824 case result := <-spendChan1: 825 if result.Err != nil { 826 t.Fatalf("expected successful sweep") 827 } 828 case <-time.After(defaultTestTimeout): 829 t.Fatalf("no result received") 830 } 831 832 // Timer tick should trigger republishing a sweep for the remaining 833 // input. 834 ctx.tick() 835 836 ctx.receiveTx() 837 838 ctx.backend.mine() 839 840 select { 841 case result := <-spendChan2: 842 if result.Err != nil { 843 t.Fatalf("expected successful sweep") 844 } 845 case <-time.After(defaultTestTimeout): 846 t.Fatalf("no result received") 847 } 848 849 // Restart sweeper again. No action is expected. 850 ctx.restartSweeper() 851 852 // Expect last tx to be republished. 853 ctx.receiveTx() 854 855 ctx.finish(1) 856 } 857 858 // TestRestartRemoteSpend asserts that the sweeper picks up sweeping properly after 859 // a restart with remote spend. 860 func TestRestartRemoteSpend(t *testing.T) { 861 862 ctx := createSweeperTestContext(t) 863 864 // Sweep input. 865 input1 := spendableInputs[0] 866 if _, err := ctx.sweeper.SweepInput(input1, defaultFeePref); err != nil { 867 t.Fatal(err) 868 } 869 870 // Sweep another input. 871 input2 := spendableInputs[1] 872 if _, err := ctx.sweeper.SweepInput(input2, defaultFeePref); err != nil { 873 t.Fatal(err) 874 } 875 876 ctx.tick() 877 878 sweepTx := ctx.receiveTx() 879 880 // Restart sweeper. 881 ctx.restartSweeper() 882 883 // Expect last tx to be republished. 884 ctx.receiveTx() 885 886 // Replace the sweep tx with a remote tx spending input 1. 887 ctx.backend.deleteUnconfirmed(sweepTx.TxHash()) 888 889 remoteTx := &wire.MsgTx{ 890 TxIn: []*wire.TxIn{ 891 { 892 PreviousOutPoint: *(input2.OutPoint()), 893 }, 894 }, 895 } 896 if err := ctx.backend.publishTransaction(remoteTx); err != nil { 897 t.Fatal(err) 898 } 899 900 // Mine remote spending tx. 901 ctx.backend.mine() 902 903 // Simulate other subsystem (e.g. contract resolver) re-offering input 0. 904 spendChan, err := ctx.sweeper.SweepInput(input1, defaultFeePref) 905 if err != nil { 906 t.Fatal(err) 907 } 908 909 // Notify of a new block so the sweeper re-attempts an input from its 910 // last tx. 911 ctx.notifier.NotifyEpoch(1000) 912 913 // Expect sweeper to construct a new tx, because input 1 was spend 914 // remotely. 915 ctx.tick() 916 917 ctx.receiveTx() 918 919 ctx.backend.mine() 920 921 ctx.expectResult(spendChan, nil) 922 923 ctx.finish(1) 924 } 925 926 // TestRestartConfirmed asserts that the sweeper picks up sweeping properly after 927 // a restart with a confirm of our own sweep tx. 928 func TestRestartConfirmed(t *testing.T) { 929 ctx := createSweeperTestContext(t) 930 931 // Sweep input. 932 input := spendableInputs[0] 933 if _, err := ctx.sweeper.SweepInput(input, defaultFeePref); err != nil { 934 t.Fatal(err) 935 } 936 937 ctx.tick() 938 939 ctx.receiveTx() 940 941 // Restart sweeper. 942 ctx.restartSweeper() 943 944 // Expect last tx to be republished. 945 ctx.receiveTx() 946 947 // Mine the sweep tx. 948 ctx.backend.mine() 949 950 // Simulate other subsystem (e.g. contract resolver) re-offering input 0. 951 spendChan, err := ctx.sweeper.SweepInput(input, defaultFeePref) 952 if err != nil { 953 t.Fatal(err) 954 } 955 956 // Here we expect again a successful sweep. 957 ctx.expectResult(spendChan, nil) 958 959 ctx.finish(1) 960 } 961 962 // TestRestartRepublish asserts that sweeper republishes the last published 963 // tx on restart. 964 func TestRestartRepublish(t *testing.T) { 965 ctx := createSweeperTestContext(t) 966 967 _, err := ctx.sweeper.SweepInput(spendableInputs[0], defaultFeePref) 968 if err != nil { 969 t.Fatal(err) 970 } 971 972 ctx.tick() 973 974 sweepTx := ctx.receiveTx() 975 976 // Restart sweeper again. No action is expected. 977 ctx.restartSweeper() 978 979 republishedTx := ctx.receiveTx() 980 981 if sweepTx.TxHash() != republishedTx.TxHash() { 982 t.Fatalf("last tx not republished") 983 } 984 985 // Mine the tx to conclude the test properly. 986 ctx.backend.mine() 987 988 ctx.finish(1) 989 } 990 991 // TestRetry tests the sweeper retry flow. 992 func TestRetry(t *testing.T) { 993 ctx := createSweeperTestContext(t) 994 995 resultChan0, err := ctx.sweeper.SweepInput( 996 spendableInputs[0], defaultFeePref, 997 ) 998 if err != nil { 999 t.Fatal(err) 1000 } 1001 1002 ctx.tick() 1003 1004 // We expect a sweep to be published. 1005 ctx.receiveTx() 1006 1007 // New block arrives. This should trigger a new sweep attempt timer 1008 // start. 1009 ctx.notifier.NotifyEpoch(1000) 1010 1011 // Offer a fresh input. 1012 resultChan1, err := ctx.sweeper.SweepInput( 1013 spendableInputs[1], defaultFeePref, 1014 ) 1015 if err != nil { 1016 t.Fatal(err) 1017 } 1018 1019 ctx.tick() 1020 1021 // Two txes are expected to be published, because new and retry inputs 1022 // are separated. 1023 ctx.receiveTx() 1024 ctx.receiveTx() 1025 1026 ctx.backend.mine() 1027 1028 ctx.expectResult(resultChan0, nil) 1029 ctx.expectResult(resultChan1, nil) 1030 1031 ctx.finish(1) 1032 } 1033 1034 // TestGiveUp asserts that the sweeper gives up on an input if it can't be swept 1035 // after a configured number of attempts.a 1036 func TestGiveUp(t *testing.T) { 1037 ctx := createSweeperTestContext(t) 1038 1039 resultChan0, err := ctx.sweeper.SweepInput( 1040 spendableInputs[0], defaultFeePref, 1041 ) 1042 if err != nil { 1043 t.Fatal(err) 1044 } 1045 1046 ctx.tick() 1047 1048 // We expect a sweep to be published at height 100 (mockChainIOHeight). 1049 ctx.receiveTx() 1050 1051 // Because of MaxSweepAttemps, two more sweeps will be attempted. We 1052 // configured exponential back-off without randomness for the test. The 1053 // second attempt, we expect to happen at 101. The third attempt at 103. 1054 // At that point, the input is expected to be failed. 1055 1056 // Second attempt 1057 ctx.notifier.NotifyEpoch(101) 1058 ctx.tick() 1059 ctx.receiveTx() 1060 1061 // Third attempt 1062 ctx.notifier.NotifyEpoch(103) 1063 ctx.tick() 1064 ctx.receiveTx() 1065 1066 ctx.expectResult(resultChan0, ErrTooManyAttempts) 1067 1068 ctx.backend.mine() 1069 1070 ctx.finish(1) 1071 } 1072 1073 // TestDifferentFeePreferences ensures that the sweeper can have different 1074 // transactions for different fee preferences. These transactions should be 1075 // broadcast from highest to lowest fee rate. 1076 func TestDifferentFeePreferences(t *testing.T) { 1077 ctx := createSweeperTestContext(t) 1078 1079 // Throughout this test, we'll be attempting to sweep three inputs, two 1080 // with the higher fee preference, and the last with the lower. We do 1081 // this to ensure the sweeper can broadcast distinct transactions for 1082 // each sweep with a different fee preference. 1083 lowFeePref := FeePreference{ConfTarget: 12} 1084 lowFeeRate := chainfee.AtomPerKByte(10000) 1085 ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate 1086 1087 highFeePref := FeePreference{ConfTarget: 6} 1088 highFeeRate := chainfee.AtomPerKByte(20000) 1089 ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate 1090 1091 input1 := spendableInputs[0] 1092 resultChan1, err := ctx.sweeper.SweepInput( 1093 input1, Params{Fee: highFeePref}, 1094 ) 1095 if err != nil { 1096 t.Fatal(err) 1097 } 1098 input2 := spendableInputs[1] 1099 resultChan2, err := ctx.sweeper.SweepInput( 1100 input2, Params{Fee: highFeePref}, 1101 ) 1102 if err != nil { 1103 t.Fatal(err) 1104 } 1105 input3 := spendableInputs[2] 1106 resultChan3, err := ctx.sweeper.SweepInput( 1107 input3, Params{Fee: lowFeePref}, 1108 ) 1109 if err != nil { 1110 t.Fatal(err) 1111 } 1112 1113 // Start the sweeper's batch ticker, which should cause the sweep 1114 // transactions to be broadcast in order of high to low fee preference. 1115 ctx.tick() 1116 1117 // The first transaction broadcast should be the one spending the higher 1118 // fee rate inputs. 1119 sweepTx1 := ctx.receiveTx() 1120 assertTxFeeRate(t, &sweepTx1, highFeeRate, input1, input2) 1121 1122 // The second should be the one spending the lower fee rate inputs. 1123 sweepTx2 := ctx.receiveTx() 1124 assertTxFeeRate(t, &sweepTx2, lowFeeRate, input3) 1125 1126 // With the transactions broadcast, we'll mine a block to so that the 1127 // result is delivered to each respective client. 1128 ctx.backend.mine() 1129 resultChans := []chan Result{resultChan1, resultChan2, resultChan3} 1130 for _, resultChan := range resultChans { 1131 ctx.expectResult(resultChan, nil) 1132 } 1133 1134 ctx.finish(1) 1135 } 1136 1137 // TestPendingInputs ensures that the sweeper correctly determines the inputs 1138 // pending to be swept. 1139 func TestPendingInputs(t *testing.T) { 1140 ctx := createSweeperTestContext(t) 1141 1142 // Throughout this test, we'll be attempting to sweep three inputs, two 1143 // with the higher fee preference, and the last with the lower. We do 1144 // this to ensure the sweeper can return all pending inputs, even those 1145 // with different fee preferences. 1146 const ( 1147 lowFeeRate = 10000 1148 highFeeRate = 20000 1149 ) 1150 1151 lowFeePref := FeePreference{ 1152 ConfTarget: 12, 1153 } 1154 ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate 1155 1156 highFeePref := FeePreference{ 1157 ConfTarget: 6, 1158 } 1159 ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate 1160 1161 input1 := spendableInputs[0] 1162 resultChan1, err := ctx.sweeper.SweepInput( 1163 input1, Params{Fee: highFeePref}, 1164 ) 1165 if err != nil { 1166 t.Fatal(err) 1167 } 1168 input2 := spendableInputs[1] 1169 _, err = ctx.sweeper.SweepInput( 1170 input2, Params{Fee: highFeePref}, 1171 ) 1172 if err != nil { 1173 t.Fatal(err) 1174 } 1175 input3 := spendableInputs[2] 1176 resultChan3, err := ctx.sweeper.SweepInput( 1177 input3, Params{Fee: lowFeePref}, 1178 ) 1179 if err != nil { 1180 t.Fatal(err) 1181 } 1182 1183 // We should expect to see all inputs pending. 1184 ctx.assertPendingInputs(input1, input2, input3) 1185 1186 // We should expect to see both sweep transactions broadcast. The higher 1187 // fee rate sweep should be broadcast first. We'll remove the lower fee 1188 // rate sweep to ensure we can detect pending inputs after a sweep. 1189 // Once the higher fee rate sweep confirms, we should no longer see 1190 // those inputs pending. 1191 ctx.tick() 1192 ctx.receiveTx() 1193 lowFeeRateTx := ctx.receiveTx() 1194 ctx.backend.deleteUnconfirmed(lowFeeRateTx.TxHash()) 1195 ctx.backend.mine() 1196 ctx.expectResult(resultChan1, nil) 1197 ctx.assertPendingInputs(input3) 1198 1199 // We'll then trigger a new block to rebroadcast the lower fee rate 1200 // sweep. Once again we'll ensure those inputs are no longer pending 1201 // once the sweep transaction confirms. 1202 ctx.backend.notifier.NotifyEpoch(101) 1203 ctx.tick() 1204 ctx.receiveTx() 1205 ctx.backend.mine() 1206 ctx.expectResult(resultChan3, nil) 1207 ctx.assertPendingInputs() 1208 1209 ctx.finish(1) 1210 } 1211 1212 // TestBumpFeeRBF ensures that the UtxoSweeper can properly handle a fee bump 1213 // request for an input it is currently attempting to sweep. When sweeping the 1214 // input with the higher fee rate, a replacement transaction is created. 1215 func TestBumpFeeRBF(t *testing.T) { 1216 ctx := createSweeperTestContext(t) 1217 1218 lowFeePref := FeePreference{ConfTarget: 144} 1219 lowFeeRate := chainfee.FeePerKBFloor 1220 ctx.estimator.blocksToFee[lowFeePref.ConfTarget] = lowFeeRate 1221 1222 // We'll first try to bump the fee of an output currently unknown to the 1223 // UtxoSweeper. Doing so should result in a lnwallet.ErrNotMine error. 1224 _, err := ctx.sweeper.UpdateParams( 1225 wire.OutPoint{}, ParamsUpdate{Fee: lowFeePref}, 1226 ) 1227 if err != lnwallet.ErrNotMine { 1228 t.Fatalf("expected error lnwallet.ErrNotMine, got \"%v\"", err) 1229 } 1230 1231 // We'll then attempt to sweep an input, which we'll use to bump its fee 1232 // later on. 1233 input := createTestInput( 1234 dcrutil.AtomsPerCoin, input.CommitmentTimeLock, 1235 ) 1236 sweepResult, err := ctx.sweeper.SweepInput( 1237 &input, Params{Fee: lowFeePref}, 1238 ) 1239 if err != nil { 1240 t.Fatal(err) 1241 } 1242 1243 // Ensure that a transaction is broadcast with the lower fee preference. 1244 ctx.tick() 1245 lowFeeTx := ctx.receiveTx() 1246 assertTxFeeRate(t, &lowFeeTx, lowFeeRate, &input) 1247 1248 // We'll then attempt to bump its fee rate. 1249 highFeePref := FeePreference{ConfTarget: 6} 1250 highFeeRate := DefaultMaxFeeRate 1251 ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate 1252 1253 // We should expect to see an error if a fee preference isn't provided. 1254 _, err = ctx.sweeper.UpdateParams(*input.OutPoint(), ParamsUpdate{}) 1255 if err != ErrNoFeePreference { 1256 t.Fatalf("expected ErrNoFeePreference, got %v", err) 1257 } 1258 1259 bumpResult, err := ctx.sweeper.UpdateParams( 1260 *input.OutPoint(), ParamsUpdate{Fee: highFeePref}, 1261 ) 1262 if err != nil { 1263 t.Fatalf("unable to bump input's fee: %v", err) 1264 } 1265 1266 // A higher fee rate transaction should be immediately broadcast. 1267 ctx.tick() 1268 highFeeTx := ctx.receiveTx() 1269 assertTxFeeRate(t, &highFeeTx, highFeeRate, &input) 1270 1271 // We'll finish our test by mining the sweep transaction. 1272 ctx.backend.mine() 1273 ctx.expectResult(sweepResult, nil) 1274 ctx.expectResult(bumpResult, nil) 1275 1276 ctx.finish(1) 1277 } 1278 1279 // TestExclusiveGroup tests the sweeper exclusive group functionality. 1280 func TestExclusiveGroup(t *testing.T) { 1281 ctx := createSweeperTestContext(t) 1282 1283 // Sweep three inputs in the same exclusive group. 1284 var results []chan Result 1285 for i := 0; i < 3; i++ { 1286 exclusiveGroup := uint64(1) 1287 result, err := ctx.sweeper.SweepInput( 1288 spendableInputs[i], Params{ 1289 Fee: FeePreference{ConfTarget: 6}, 1290 ExclusiveGroup: &exclusiveGroup, 1291 }, 1292 ) 1293 if err != nil { 1294 t.Fatal(err) 1295 } 1296 results = append(results, result) 1297 } 1298 1299 // We expect all inputs to be published in separate transactions, even 1300 // though they share the same fee preference. 1301 ctx.tick() 1302 for i := 0; i < 3; i++ { 1303 sweepTx := ctx.receiveTx() 1304 if len(sweepTx.TxOut) != 1 { 1305 t.Fatal("expected a single tx out in the sweep tx") 1306 } 1307 1308 // Remove all txes except for the one that sweeps the first 1309 // input. This simulates the sweeps being conflicting. 1310 if sweepTx.TxIn[0].PreviousOutPoint != 1311 *spendableInputs[0].OutPoint() { 1312 1313 ctx.backend.deleteUnconfirmed(sweepTx.TxHash()) 1314 } 1315 } 1316 1317 // Mine the first sweep tx. 1318 ctx.backend.mine() 1319 1320 // Expect the first input to be swept by the confirmed sweep tx. 1321 result0 := <-results[0] 1322 if result0.Err != nil { 1323 t.Fatal("expected first input to be swept") 1324 } 1325 1326 // Expect the other two inputs to return an error. They have no chance 1327 // of confirming. 1328 result1 := <-results[1] 1329 if result1.Err != ErrExclusiveGroupSpend { 1330 t.Fatal("expected second input to be canceled") 1331 } 1332 1333 result2 := <-results[2] 1334 if result2.Err != ErrExclusiveGroupSpend { 1335 t.Fatal("expected third input to be canceled") 1336 } 1337 } 1338 1339 // TestCpfp tests that the sweeper spends cpfp inputs at a fee rate that exceeds 1340 // the parent tx fee rate. 1341 func TestCpfp(t *testing.T) { 1342 ctx := createSweeperTestContext(t) 1343 1344 ctx.estimator.updateFees(10000, chainfee.FeePerKBFloor) 1345 1346 // Offer an input with an unconfirmed parent tx to the sweeper. The 1347 // parent tx pays 3000 sat/kw. 1348 hash := chainhash.Hash{1} 1349 input := input.MakeBaseInput( 1350 &wire.OutPoint{Hash: hash}, 1351 input.CommitmentTimeLock, 1352 &input.SignDescriptor{ 1353 Output: &wire.TxOut{ 1354 Value: 25000, 1355 }, 1356 KeyDesc: keychain.KeyDescriptor{ 1357 PubKey: testPubKey, 1358 }, 1359 }, 1360 0, 1361 &input.TxInfo{ 1362 Size: 300, 1363 Fee: 9000, 1364 }, 1365 ) 1366 1367 feePref := FeePreference{ConfTarget: 6} 1368 result, err := ctx.sweeper.SweepInput( 1369 &input, Params{Fee: feePref, Force: true}, 1370 ) 1371 require.NoError(t, err) 1372 1373 // Because we sweep at 1000 sat/kw, the parent cannot be paid for. We 1374 // expect the sweeper to remain idle. 1375 ctx.assertNoTick() 1376 1377 // Increase the fee estimate to above the parent tx fee rate. 1378 ctx.estimator.updateFees(50000, chainfee.FeePerKBFloor) 1379 1380 // Signal a new block. This is a trigger for the sweeper to refresh fee 1381 // estimates. 1382 ctx.notifier.NotifyEpoch(1000) 1383 1384 // Now we do expect a sweep transaction to be published with our input 1385 // and an attached wallet utxo. 1386 ctx.tick() 1387 tx := ctx.receiveTx() 1388 require.Len(t, tx.TxIn, 2) 1389 require.Len(t, tx.TxOut, 1) 1390 1391 // As inputs we have 1000000 atoms from the wallet and 25000 atoms from 1392 // the cpfp input. The sweep tx size is expected to be 432 bytes. There 1393 // is an additional 300 bytes from the parent to include in the package, 1394 // making a total of 732 bytes. At 50000 atoms/kB, the required fee for 1395 // the package is 36600 atoms. The parent already paid 9000 atoms, so 1396 // there is 27600 atoms remaining to be paid. The expected output value 1397 // is therefore 1000000 + 25000 - 27600 = 7400. 1398 require.Equal(t, int64(997400), tx.TxOut[0].Value) 1399 1400 // Mine the tx and assert that the result is passed back. 1401 ctx.backend.mine() 1402 ctx.expectResult(result, nil) 1403 1404 ctx.finish(1) 1405 } 1406 1407 var ( 1408 testInputsA = pendingInputs{ 1409 wire.OutPoint{Hash: chainhash.Hash{}, Index: 0}: &pendingInput{}, 1410 wire.OutPoint{Hash: chainhash.Hash{}, Index: 1}: &pendingInput{}, 1411 wire.OutPoint{Hash: chainhash.Hash{}, Index: 2}: &pendingInput{}, 1412 } 1413 1414 testInputsB = pendingInputs{ 1415 wire.OutPoint{Hash: chainhash.Hash{}, Index: 10}: &pendingInput{}, 1416 wire.OutPoint{Hash: chainhash.Hash{}, Index: 11}: &pendingInput{}, 1417 wire.OutPoint{Hash: chainhash.Hash{}, Index: 12}: &pendingInput{}, 1418 } 1419 1420 testInputsC = pendingInputs{ 1421 wire.OutPoint{Hash: chainhash.Hash{}, Index: 0}: &pendingInput{}, 1422 wire.OutPoint{Hash: chainhash.Hash{}, Index: 1}: &pendingInput{}, 1423 wire.OutPoint{Hash: chainhash.Hash{}, Index: 2}: &pendingInput{}, 1424 wire.OutPoint{Hash: chainhash.Hash{}, Index: 10}: &pendingInput{}, 1425 wire.OutPoint{Hash: chainhash.Hash{}, Index: 11}: &pendingInput{}, 1426 wire.OutPoint{Hash: chainhash.Hash{}, Index: 12}: &pendingInput{}, 1427 } 1428 ) 1429 1430 // TestMergeClusters check that we properly can merge clusters together, 1431 // according to their required locktime. 1432 func TestMergeClusters(t *testing.T) { 1433 t.Parallel() 1434 1435 lockTime1 := uint32(100) 1436 lockTime2 := uint32(200) 1437 1438 testCases := []struct { 1439 name string 1440 a inputCluster 1441 b inputCluster 1442 res []inputCluster 1443 }{ 1444 { 1445 name: "max fee rate", 1446 a: inputCluster{ 1447 sweepFeeRate: 5000, 1448 inputs: testInputsA, 1449 }, 1450 b: inputCluster{ 1451 sweepFeeRate: 7000, 1452 inputs: testInputsB, 1453 }, 1454 res: []inputCluster{ 1455 { 1456 sweepFeeRate: 7000, 1457 inputs: testInputsC, 1458 }, 1459 }, 1460 }, 1461 { 1462 name: "same locktime", 1463 a: inputCluster{ 1464 lockTime: &lockTime1, 1465 sweepFeeRate: 5000, 1466 inputs: testInputsA, 1467 }, 1468 b: inputCluster{ 1469 lockTime: &lockTime1, 1470 sweepFeeRate: 7000, 1471 inputs: testInputsB, 1472 }, 1473 res: []inputCluster{ 1474 { 1475 lockTime: &lockTime1, 1476 sweepFeeRate: 7000, 1477 inputs: testInputsC, 1478 }, 1479 }, 1480 }, 1481 { 1482 name: "diff locktime", 1483 a: inputCluster{ 1484 lockTime: &lockTime1, 1485 sweepFeeRate: 5000, 1486 inputs: testInputsA, 1487 }, 1488 b: inputCluster{ 1489 lockTime: &lockTime2, 1490 sweepFeeRate: 7000, 1491 inputs: testInputsB, 1492 }, 1493 res: []inputCluster{ 1494 { 1495 lockTime: &lockTime1, 1496 sweepFeeRate: 5000, 1497 inputs: testInputsA, 1498 }, 1499 { 1500 lockTime: &lockTime2, 1501 sweepFeeRate: 7000, 1502 inputs: testInputsB, 1503 }, 1504 }, 1505 }, 1506 } 1507 1508 for _, test := range testCases { 1509 merged := mergeClusters(test.a, test.b) 1510 if !reflect.DeepEqual(merged, test.res) { 1511 t.Fatalf("[%s] unexpected result: %v", 1512 test.name, spew.Sdump(merged)) 1513 } 1514 } 1515 } 1516 1517 // TestZipClusters tests that we can merge lists of inputs clusters correctly. 1518 func TestZipClusters(t *testing.T) { 1519 t.Parallel() 1520 1521 createCluster := func(inp pendingInputs, f chainfee.AtomPerKByte) inputCluster { 1522 return inputCluster{ 1523 sweepFeeRate: f, 1524 inputs: inp, 1525 } 1526 } 1527 1528 testCases := []struct { 1529 name string 1530 as []inputCluster 1531 bs []inputCluster 1532 res []inputCluster 1533 }{ 1534 { 1535 name: "merge A into B", 1536 as: []inputCluster{ 1537 createCluster(testInputsA, 5000), 1538 }, 1539 bs: []inputCluster{ 1540 createCluster(testInputsB, 7000), 1541 }, 1542 res: []inputCluster{ 1543 createCluster(testInputsC, 7000), 1544 }, 1545 }, 1546 { 1547 name: "A can't merge with B", 1548 as: []inputCluster{ 1549 createCluster(testInputsA, 7000), 1550 }, 1551 bs: []inputCluster{ 1552 createCluster(testInputsB, 5000), 1553 }, 1554 res: []inputCluster{ 1555 createCluster(testInputsA, 7000), 1556 createCluster(testInputsB, 5000), 1557 }, 1558 }, 1559 { 1560 name: "empty bs", 1561 as: []inputCluster{ 1562 createCluster(testInputsA, 7000), 1563 }, 1564 bs: []inputCluster{}, 1565 res: []inputCluster{ 1566 createCluster(testInputsA, 7000), 1567 }, 1568 }, 1569 { 1570 name: "empty as", 1571 as: []inputCluster{}, 1572 bs: []inputCluster{ 1573 createCluster(testInputsB, 5000), 1574 }, 1575 res: []inputCluster{ 1576 createCluster(testInputsB, 5000), 1577 }, 1578 }, 1579 1580 { 1581 name: "zip 3xA into 3xB", 1582 as: []inputCluster{ 1583 createCluster(testInputsA, 5000), 1584 createCluster(testInputsA, 5000), 1585 createCluster(testInputsA, 5000), 1586 }, 1587 bs: []inputCluster{ 1588 createCluster(testInputsB, 7000), 1589 createCluster(testInputsB, 7000), 1590 createCluster(testInputsB, 7000), 1591 }, 1592 res: []inputCluster{ 1593 createCluster(testInputsC, 7000), 1594 createCluster(testInputsC, 7000), 1595 createCluster(testInputsC, 7000), 1596 }, 1597 }, 1598 { 1599 name: "zip A into 3xB", 1600 as: []inputCluster{ 1601 createCluster(testInputsA, 2500), 1602 }, 1603 bs: []inputCluster{ 1604 createCluster(testInputsB, 3000), 1605 createCluster(testInputsB, 2000), 1606 createCluster(testInputsB, 1000), 1607 }, 1608 res: []inputCluster{ 1609 createCluster(testInputsC, 3000), 1610 createCluster(testInputsB, 2000), 1611 createCluster(testInputsB, 1000), 1612 }, 1613 }, 1614 } 1615 1616 for _, test := range testCases { 1617 zipped := zipClusters(test.as, test.bs) 1618 if !reflect.DeepEqual(zipped, test.res) { 1619 t.Fatalf("[%s] unexpected result: %v", 1620 test.name, spew.Sdump(zipped)) 1621 } 1622 } 1623 } 1624 1625 type testInput struct { 1626 *input.BaseInput 1627 1628 locktime *uint32 1629 reqTxOut *wire.TxOut 1630 } 1631 1632 func (i *testInput) RequiredLockTime() (uint32, bool) { 1633 if i.locktime != nil { 1634 return *i.locktime, true 1635 } 1636 1637 return 0, false 1638 } 1639 1640 func (i *testInput) RequiredTxOut() *wire.TxOut { 1641 return i.reqTxOut 1642 } 1643 1644 // CraftInputScript is a custom sign method for the testInput type that will 1645 // encode the spending outpoint and the tx input index as part of the returned 1646 // witness. 1647 func (i *testInput) CraftInputScript(_ input.Signer, txn *wire.MsgTx, 1648 txinIdx int) (*input.Script, error) { 1649 1650 // We'll encode the outpoint in the witness, so we can assert that the 1651 // expected input was signed at the correct index. 1652 // 1653 // Note(decred) OP_1 + <int> is used instead of int directly because 1654 // SigScriptToWitnessStack converts OP_0 to a nil byte slice, making it 1655 // harder to verify. Thus we add OP_1 to ensure a data push actually 1656 // happens. 1657 op := i.OutPoint() 1658 return &input.Script{ 1659 Witness: [][]byte{ 1660 // We encode the hash of the outpoint... 1661 op.Hash[:], 1662 // ..the outpoint index... 1663 //{byte(op.Index)}, 1664 {txscript.OP_1 + byte(op.Index)}, 1665 // ..and finally the tx input index. 1666 {txscript.OP_1 + byte(txinIdx)}, 1667 }, 1668 }, nil 1669 } 1670 1671 // assertSignedIndex goes through all inputs to the tx and checks that all 1672 // testInputs have witnesses corresponding to the outpoints they are spending, 1673 // and are signed at the correct tx input index. All found testInputs are 1674 // returned such that we can sum up and sanity check that all testInputs were 1675 // part of the sweep. 1676 func assertSignedIndex(t *testing.T, tx *wire.MsgTx, 1677 testInputs map[wire.OutPoint]*testInput) map[wire.OutPoint]struct{} { 1678 1679 found := make(map[wire.OutPoint]struct{}) 1680 for idx, txIn := range tx.TxIn { 1681 op := txIn.PreviousOutPoint 1682 1683 // Not a testInput, it won't have the test encoding we require 1684 // to check outpoint and index. 1685 if _, ok := testInputs[op]; !ok { 1686 continue 1687 } 1688 1689 if _, ok := found[op]; ok { 1690 t.Fatalf("input already used") 1691 } 1692 1693 // Check it was signes spending the correct outpoint, and at 1694 // the expected tx input index. 1695 witness, err := input.SigScriptToWitnessStack(txIn.SignatureScript) 1696 require.NoError(t, err) 1697 require.Equal(t, witness[0], op.Hash[:]) 1698 require.Equal(t, witness[1], []byte{txscript.OP_1 + byte(op.Index)}) 1699 require.Equal(t, witness[2], []byte{txscript.OP_1 + byte(idx)}) 1700 found[op] = struct{}{} 1701 } 1702 1703 return found 1704 } 1705 1706 // TestLockTimes checks that the sweeper properly groups inputs requiring the 1707 // same locktime together into sweep transactions. 1708 func TestLockTimes(t *testing.T) { 1709 ctx := createSweeperTestContext(t) 1710 1711 // We increase the number of max inputs to a tx so that won't 1712 // impact our test. 1713 ctx.sweeper.cfg.MaxInputsPerTx = 100 1714 1715 // We will set up the lock times in such a way that we expect the 1716 // sweeper to divide the inputs into 4 diffeerent transactions. 1717 const numSweeps = 4 1718 1719 // Sweep 8 inputs, using 4 different lock times. 1720 var ( 1721 results []chan Result 1722 inputs = make(map[wire.OutPoint]input.Input) 1723 ) 1724 for i := 0; i < numSweeps*2; i++ { 1725 lt := uint32(10 + (i % numSweeps)) 1726 inp := &testInput{ 1727 BaseInput: spendableInputs[i], 1728 locktime: <, 1729 } 1730 1731 result, err := ctx.sweeper.SweepInput( 1732 inp, Params{ 1733 Fee: FeePreference{ConfTarget: 6}, 1734 }, 1735 ) 1736 if err != nil { 1737 t.Fatal(err) 1738 } 1739 results = append(results, result) 1740 1741 op := inp.OutPoint() 1742 inputs[*op] = inp 1743 } 1744 1745 // We also add 3 regular inputs that don't require any specific lock 1746 // time. 1747 for i := 0; i < 3; i++ { 1748 inp := spendableInputs[i+numSweeps*2] 1749 result, err := ctx.sweeper.SweepInput( 1750 inp, Params{ 1751 Fee: FeePreference{ConfTarget: 6}, 1752 }, 1753 ) 1754 if err != nil { 1755 t.Fatal(err) 1756 } 1757 1758 results = append(results, result) 1759 1760 op := inp.OutPoint() 1761 inputs[*op] = inp 1762 } 1763 1764 // We expect all inputs to be published in separate transactions, even 1765 // though they share the same fee preference. 1766 ctx.tick() 1767 1768 // Check the sweeps transactions, ensuring all inputs are there, and 1769 // all the locktimes are satisfied. 1770 for i := 0; i < numSweeps; i++ { 1771 sweepTx := ctx.receiveTx() 1772 if len(sweepTx.TxOut) != 1 { 1773 t.Fatal("expected a single tx out in the sweep tx") 1774 } 1775 1776 for _, txIn := range sweepTx.TxIn { 1777 op := txIn.PreviousOutPoint 1778 inp, ok := inputs[op] 1779 if !ok { 1780 t.Fatalf("Unexpected outpoint: %v", op) 1781 } 1782 1783 delete(inputs, op) 1784 1785 // If this input had a required locktime, ensure the tx 1786 // has that set correctly. 1787 lt, ok := inp.RequiredLockTime() 1788 if !ok { 1789 continue 1790 } 1791 1792 if lt != sweepTx.LockTime { 1793 t.Fatalf("Input required locktime %v, sweep "+ 1794 "tx had locktime %v", lt, sweepTx.LockTime) 1795 } 1796 1797 } 1798 } 1799 1800 // The should be no inputs not foud in any of the sweeps. 1801 if len(inputs) != 0 { 1802 t.Fatalf("had unsweeped inputs") 1803 } 1804 1805 // Mine the first sweeps 1806 ctx.backend.mine() 1807 1808 // Results should all come back. 1809 for i := range results { 1810 result := <-results[i] 1811 if result.Err != nil { 1812 t.Fatal("expected input to be swept") 1813 } 1814 } 1815 } 1816 1817 // TestRequiredTxOuts checks that inputs having a required TxOut gets swept with 1818 // sweep transactions paying into these outputs. 1819 func TestRequiredTxOuts(t *testing.T) { 1820 // Create some test inputs and locktime vars. 1821 var inputs []*input.BaseInput 1822 for i := 0; i < 20; i++ { 1823 input := createTestInput( 1824 int64(dcrutil.AtomsPerCoin+i*500), 1825 input.CommitmentTimeLock, 1826 ) 1827 1828 inputs = append(inputs, &input) 1829 } 1830 1831 locktime1 := uint32(51) 1832 locktime2 := uint32(52) 1833 locktime3 := uint32(53) 1834 1835 aPkScript := make([]byte, input.P2PKHPkScriptSize) 1836 aPkScript[0] = 'a' 1837 1838 bPkScript := make([]byte, input.P2SHPkScriptSize) 1839 bPkScript[0] = 'b' 1840 1841 ePkScript := make([]byte, 25) 1842 ePkScript[0] = 'e' 1843 1844 testCases := []struct { 1845 name string 1846 inputs []*testInput 1847 assertSweeps func(*testing.T, map[wire.OutPoint]*testInput, 1848 []*wire.MsgTx) 1849 }{ 1850 { 1851 // Single input with a required TX out that is smaller. 1852 // We expect a change output to be added. 1853 name: "single input, leftover change", 1854 inputs: []*testInput{ 1855 { 1856 BaseInput: inputs[0], 1857 reqTxOut: &wire.TxOut{ 1858 PkScript: aPkScript, 1859 Value: 100000, 1860 }, 1861 }, 1862 }, 1863 1864 // Since the required output value is small, we expect 1865 // the rest after fees to go into a change output. 1866 assertSweeps: func(t *testing.T, 1867 _ map[wire.OutPoint]*testInput, 1868 txs []*wire.MsgTx) { 1869 1870 require.Equal(t, 1, len(txs)) 1871 1872 tx := txs[0] 1873 require.Equal(t, 1, len(tx.TxIn)) 1874 1875 // We should have two outputs, the required 1876 // output must be the first one. 1877 require.Equal(t, 2, len(tx.TxOut)) 1878 out := tx.TxOut[0] 1879 require.Equal(t, aPkScript, out.PkScript) 1880 require.Equal(t, int64(100000), out.Value) 1881 }, 1882 }, 1883 { 1884 // An input committing to a slightly smaller output, so 1885 // it will pay its own fees. 1886 name: "single input, no change", 1887 inputs: []*testInput{ 1888 { 1889 BaseInput: inputs[0], 1890 reqTxOut: &wire.TxOut{ 1891 PkScript: aPkScript, 1892 1893 // Fee will be about 5340 sats. 1894 // Subtract a bit more to 1895 // ensure no dust change output 1896 // is manifested. 1897 Value: inputs[0].SignDesc().Output.Value - 6300, 1898 }, 1899 }, 1900 }, 1901 1902 // We expect this single input/output pair. 1903 assertSweeps: func(t *testing.T, 1904 _ map[wire.OutPoint]*testInput, 1905 txs []*wire.MsgTx) { 1906 1907 require.Equal(t, 1, len(txs)) 1908 1909 tx := txs[0] 1910 require.Equal(t, 1, len(tx.TxIn)) 1911 1912 require.Equal(t, 1, len(tx.TxOut)) 1913 out := tx.TxOut[0] 1914 require.Equal(t, aPkScript, out.PkScript) 1915 require.Equal( 1916 t, 1917 inputs[0].SignDesc().Output.Value-6300, 1918 out.Value, 1919 ) 1920 }, 1921 }, 1922 { 1923 // Two inputs, where the first one required no tx out. 1924 name: "two inputs, one with required tx out", 1925 inputs: []*testInput{ 1926 { 1927 1928 // We add a normal, non-requiredTxOut 1929 // input. We use test input 10, to make 1930 // sure this has a higher yield than 1931 // the other input, and will be 1932 // attempted added first to the sweep 1933 // tx. 1934 BaseInput: inputs[10], 1935 }, 1936 { 1937 // The second input requires a TxOut. 1938 BaseInput: inputs[0], 1939 reqTxOut: &wire.TxOut{ 1940 PkScript: aPkScript, 1941 Value: inputs[0].SignDesc().Output.Value, 1942 }, 1943 }, 1944 }, 1945 1946 // We expect the inputs to have been reordered. 1947 assertSweeps: func(t *testing.T, 1948 _ map[wire.OutPoint]*testInput, 1949 txs []*wire.MsgTx) { 1950 1951 require.Equal(t, 1, len(txs)) 1952 1953 tx := txs[0] 1954 require.Equal(t, 2, len(tx.TxIn)) 1955 require.Equal(t, 2, len(tx.TxOut)) 1956 1957 // The required TxOut should be the first one. 1958 out := tx.TxOut[0] 1959 require.Equal(t, aPkScript, out.PkScript) 1960 require.Equal( 1961 t, inputs[0].SignDesc().Output.Value, 1962 out.Value, 1963 ) 1964 1965 // The first input should be the one having the 1966 // required TxOut. 1967 require.Len(t, tx.TxIn, 2) 1968 require.Equal( 1969 t, inputs[0].OutPoint(), 1970 &tx.TxIn[0].PreviousOutPoint, 1971 ) 1972 1973 // Second one is the one without a required tx 1974 // out. 1975 require.Equal( 1976 t, inputs[10].OutPoint(), 1977 &tx.TxIn[1].PreviousOutPoint, 1978 ) 1979 }, 1980 }, 1981 1982 { 1983 // An input committing to an output of equal value, just 1984 // add input to pay fees. 1985 name: "single input, extra fee input", 1986 inputs: []*testInput{ 1987 { 1988 BaseInput: inputs[0], 1989 reqTxOut: &wire.TxOut{ 1990 PkScript: aPkScript, 1991 Value: inputs[0].SignDesc().Output.Value, 1992 }, 1993 }, 1994 }, 1995 1996 // We expect an extra input and output. 1997 assertSweeps: func(t *testing.T, 1998 _ map[wire.OutPoint]*testInput, 1999 txs []*wire.MsgTx) { 2000 2001 require.Equal(t, 1, len(txs)) 2002 2003 tx := txs[0] 2004 require.Equal(t, 2, len(tx.TxIn)) 2005 2006 require.Equal(t, 2, len(tx.TxOut)) 2007 out := tx.TxOut[0] 2008 require.Equal(t, aPkScript, out.PkScript) 2009 require.Equal( 2010 t, inputs[0].SignDesc().Output.Value, 2011 out.Value, 2012 ) 2013 }, 2014 }, 2015 { 2016 // Three inputs added, should be combined into a single 2017 // sweep. 2018 name: "three inputs", 2019 inputs: []*testInput{ 2020 { 2021 BaseInput: inputs[0], 2022 reqTxOut: &wire.TxOut{ 2023 PkScript: aPkScript, 2024 Value: inputs[0].SignDesc().Output.Value, 2025 }, 2026 }, 2027 { 2028 BaseInput: inputs[1], 2029 reqTxOut: &wire.TxOut{ 2030 PkScript: bPkScript, 2031 Value: inputs[1].SignDesc().Output.Value, 2032 }, 2033 }, 2034 { 2035 BaseInput: inputs[2], 2036 reqTxOut: &wire.TxOut{ 2037 PkScript: ePkScript, 2038 Value: inputs[2].SignDesc().Output.Value, 2039 }, 2040 }, 2041 }, 2042 2043 // We expect an extra input and output to pay fees. 2044 assertSweeps: func(t *testing.T, 2045 testInputs map[wire.OutPoint]*testInput, 2046 txs []*wire.MsgTx) { 2047 2048 require.Equal(t, 1, len(txs)) 2049 2050 tx := txs[0] 2051 require.Equal(t, 4, len(tx.TxIn)) 2052 require.Equal(t, 4, len(tx.TxOut)) 2053 2054 // The inputs and outputs must be in the same 2055 // order. 2056 for i, in := range tx.TxIn { 2057 // Last one is the change input/output 2058 // pair, so we'll skip it. 2059 if i == 3 { 2060 continue 2061 } 2062 2063 // Get this input to ensure the output 2064 // on index i coresponsd to this one. 2065 inp := testInputs[in.PreviousOutPoint] 2066 require.NotNil(t, inp) 2067 2068 require.Equal( 2069 t, tx.TxOut[i].Value, 2070 inp.SignDesc().Output.Value, 2071 ) 2072 } 2073 }, 2074 }, 2075 { 2076 // Six inputs added, which 3 different locktimes. 2077 // Should result in 3 sweeps. 2078 name: "six inputs", 2079 inputs: []*testInput{ 2080 { 2081 BaseInput: inputs[0], 2082 locktime: &locktime1, 2083 reqTxOut: &wire.TxOut{ 2084 PkScript: aPkScript, 2085 Value: inputs[0].SignDesc().Output.Value, 2086 }, 2087 }, 2088 { 2089 BaseInput: inputs[1], 2090 locktime: &locktime1, 2091 reqTxOut: &wire.TxOut{ 2092 PkScript: bPkScript, 2093 Value: inputs[1].SignDesc().Output.Value, 2094 }, 2095 }, 2096 { 2097 BaseInput: inputs[2], 2098 locktime: &locktime2, 2099 reqTxOut: &wire.TxOut{ 2100 PkScript: ePkScript, 2101 Value: inputs[2].SignDesc().Output.Value, 2102 }, 2103 }, 2104 { 2105 BaseInput: inputs[3], 2106 locktime: &locktime2, 2107 reqTxOut: &wire.TxOut{ 2108 PkScript: aPkScript, 2109 Value: inputs[3].SignDesc().Output.Value, 2110 }, 2111 }, 2112 { 2113 BaseInput: inputs[4], 2114 locktime: &locktime3, 2115 reqTxOut: &wire.TxOut{ 2116 PkScript: bPkScript, 2117 Value: inputs[4].SignDesc().Output.Value, 2118 }, 2119 }, 2120 { 2121 BaseInput: inputs[5], 2122 locktime: &locktime3, 2123 reqTxOut: &wire.TxOut{ 2124 PkScript: ePkScript, 2125 Value: inputs[5].SignDesc().Output.Value, 2126 }, 2127 }, 2128 }, 2129 2130 // We expect three sweeps, each having two of our 2131 // inputs, one extra input and output to pay fees. 2132 assertSweeps: func(t *testing.T, 2133 testInputs map[wire.OutPoint]*testInput, 2134 txs []*wire.MsgTx) { 2135 2136 require.Equal(t, 3, len(txs)) 2137 2138 for _, tx := range txs { 2139 require.Equal(t, 3, len(tx.TxIn)) 2140 require.Equal(t, 3, len(tx.TxOut)) 2141 2142 // The inputs and outputs must be in 2143 // the same order. 2144 for i, in := range tx.TxIn { 2145 // Last one is the change 2146 // output, so we'll skip it. 2147 if i == 2 { 2148 continue 2149 } 2150 2151 // Get this input to ensure the 2152 // output on index i coresponsd 2153 // to this one. 2154 inp := testInputs[in.PreviousOutPoint] 2155 require.NotNil(t, inp) 2156 2157 require.Equal( 2158 t, tx.TxOut[i].Value, 2159 inp.SignDesc().Output.Value, 2160 ) 2161 2162 // Check that the locktimes are 2163 // kept intact. 2164 require.Equal( 2165 t, tx.LockTime, 2166 *inp.locktime, 2167 ) 2168 } 2169 } 2170 }, 2171 }, 2172 } 2173 2174 for _, testCase := range testCases { 2175 testCase := testCase 2176 2177 t.Run(testCase.name, func(t *testing.T) { 2178 ctx := createSweeperTestContext(t) 2179 2180 // We increase the number of max inputs to a tx so that 2181 // won't impact our test. 2182 ctx.sweeper.cfg.MaxInputsPerTx = 100 2183 2184 // Sweep all test inputs. 2185 var ( 2186 inputs = make(map[wire.OutPoint]*testInput) 2187 results = make(map[wire.OutPoint]chan Result) 2188 ) 2189 for _, inp := range testCase.inputs { 2190 result, err := ctx.sweeper.SweepInput( 2191 inp, Params{ 2192 Fee: FeePreference{ConfTarget: 6}, 2193 }, 2194 ) 2195 if err != nil { 2196 t.Fatal(err) 2197 } 2198 2199 op := inp.OutPoint() 2200 results[*op] = result 2201 inputs[*op] = inp 2202 } 2203 2204 // Tick, which should trigger a sweep of all inputs. 2205 ctx.tick() 2206 2207 // Check the sweeps transactions, ensuring all inputs 2208 // are there, and all the locktimes are satisfied. 2209 var sweeps []*wire.MsgTx 2210 Loop: 2211 for { 2212 select { 2213 case tx := <-ctx.publishChan: 2214 sweeps = append(sweeps, &tx) 2215 case <-time.After(200 * time.Millisecond): 2216 break Loop 2217 } 2218 } 2219 2220 // Mine the sweeps. 2221 ctx.backend.mine() 2222 2223 // Results should all come back. 2224 for _, resultChan := range results { 2225 result := <-resultChan 2226 if result.Err != nil { 2227 t.Fatalf("expected input to be "+ 2228 "swept: %v", result.Err) 2229 } 2230 } 2231 2232 // Assert the transactions are what we expect. 2233 testCase.assertSweeps(t, inputs, sweeps) 2234 2235 // Finally we assert that all our test inputs were part 2236 // of the sweeps, and that they were signed correctly. 2237 sweptInputs := make(map[wire.OutPoint]struct{}) 2238 for _, sweep := range sweeps { 2239 swept := assertSignedIndex(t, sweep, inputs) 2240 for op := range swept { 2241 if _, ok := sweptInputs[op]; ok { 2242 t.Fatalf("outpoint %v part of "+ 2243 "previous sweep", op) 2244 } 2245 2246 sweptInputs[op] = struct{}{} 2247 } 2248 } 2249 2250 require.Equal(t, len(inputs), len(sweptInputs)) 2251 for op := range sweptInputs { 2252 _, ok := inputs[op] 2253 if !ok { 2254 t.Fatalf("swept input %v not part of "+ 2255 "test inputs", op) 2256 } 2257 } 2258 }) 2259 } 2260 } 2261 2262 // TestSweeperShutdownHandling tests that we notify callers when the sweeper 2263 // cannot handle requests since it's in the process of shutting down. 2264 func TestSweeperShutdownHandling(t *testing.T) { 2265 ctx := createSweeperTestContext(t) 2266 2267 // Make the backing notifier break down. This is what happens during 2268 // lnd shut down, since the notifier is stopped before the sweeper. 2269 require.Len(t, ctx.notifier.epochChan, 1) 2270 for epochChan := range ctx.notifier.epochChan { 2271 close(epochChan) 2272 } 2273 2274 // Give the collector some time to exit. 2275 time.Sleep(50 * time.Millisecond) 2276 2277 // Now trying to sweep inputs should return an error on the error 2278 // channel. 2279 resultChan, err := ctx.sweeper.SweepInput( 2280 spendableInputs[0], defaultFeePref, 2281 ) 2282 require.NoError(t, err) 2283 2284 select { 2285 case res := <-resultChan: 2286 require.Equal(t, ErrSweeperShuttingDown, res.Err) 2287 2288 case <-time.After(defaultTestTimeout): 2289 t.Fatalf("no result arrived") 2290 } 2291 2292 // Stop the sweeper properly. 2293 err = ctx.sweeper.Stop() 2294 require.NoError(t, err) 2295 2296 // Now attempting to sweep an input should error out immediately. 2297 _, err = ctx.sweeper.SweepInput( 2298 spendableInputs[0], defaultFeePref, 2299 ) 2300 require.Error(t, err) 2301 }