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

     1  package itest
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/decred/dcrd/wire"
     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  // testMultiHopHtlcRemoteChainClaim tests that in the multi-hop HTLC scenario,
    18  // if the remote party goes to chain while we have an incoming HTLC, then when
    19  // we found out the preimage via the witness beacon, we properly settle the
    20  // HTLC directly on-chain using the preimage in order to ensure that we don't
    21  // lose any funds.
    22  func testMultiHopHtlcRemoteChainClaim(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, false, c,
    32  	)
    33  
    34  	// Clean up carol's node when the test finishes.
    35  	defer shutdownAndAssert(net, t, carol)
    36  
    37  	// With the network active, we'll now add a new hodl invoice at Carol's
    38  	// end. Make sure the cltv expiry delta is large enough, otherwise Bob
    39  	// won't send out the outgoing htlc.
    40  	const invoiceAmt = 100000
    41  	preimage := lntypes.Preimage{1, 2, 5}
    42  	payHash := preimage.Hash()
    43  	invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{
    44  		Value:      invoiceAmt,
    45  		CltvExpiry: 40,
    46  		Hash:       payHash[:],
    47  	}
    48  	ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
    49  	defer cancel()
    50  	carolInvoice, err := carol.AddHoldInvoice(ctxt, invoiceReq)
    51  	require.NoError(t.t, err)
    52  
    53  	// Now that we've created the invoice, we'll send a single payment from
    54  	// Alice to Carol. We won't wait for the response however, as Carol
    55  	// will not immediately settle the payment.
    56  	ctx, cancel := context.WithCancel(ctxb)
    57  	defer cancel()
    58  
    59  	_, err = alice.RouterClient.SendPaymentV2(
    60  		ctx, &routerrpc.SendPaymentRequest{
    61  			PaymentRequest: carolInvoice.PaymentRequest,
    62  			TimeoutSeconds: 60,
    63  			FeeLimitMAtoms: noFeeLimitMAtoms,
    64  		},
    65  	)
    66  	require.NoError(t.t, err)
    67  
    68  	// At this point, all 3 nodes should now have an active channel with
    69  	// the created HTLC pending on all of them.
    70  	nodes := []*lntest.HarnessNode{alice, bob, carol}
    71  	err = wait.NoError(func() error {
    72  		return assertActiveHtlcs(nodes, payHash[:])
    73  	}, defaultTimeout)
    74  	require.NoError(t.t, err)
    75  
    76  	// Wait for carol to mark invoice as accepted. There is a small gap to
    77  	// bridge between adding the htlc to the channel and executing the exit
    78  	// hop logic.
    79  	waitForInvoiceAccepted(t, carol, payHash)
    80  
    81  	// Increase the fee estimate so that the following force close tx will
    82  	// be cpfp'ed.
    83  	net.SetFeeEstimate(30000)
    84  
    85  	// Next, Alice decides that she wants to exit the channel, so she'll
    86  	// immediately force close the channel by broadcast her commitment
    87  	// transaction.
    88  	hasAnchors := commitTypeHasAnchors(c)
    89  	aliceForceClose := closeChannelAndAssertType(
    90  		t, net, alice, aliceChanPoint, hasAnchors, true,
    91  	)
    92  
    93  	// Wait for the channel to be marked pending force close.
    94  	err = waitForChannelPendingForceClose(alice, aliceChanPoint)
    95  	require.NoError(t.t, err)
    96  
    97  	// After closeChannelAndAssertType returns, it has mined a block so now
    98  	// bob will attempt to redeem his anchor commitment (if the channel
    99  	// type is of that type).
   100  	if hasAnchors {
   101  		_, err = waitForNTxsInMempool(
   102  			net.Miner.Node, 1, minerMempoolTimeout,
   103  		)
   104  		require.NoError(t.t, err)
   105  	}
   106  
   107  	if c != lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE {
   108  		// Mine enough blocks for Alice to sweep her funds from the
   109  		// force closed channel. closeChannelAndAssertType() already
   110  		// mined a block containing the commitment tx and the commit
   111  		// sweep tx will be broadcast immediately before it can be
   112  		// included in a block, so mine one less than defaultCSV in
   113  		// order to perform mempool assertions.
   114  		_, err = net.Generate(defaultCSV - 1)
   115  		require.NoError(t.t, err)
   116  
   117  		// Alice should now sweep her funds.
   118  		_, err = waitForNTxsInMempool(
   119  			net.Miner.Node, 1, minerMempoolTimeout,
   120  		)
   121  		require.NoError(t.t, err)
   122  	}
   123  
   124  	// Suspend bob, so Carol is forced to go on chain.
   125  	restartBob, err := net.SuspendNode(bob)
   126  	require.NoError(t.t, err)
   127  
   128  	// Settle invoice. This will just mark the invoice as settled, as there
   129  	// is no link anymore to remove the htlc from the commitment tx. For
   130  	// this test, it is important to actually settle and not leave the
   131  	// invoice in the accepted state, because without a known preimage, the
   132  	// channel arbitrator won't go to chain.
   133  	ctx, cancel = context.WithTimeout(ctxb, defaultTimeout)
   134  	defer cancel()
   135  	_, err = carol.SettleInvoice(ctx, &invoicesrpc.SettleInvoiceMsg{
   136  		Preimage: preimage[:],
   137  	})
   138  	require.NoError(t.t, err)
   139  
   140  	// We'll now mine enough blocks so Carol decides that she needs to go
   141  	// on-chain to claim the HTLC as Bob has been inactive.
   142  	numBlocks := padCLTV(uint32(
   143  		invoiceReq.CltvExpiry - lncfg.DefaultIncomingBroadcastDelta,
   144  	))
   145  	if c != lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE {
   146  		numBlocks -= defaultCSV
   147  	}
   148  
   149  	_, err = net.Generate(numBlocks)
   150  	require.NoError(t.t, err)
   151  
   152  	expectedTxes := 1
   153  	if hasAnchors {
   154  		expectedTxes = 2
   155  	}
   156  
   157  	// Carol's commitment transaction should now be in the mempool. If
   158  	// there are anchors, Carol also sweeps her anchor.
   159  	_, err = waitForNTxsInMempool(
   160  		net.Miner.Node, expectedTxes, minerMempoolTimeout,
   161  	)
   162  	require.NoError(t.t, err)
   163  	bobFundingTxid, err := lnrpc.GetChanPointFundingTxid(bobChanPoint)
   164  	require.NoError(t.t, err)
   165  	carolFundingPoint := wire.OutPoint{
   166  		Hash:  *bobFundingTxid,
   167  		Index: bobChanPoint.OutputIndex,
   168  	}
   169  
   170  	// The closing transaction should be spending from the funding
   171  	// transaction.
   172  	closingTx := getSpendingTxInMempool(
   173  		t, net.Miner.Node, minerMempoolTimeout, carolFundingPoint,
   174  	)
   175  	closingTxid := closingTx.TxHash()
   176  
   177  	// Mine a block, which should contain: the commitment, possibly an
   178  	// anchor sweep and the coinbase tx.
   179  	block := mineBlocks(t, net, 1, expectedTxes)[0]
   180  	require.Len(t.t, block.Transactions, expectedTxes+1)
   181  	assertTxInBlock(t, block, &closingTxid)
   182  
   183  	// Restart bob again.
   184  	err = restartBob()
   185  	require.NoError(t.t, err)
   186  
   187  	// After the force close transacion is mined, we should expect Bob and
   188  	// Carol to broadcast some transactions depending on the channel
   189  	// commitment type.
   190  	switch c {
   191  	// Carol should broadcast her second level HTLC transaction and Bob
   192  	// should broadcast a transaction to sweep his commitment output.
   193  	case lnrpc.CommitmentType_LEGACY:
   194  		expectedTxes = 2
   195  
   196  	// Carol should broadcast her second level HTLC transaction and Bob
   197  	// should broadcast a transaction to sweep his commitment output and
   198  	// another to sweep his anchor output.
   199  	case lnrpc.CommitmentType_ANCHORS:
   200  		expectedTxes = 3
   201  
   202  	// Carol should broadcast her second level HTLC transaction and Bob
   203  	// should broadcast a transaction to sweep his anchor output. Bob can't
   204  	// sweep his commitment output yet as he has incurred an additional CLTV
   205  	// due to being the channel initiator of a force closed script-enforced
   206  	// leased channel.
   207  	case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE:
   208  		expectedTxes = 2
   209  
   210  	default:
   211  		t.Fatalf("unhandled commitment type %v", c)
   212  	}
   213  	txes, err := getNTxsFromMempool(
   214  		net.Miner.Node, expectedTxes, minerMempoolTimeout,
   215  	)
   216  	require.NoError(t.t, err)
   217  
   218  	// All transactions should be pending from the commitment transaction.
   219  	assertAllTxesSpendFrom(t, txes, closingTxid)
   220  
   221  	// Mine a block to confirm the two transactions (+ coinbase).
   222  	block = mineBlocks(t, net, 1, expectedTxes)[0]
   223  	require.Len(t.t, block.Transactions, expectedTxes+1)
   224  
   225  	// Keep track of the second level tx maturity.
   226  	carolSecondLevelCSV := uint32(defaultCSV)
   227  
   228  	// When Bob notices Carol's second level transaction in the block, he
   229  	// will extract the preimage and broadcast a sweep tx to directly claim
   230  	// the HTLC in his (already closed) channel with Alice.
   231  	bobHtlcSweep, err := waitForTxInMempool(
   232  		net.Miner.Node, minerMempoolTimeout,
   233  	)
   234  	require.NoError(t.t, err)
   235  
   236  	// It should spend from the commitment in the channel with Alice.
   237  	tx, err := net.Miner.Node.GetRawTransaction(context.Background(), bobHtlcSweep)
   238  	require.NoError(t.t, err)
   239  	require.Equal(
   240  		t.t, *aliceForceClose, tx.MsgTx().TxIn[0].PreviousOutPoint.Hash,
   241  	)
   242  
   243  	// We'll now mine a block which should confirm Bob's HTLC sweep
   244  	// transaction.
   245  	block = mineBlocks(t, net, 1, 1)[0]
   246  	require.Len(t.t, block.Transactions, 2)
   247  	assertTxInBlock(t, block, bobHtlcSweep)
   248  	carolSecondLevelCSV--
   249  
   250  	// Now that the sweeping transaction has been confirmed, Bob should now
   251  	// recognize that all contracts for the Bob-Carol channel have been
   252  	// fully resolved
   253  	aliceBobPendingChansLeft := 0
   254  	if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE {
   255  		aliceBobPendingChansLeft = 1
   256  	}
   257  	for _, node := range []*lntest.HarnessNode{alice, bob} {
   258  		err = waitForNumChannelPendingForceClose(
   259  			node, aliceBobPendingChansLeft, nil,
   260  		)
   261  		require.NoError(t.t, err)
   262  	}
   263  
   264  	// If we then mine 3 additional blocks, Carol's second level tx will
   265  	// mature, and she should pull the funds.
   266  	_, err = net.Generate(carolSecondLevelCSV)
   267  	require.NoError(t.t, err)
   268  
   269  	carolSweep, err := waitForTxInMempool(
   270  		net.Miner.Node, minerMempoolTimeout,
   271  	)
   272  	require.NoError(t.t, err)
   273  
   274  	// When Carol's sweep gets confirmed, she should have no more pending
   275  	// channels.
   276  	block = mineBlocks(t, net, 1, 1)[0]
   277  	assertTxInBlock(t, block, carolSweep)
   278  
   279  	err = waitForNumChannelPendingForceClose(carol, 0, nil)
   280  	require.NoError(t.t, err)
   281  
   282  	// With the script-enforced lease commitment type, Alice and Bob still
   283  	// haven't been able to sweep their respective commit outputs due to the
   284  	// additional CLTV. We'll need to mine enough blocks for the timelock to
   285  	// expire and prompt their sweep.
   286  	if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE {
   287  		// Due to the way the test is set up, Alice and Bob share the
   288  		// same CLTV for their commit outputs even though it's enforced
   289  		// on different channels (Alice-Bob and Bob-Carol).
   290  		ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
   291  		resp, err := alice.PendingChannels(
   292  			ctxt, &lnrpc.PendingChannelsRequest{},
   293  		)
   294  		require.NoError(t.t, err)
   295  		require.Len(t.t, resp.PendingForceClosingChannels, 1)
   296  		forceCloseChan := resp.PendingForceClosingChannels[0]
   297  		require.Positive(t.t, forceCloseChan.BlocksTilMaturity)
   298  
   299  		// Mine enough blocks for the timelock to expire.
   300  		numBlocks := uint32(forceCloseChan.BlocksTilMaturity)
   301  		_, err = net.Generate(numBlocks)
   302  		require.NoError(t.t, err)
   303  
   304  		// Both Alice and Bob show broadcast their commit sweeps.
   305  		aliceCommitOutpoint := wire.OutPoint{Hash: *aliceForceClose, Index: 3}
   306  		aliceCommitSweep := assertSpendingTxInMempool(
   307  			t, net.Miner.Node, minerMempoolTimeout,
   308  			aliceCommitOutpoint,
   309  		)
   310  		bobCommitOutpoint := wire.OutPoint{Hash: closingTxid, Index: 3}
   311  		bobCommitSweep := assertSpendingTxInMempool(
   312  			t, net.Miner.Node, minerMempoolTimeout,
   313  			bobCommitOutpoint,
   314  		)
   315  
   316  		// Confirm their sweeps.
   317  		block := mineBlocks(t, net, 1, 2)[0]
   318  		assertTxInBlock(t, block, &aliceCommitSweep)
   319  		assertTxInBlock(t, block, &bobCommitSweep)
   320  
   321  		// Alice and Bob should not show any pending channels anymore as
   322  		// they have been fully resolved.
   323  		for _, node := range []*lntest.HarnessNode{alice, bob} {
   324  			err = waitForNumChannelPendingForceClose(node, 0, nil)
   325  			require.NoError(t.t, err)
   326  		}
   327  	}
   328  
   329  	// The invoice should show as settled for Carol, indicating that it was
   330  	// swept on-chain.
   331  	invoicesReq := &lnrpc.ListInvoiceRequest{}
   332  	invoicesResp, err := carol.ListInvoices(ctxb, invoicesReq)
   333  	require.NoError(t.t, err)
   334  	require.Len(t.t, invoicesResp.Invoices, 1)
   335  	invoice := invoicesResp.Invoices[0]
   336  	require.Equal(t.t, lnrpc.Invoice_SETTLED, invoice.State)
   337  	require.Equal(t.t, int64(invoiceAmt), invoice.AmtPaidAtoms)
   338  
   339  	// Finally, check that the Alice's payment is correctly marked
   340  	// succeeded.
   341  	err = checkPaymentStatus(alice, preimage, lnrpc.Payment_SUCCEEDED)
   342  	require.NoError(t.t, err)
   343  }