github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go (about)

     1  package itest
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/decred/dcrd/dcrutil/v4"
     8  	"github.com/decred/dcrd/wire"
     9  	"github.com/decred/dcrlnd/lncfg"
    10  	"github.com/decred/dcrlnd/lnrpc"
    11  	"github.com/decred/dcrlnd/lnrpc/routerrpc"
    12  	"github.com/decred/dcrlnd/lntest"
    13  	"github.com/decred/dcrlnd/lntest/wait"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  // testMultiHopHtlcLocalTimeout tests that in a multi-hop HTLC scenario, if the
    18  // outgoing HTLC is about to time out, then we'll go to chain in order to claim
    19  // it using the HTLC timeout transaction. Any dust HTLC's should be immediately
    20  // canceled backwards. Once the timeout has been reached, then we should sweep
    21  // it on-chain, and cancel the HTLC backwards.
    22  func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest,
    23  	alice, bob *lntest.HarnessNode, c lnrpc.CommitmentType) {
    24  
    25  	ctxb := context.Background()
    26  
    27  	// First, we'll create a three hop network: Alice -> Bob -> Carol, with
    28  	// Carol refusing to actually settle or directly cancel any HTLC's
    29  	// self.
    30  	aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork(
    31  		t, net, alice, bob, true, c,
    32  	)
    33  
    34  	// Clean up carol's node when the test finishes.
    35  	defer shutdownAndAssert(net, t, carol)
    36  
    37  	time.Sleep(time.Second * 1)
    38  
    39  	// Now that our channels are set up, we'll send two HTLC's from Alice
    40  	// to Carol. The first HTLC will be universally considered "dust",
    41  	// while the second will be a proper fully valued HTLC.
    42  	const (
    43  		dustHtlcAmt    = dcrutil.Amount(100)
    44  		htlcAmt        = dcrutil.Amount(300_00)
    45  		finalCltvDelta = 40
    46  	)
    47  
    48  	ctx, cancel := context.WithCancel(ctxb)
    49  	defer cancel()
    50  
    51  	// We'll create two random payment hashes unknown to carol, then send
    52  	// each of them by manually specifying the HTLC details.
    53  	carolPubKey := carol.PubKey[:]
    54  	dustPayHash := makeFakePayHash(t)
    55  	payHash := makeFakePayHash(t)
    56  
    57  	_, err := alice.RouterClient.SendPaymentV2(
    58  		ctx, &routerrpc.SendPaymentRequest{
    59  			Dest:           carolPubKey,
    60  			Amt:            int64(dustHtlcAmt),
    61  			PaymentHash:    dustPayHash,
    62  			FinalCltvDelta: finalCltvDelta,
    63  			TimeoutSeconds: 60,
    64  			FeeLimitMAtoms: noFeeLimitMAtoms,
    65  		},
    66  	)
    67  	require.NoError(t.t, err)
    68  
    69  	_, err = alice.RouterClient.SendPaymentV2(
    70  		ctx, &routerrpc.SendPaymentRequest{
    71  			Dest:           carolPubKey,
    72  			Amt:            int64(htlcAmt),
    73  			PaymentHash:    payHash,
    74  			FinalCltvDelta: finalCltvDelta,
    75  			TimeoutSeconds: 60,
    76  			FeeLimitMAtoms: noFeeLimitMAtoms,
    77  		},
    78  	)
    79  	require.NoError(t.t, err)
    80  
    81  	// Verify that all nodes in the path now have two HTLC's with the
    82  	// proper parameters.
    83  	nodes := []*lntest.HarnessNode{alice, bob, carol}
    84  	err = wait.NoError(func() error {
    85  		return assertActiveHtlcs(nodes, dustPayHash, payHash)
    86  	}, defaultTimeout)
    87  	require.NoError(t.t, err)
    88  
    89  	// Increase the fee estimate so that the following force close tx will
    90  	// be cpfp'ed.
    91  	net.SetFeeEstimate(30000)
    92  
    93  	// We'll now mine enough blocks to trigger Bob's broadcast of his
    94  	// commitment transaction due to the fact that the HTLC is about to
    95  	// timeout. With the default outgoing broadcast delta of zero, this will
    96  	// be the same height as the htlc expiry height.
    97  	numBlocks := padCLTV(
    98  		uint32(finalCltvDelta - lncfg.DefaultOutgoingBroadcastDelta),
    99  	)
   100  	_, err = net.Generate(numBlocks)
   101  	require.NoError(t.t, err)
   102  
   103  	// Bob's force close transaction should now be found in the mempool. If
   104  	// there are anchors, we also expect Bob's anchor sweep.
   105  	expectedTxes := 1
   106  	hasAnchors := commitTypeHasAnchors(c)
   107  	if hasAnchors {
   108  		expectedTxes = 2
   109  	}
   110  	_, err = waitForNTxsInMempool(
   111  		net.Miner.Node, expectedTxes, minerMempoolTimeout,
   112  	)
   113  	require.NoError(t.t, err)
   114  
   115  	bobFundingTxid, err := lnrpc.GetChanPointFundingTxid(bobChanPoint)
   116  	require.NoError(t.t, err)
   117  	bobChanOutpoint := wire.OutPoint{
   118  		Hash:  *bobFundingTxid,
   119  		Index: bobChanPoint.OutputIndex,
   120  	}
   121  	closeTxid := assertSpendingTxInMempool(
   122  		t, net.Miner.Node, minerMempoolTimeout, bobChanOutpoint,
   123  	)
   124  
   125  	// Mine a block to confirm the closing transaction.
   126  	mineBlocks(t, net, 1, expectedTxes)
   127  
   128  	// At this point, Bob should have canceled backwards the dust HTLC
   129  	// that we sent earlier. This means Alice should now only have a single
   130  	// HTLC on her channel.
   131  	nodes = []*lntest.HarnessNode{alice}
   132  	err = wait.NoError(func() error {
   133  		return assertActiveHtlcs(nodes, payHash)
   134  	}, defaultTimeout)
   135  	require.NoError(t.t, err)
   136  
   137  	// With the closing transaction confirmed, we should expect Bob's HTLC
   138  	// timeout transaction to be broadcast due to the expiry being reached.
   139  	// If there are anchors, we also expect Carol's anchor sweep now.
   140  	_, err = getNTxsFromMempool(
   141  		net.Miner.Node, expectedTxes, minerMempoolTimeout,
   142  	)
   143  	require.NoError(t.t, err)
   144  
   145  	// We'll also obtain the expected HTLC timeout transaction hash.
   146  	htlcOutpoint := wire.OutPoint{Hash: closeTxid, Index: 0}
   147  	commitOutpoint := wire.OutPoint{Hash: closeTxid, Index: 1}
   148  	if hasAnchors {
   149  		htlcOutpoint.Index = 2
   150  		commitOutpoint.Index = 3
   151  	}
   152  	htlcTimeoutTxid := assertSpendingTxInMempool(
   153  		t, net.Miner.Node, minerMempoolTimeout, htlcOutpoint,
   154  	)
   155  
   156  	// Mine a block to confirm the expected transactions.
   157  	_ = mineBlocks(t, net, 1, expectedTxes)
   158  
   159  	// With Bob's HTLC timeout transaction confirmed, there should be no
   160  	// active HTLC's on the commitment transaction from Alice -> Bob.
   161  	err = wait.NoError(func() error {
   162  		return assertNumActiveHtlcs([]*lntest.HarnessNode{alice}, 0)
   163  	}, defaultTimeout)
   164  	require.NoError(t.t, err)
   165  
   166  	// At this point, Bob should show that the pending HTLC has advanced to
   167  	// the second stage and is ready to be swept once the timelock is up.
   168  	ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
   169  	pendingChansRequest := &lnrpc.PendingChannelsRequest{}
   170  	pendingChanResp, err := bob.PendingChannels(ctxt, pendingChansRequest)
   171  	require.NoError(t.t, err)
   172  	require.Equal(t.t, 1, len(pendingChanResp.PendingForceClosingChannels))
   173  	forceCloseChan := pendingChanResp.PendingForceClosingChannels[0]
   174  	require.NotZero(t.t, forceCloseChan.LimboBalance)
   175  	require.Positive(t.t, forceCloseChan.BlocksTilMaturity)
   176  	require.Equal(t.t, 1, len(forceCloseChan.PendingHtlcs))
   177  	require.Equal(t.t, uint32(2), forceCloseChan.PendingHtlcs[0].Stage)
   178  
   179  	htlcTimeoutOutpoint := wire.OutPoint{Hash: htlcTimeoutTxid, Index: 0}
   180  	if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE {
   181  		// Since Bob is the initiator of the script-enforced leased
   182  		// channel between him and Carol, he will incur an additional
   183  		// CLTV on top of the usual CSV delay on any outputs that he can
   184  		// sweep back to his wallet.
   185  		blocksTilMaturity := uint32(forceCloseChan.BlocksTilMaturity)
   186  		mineBlocks(t, net, blocksTilMaturity, 0)
   187  
   188  		// Check that the sweep spends the expected inputs.
   189  		_ = assertSpendingTxInMempool(
   190  			t, net.Miner.Node, minerMempoolTimeout,
   191  			commitOutpoint, htlcTimeoutOutpoint,
   192  		)
   193  	} else {
   194  		// Since Bob force closed the channel between him and Carol, he
   195  		// will incur the usual CSV delay on any outputs that he can
   196  		// sweep back to his wallet. We'll subtract one block from our
   197  		// current maturity period to assert on the mempool.
   198  		mineBlocks(t, net, uint32(forceCloseChan.BlocksTilMaturity-1), 0)
   199  
   200  		// Check that the sweep spends from the mined commitment.
   201  		_ = assertSpendingTxInMempool(
   202  			t, net.Miner.Node, minerMempoolTimeout, commitOutpoint,
   203  		)
   204  
   205  		// Mine a block to confirm Bob's commit sweep tx and assert it
   206  		// was in fact mined.
   207  		_ = mineBlocks(t, net, 1, 1)[0]
   208  
   209  		// Mine an additional block to prompt Bob to broadcast their
   210  		// second layer sweep due to the CSV on the HTLC timeout output.
   211  		mineBlocks(t, net, 1, 0)
   212  		_ = assertSpendingTxInMempool(
   213  			t, net.Miner.Node, minerMempoolTimeout,
   214  			htlcTimeoutOutpoint,
   215  		)
   216  	}
   217  
   218  	// Next, we'll mine a final block that should confirm the sweeping
   219  	// transactions left.
   220  	_, err = net.Generate(1)
   221  	require.NoError(t.t, err)
   222  
   223  	// Once this transaction has been confirmed, Bob should detect that he
   224  	// no longer has any pending channels.
   225  	err = waitForNumChannelPendingForceClose(bob, 0, nil)
   226  	require.NoError(t.t, err)
   227  
   228  	// Coop close channel, expect no anchors.
   229  	closeChannelAndAssertType(t, net, alice, aliceChanPoint, false, false)
   230  }