github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_hold_invoice_force_test.go (about)

     1  package itest
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/decred/dcrlnd/lncfg"
     8  	"github.com/decred/dcrlnd/lnrpc"
     9  	"github.com/decred/dcrlnd/lnrpc/invoicesrpc"
    10  	"github.com/decred/dcrlnd/lnrpc/routerrpc"
    11  	"github.com/decred/dcrlnd/lntest"
    12  	"github.com/decred/dcrlnd/lntest/wait"
    13  	"github.com/decred/dcrlnd/lntypes"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  // testHoldInvoiceForceClose tests cancelation of accepted hold invoices which
    18  // would otherwise trigger force closes when they expire.
    19  func testHoldInvoiceForceClose(net *lntest.NetworkHarness, t *harnessTest) {
    20  	ctxb, cancel := context.WithCancel(context.Background())
    21  	defer cancel()
    22  
    23  	// Open a channel between alice and bob.
    24  	chanReq := lntest.OpenChannelParams{
    25  		Amt: 300000,
    26  	}
    27  
    28  	chanPoint := openChannelAndAssert(
    29  		t, net, net.Alice, net.Bob, chanReq,
    30  	)
    31  
    32  	// Create a non-dust hold invoice for bob.
    33  	var (
    34  		preimage = lntypes.Preimage{1, 2, 3}
    35  		payHash  = preimage.Hash()
    36  	)
    37  	invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{
    38  		Value:      30000,
    39  		CltvExpiry: 40,
    40  		Hash:       payHash[:],
    41  	}
    42  
    43  	ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
    44  	defer cancel()
    45  	bobInvoice, err := net.Bob.AddHoldInvoice(ctxt, invoiceReq)
    46  	require.NoError(t.t, err)
    47  
    48  	// Pay this invoice from Alice -> Bob, we should achieve this with a
    49  	// single htlc.
    50  	_, err = net.Alice.RouterClient.SendPaymentV2(
    51  		ctxb, &routerrpc.SendPaymentRequest{
    52  			PaymentRequest: bobInvoice.PaymentRequest,
    53  			TimeoutSeconds: 60,
    54  			FeeLimitMAtoms: noFeeLimitMAtoms,
    55  		},
    56  	)
    57  	require.NoError(t.t, err)
    58  
    59  	waitForInvoiceAccepted(t, net.Bob, payHash)
    60  
    61  	// Once the HTLC has cleared, alice and bob should both have a single
    62  	// htlc locked in.
    63  	nodes := []*lntest.HarnessNode{net.Alice, net.Bob}
    64  	err = wait.NoError(func() error {
    65  		return assertActiveHtlcs(nodes, payHash[:])
    66  	}, defaultTimeout)
    67  	require.NoError(t.t, err)
    68  
    69  	// Get our htlc expiry height and current block height so that we
    70  	// can mine the exact number of blocks required to expire the htlc.
    71  	chans, err := net.Alice.ListChannels(ctxb, &lnrpc.ListChannelsRequest{})
    72  	require.NoError(t.t, err)
    73  	require.Len(t.t, chans.Channels, 1)
    74  	require.Len(t.t, chans.Channels[0].PendingHtlcs, 1)
    75  	activeHtlc := chans.Channels[0].PendingHtlcs[0]
    76  
    77  	require.NoError(t.t, net.Alice.WaitForBlockchainSync())
    78  	require.NoError(t.t, net.Bob.WaitForBlockchainSync())
    79  
    80  	info, err := net.Alice.GetInfo(ctxb, &lnrpc.GetInfoRequest{})
    81  	require.NoError(t.t, err)
    82  
    83  	// Now we will mine blocks until the htlc expires, and wait for each
    84  	// node to sync to our latest height. Sanity check that we won't
    85  	// underflow.
    86  	require.Greater(
    87  		t.t, activeHtlc.ExpirationHeight, info.BlockHeight,
    88  		"expected expiry after current height",
    89  	)
    90  	blocksTillExpiry := activeHtlc.ExpirationHeight - info.BlockHeight
    91  
    92  	// Alice will go to chain with some delta, sanity check that we won't
    93  	// underflow and subtract this from our mined blocks.
    94  	require.Greater(
    95  		t.t, blocksTillExpiry,
    96  		uint32(lncfg.DefaultOutgoingBroadcastDelta),
    97  	)
    98  	blocksTillForce := blocksTillExpiry - lncfg.DefaultOutgoingBroadcastDelta
    99  
   100  	mineBlocksSlow(t, net, blocksTillForce, 0)
   101  
   102  	require.NoError(t.t, net.Alice.WaitForBlockchainSync())
   103  	require.NoError(t.t, net.Bob.WaitForBlockchainSync())
   104  
   105  	// Our channel should not have been force closed, instead we expect our
   106  	// channel to still be open and our invoice to have been canceled before
   107  	// expiry.
   108  	chanInfo, err := getChanInfo(net.Alice)
   109  	require.NoError(t.t, err)
   110  
   111  	fundingTxID, err := lnrpc.GetChanPointFundingTxid(chanPoint)
   112  	require.NoError(t.t, err)
   113  	chanStr := fmt.Sprintf("%v:%v", fundingTxID, chanPoint.OutputIndex)
   114  	require.Equal(t.t, chanStr, chanInfo.ChannelPoint)
   115  
   116  	err = wait.NoError(func() error {
   117  		inv, err := net.Bob.LookupInvoice(ctxt, &lnrpc.PaymentHash{
   118  			RHash: payHash[:],
   119  		})
   120  		if err != nil {
   121  			return err
   122  		}
   123  
   124  		if inv.State != lnrpc.Invoice_CANCELED {
   125  			return fmt.Errorf("expected canceled invoice, got: %v",
   126  				inv.State)
   127  		}
   128  
   129  		for _, htlc := range inv.Htlcs {
   130  			if htlc.State != lnrpc.InvoiceHTLCState_CANCELED {
   131  				return fmt.Errorf("expected htlc canceled, "+
   132  					"got: %v", htlc.State)
   133  			}
   134  		}
   135  
   136  		return nil
   137  	}, defaultTimeout)
   138  	require.NoError(t.t, err, "expected canceled invoice")
   139  
   140  	// Clean up the channel.
   141  	closeChannelAndAssert(t, net, net.Alice, chanPoint, false)
   142  }