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 }