@@ -581,3 +581,117 @@ func TestPreimagePush(t *testing.T) {
581
581
582
582
require .NoError (t , <- errChan )
583
583
}
584
+
585
+ // TestExpiryBeforeReveal tests the case where the on-chain HTLC expires before
586
+ // we have revealed our preimage. This test demonstrates that the client will
587
+ // erroneously reveal the preimage even though we're nearing our timeout.
588
+ func TestExpiryBeforeReveal (t * testing.T ) {
589
+ defer test .Guard (t )()
590
+
591
+ lnd := test .NewMockLnd ()
592
+ ctx := test .NewContext (t , lnd )
593
+ server := newServerMock (lnd )
594
+
595
+ testReq := * testRequest
596
+
597
+ // Set on-chain HTLC CLTV.
598
+ testReq .Expiry = ctx .Lnd .Height + testLoopOutMinOnChainCltvDelta
599
+
600
+ // Set our fee estimate to higher than our max miner fee will allow.
601
+ lnd .SetFeeEstimate (testReq .SweepConfTarget , chainfee .SatPerKWeight (
602
+ testReq .MaxMinerFee * 2 ,
603
+ ))
604
+
605
+ // Setup the cfg using mock server and init a loop out request.
606
+ cfg := newSwapConfig (
607
+ & lnd .LndServices , newStoreMock (t ), server ,
608
+ )
609
+ initResult , err := newLoopOutSwap (
610
+ context .Background (), cfg , ctx .Lnd .Height , & testReq ,
611
+ )
612
+ require .NoError (t , err )
613
+ swap := initResult .swap
614
+
615
+ // Set up the required dependencies to execute the swap.
616
+ sweeper := & sweep.Sweeper {Lnd : & lnd .LndServices }
617
+ blockEpochChan := make (chan interface {})
618
+ statusChan := make (chan SwapInfo )
619
+ expiryChan := make (chan time.Time )
620
+ timerFactory := func (_ time.Duration ) <- chan time.Time {
621
+ return expiryChan
622
+ }
623
+
624
+ errChan := make (chan error )
625
+ cancelCtx , cancel := context .WithCancel (context .Background ())
626
+ go func () {
627
+ err := swap .execute (cancelCtx , & executeConfig {
628
+ statusChan : statusChan ,
629
+ blockEpochChan : blockEpochChan ,
630
+ timerFactory : timerFactory ,
631
+ sweeper : sweeper ,
632
+ }, ctx .Lnd .Height )
633
+ if err != nil {
634
+ log .Error (err )
635
+ }
636
+ errChan <- err
637
+ }()
638
+
639
+ // The swap should be found in its initial state.
640
+ cfg .store .(* storeMock ).assertLoopOutStored ()
641
+ state := <- statusChan
642
+ require .Equal (t , loopdb .StateInitiated , state .State )
643
+
644
+ // We'll then pay both the swap and prepay invoice, which should trigger
645
+ // the server to publish the on-chain HTLC.
646
+ signalSwapPaymentResult := ctx .AssertPaid (swapInvoiceDesc )
647
+ signalPrepaymentResult := ctx .AssertPaid (prepayInvoiceDesc )
648
+
649
+ signalSwapPaymentResult (nil )
650
+ signalPrepaymentResult (nil )
651
+
652
+ // Notify the confirmation notification for the HTLC.
653
+ ctx .AssertRegisterConf (false , defaultConfirmations )
654
+
655
+ // Advance the block height to get the HTLC confirmed.
656
+ blockEpochChan <- ctx .Lnd .Height + 1
657
+
658
+ htlcTx := wire .NewMsgTx (2 )
659
+ htlcTx .AddTxOut (& wire.TxOut {
660
+ Value : int64 (swap .AmountRequested ),
661
+ PkScript : swap .htlc .PkScript ,
662
+ })
663
+ ctx .NotifyConf (htlcTx )
664
+
665
+ // The client should then register for a spend of the HTLC and attempt
666
+ // to sweep it using the custom confirmation target.
667
+ ctx .AssertRegisterSpendNtfn (swap .htlc .PkScript )
668
+
669
+ // Assert that we made a query to track our payment, as required for
670
+ // preimage push tracking.
671
+ ctx .AssertTrackPayment ()
672
+
673
+ // Tick the expiry channel. Because our max miner fee is too high, we
674
+ // won't attempt a sweep at this point.
675
+ expiryChan <- testTime
676
+
677
+ // Now we decrease our conf target to less than our max miner fee.
678
+ lnd .SetFeeEstimate (testReq .SweepConfTarget , chainfee .SatPerKWeight (
679
+ testReq .MaxMinerFee / 2 ,
680
+ ))
681
+
682
+ // Advance the block height to the point where we would do timeout
683
+ // instead of pushing the preimage.
684
+ blockEpochChan <- lnd .Height + testReq .Expiry
685
+
686
+ // Tick our expiry chan again, this time we expect the swap to
687
+ // publish our sweep timeout, despite expiry having passed, and the
688
+ // potential for a race with the server.
689
+ expiryChan <- testTime
690
+
691
+ // Expect a signing request for the HTLC success transaction.
692
+ <- ctx .Lnd .SignOutputRawChannel
693
+
694
+ // We just cancel the swap now rather than testing to completion.
695
+ cancel ()
696
+ require .Equal (t , context .Canceled , <- errChan )
697
+ }
0 commit comments