github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go (about) 1 package itest 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/decred/dcrd/wire" 8 "github.com/decred/dcrlnd/lncfg" 9 "github.com/decred/dcrlnd/lnrpc" 10 "github.com/decred/dcrlnd/lnrpc/invoicesrpc" 11 "github.com/decred/dcrlnd/lnrpc/routerrpc" 12 "github.com/decred/dcrlnd/lntest" 13 "github.com/decred/dcrlnd/lntest/wait" 14 "github.com/decred/dcrlnd/lntypes" 15 "github.com/stretchr/testify/require" 16 ) 17 18 // testMultiHopHtlcLocalChainClaim tests that in a multi-hop HTLC scenario, if 19 // we force close a channel with an incoming HTLC, and later find out the 20 // preimage via the witness beacon, we properly settle the HTLC on-chain using 21 // the HTLC success transaction in order to ensure we don't lose any funds. 22 func testMultiHopHtlcLocalChainClaim(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 41 const invoiceAmt = 100000 42 preimage := lntypes.Preimage{1, 2, 3} 43 payHash := preimage.Hash() 44 invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{ 45 Value: invoiceAmt, 46 CltvExpiry: 40, 47 Hash: payHash[:], 48 } 49 ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) 50 defer cancel() 51 carolInvoice, err := carol.AddHoldInvoice(ctxt, invoiceReq) 52 require.NoError(t.t, err) 53 54 // Now that we've created the invoice, we'll send a single payment from 55 // Alice to Carol. We won't wait for the response however, as Carol 56 // will not immediately settle the payment. 57 ctx, cancel := context.WithCancel(ctxb) 58 defer cancel() 59 60 _, err = alice.RouterClient.SendPaymentV2( 61 ctx, &routerrpc.SendPaymentRequest{ 62 PaymentRequest: carolInvoice.PaymentRequest, 63 TimeoutSeconds: 60, 64 FeeLimitMAtoms: noFeeLimitMAtoms, 65 }, 66 ) 67 require.NoError(t.t, err) 68 69 // At this point, all 3 nodes should now have an active channel with 70 // the created HTLC pending on all of them. 71 nodes := []*lntest.HarnessNode{alice, bob, carol} 72 err = wait.NoError(func() error { 73 return assertActiveHtlcs(nodes, payHash[:]) 74 }, defaultTimeout) 75 require.NoError(t.t, err) 76 77 // Wait for carol to mark invoice as accepted. There is a small gap to 78 // bridge between adding the htlc to the channel and executing the exit 79 // hop logic. 80 waitForInvoiceAccepted(t, carol, payHash) 81 82 // Increase the fee estimate so that the following force close tx will 83 // be cpfp'ed. 84 net.SetFeeEstimate(30000) 85 86 // At this point, Bob decides that he wants to exit the channel 87 // immediately, so he force closes his commitment transaction. 88 hasAnchors := commitTypeHasAnchors(c) 89 bobForceClose := closeChannelAndAssertType( 90 t, net, bob, aliceChanPoint, hasAnchors, true, 91 ) 92 93 var expectedTxes int 94 switch c { 95 // Alice will sweep her commitment output immediately. 96 case lnrpc.CommitmentType_LEGACY: 97 expectedTxes = 1 98 99 // Alice will sweep her commitment and anchor output immediately. 100 case lnrpc.CommitmentType_ANCHORS: 101 expectedTxes = 2 102 103 // Alice will sweep her anchor output immediately. Her commitment output 104 // cannot be swept yet as it has incurred an additional CLTV due to 105 // being the initiator of a script-enforced leased channel. 106 case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE: 107 expectedTxes = 1 108 109 default: 110 t.Fatalf("unhandled commitment type %v", c) 111 } 112 _, err = waitForNTxsInMempool( 113 net.Miner.Node, expectedTxes, minerMempoolTimeout, 114 ) 115 require.NoError(t.t, err) 116 117 // Suspend Bob to force Carol to go to chain. 118 restartBob, err := net.SuspendNode(bob) 119 require.NoError(t.t, err) 120 121 // Settle invoice. This will just mark the invoice as settled, as there 122 // is no link anymore to remove the htlc from the commitment tx. For 123 // this test, it is important to actually settle and not leave the 124 // invoice in the accepted state, because without a known preimage, the 125 // channel arbitrator won't go to chain. 126 ctx, cancel = context.WithTimeout(ctxb, defaultTimeout) 127 defer cancel() 128 _, err = carol.SettleInvoice(ctx, &invoicesrpc.SettleInvoiceMsg{ 129 Preimage: preimage[:], 130 }) 131 require.NoError(t.t, err) 132 133 // We'll now mine enough blocks so Carol decides that she needs to go 134 // on-chain to claim the HTLC as Bob has been inactive. We mine up to 135 // just before the deadline to check the transaction is in the mempool. 136 numBlocks := padCLTV(uint32(invoiceReq.CltvExpiry - 137 lncfg.DefaultIncomingBroadcastDelta - 1)) 138 _, err = net.Generate(numBlocks) 139 require.NoError(t.t, err) 140 141 // Carol's commitment transaction should now be in the mempool. If there 142 // is an anchor, Carol will sweep that too. 143 _, err = waitForNTxsInMempool( 144 net.Miner.Node, expectedTxes, minerMempoolTimeout, 145 ) 146 require.NoError(t.t, err) 147 bobFundingTxid, err := lnrpc.GetChanPointFundingTxid(bobChanPoint) 148 require.NoError(t.t, err) 149 carolFundingPoint := wire.OutPoint{ 150 Hash: *bobFundingTxid, 151 Index: bobChanPoint.OutputIndex, 152 } 153 154 // Look up the closing transaction. It should be spending from the 155 // funding transaction, 156 closingTx := getSpendingTxInMempool( 157 t, net.Miner.Node, minerMempoolTimeout, carolFundingPoint, 158 ) 159 closingTxid := closingTx.TxHash() 160 161 // Mine a block that should confirm the commit tx, the anchor if present 162 // and the coinbase. 163 block := mineBlocks(t, net, 1, expectedTxes)[0] 164 require.Len(t.t, block.Transactions, expectedTxes+1) 165 assertTxInBlock(t, block, &closingTxid) 166 167 // Restart bob again. 168 err = restartBob() 169 require.NoError(t.t, err) 170 171 // After the force close transacion is mined, transactions will be 172 // broadcast by both Bob and Carol. 173 switch c { 174 // Carol will broadcast her second level HTLC transaction and Bob will 175 // sweep his commitment output. 176 case lnrpc.CommitmentType_LEGACY: 177 expectedTxes = 2 178 179 // Carol will broadcast her second level HTLC transaction and Bob will 180 // sweep his commitment and anchor output. 181 case lnrpc.CommitmentType_ANCHORS: 182 expectedTxes = 3 183 184 // Carol will broadcast her second level HTLC transaction and anchor 185 // sweep, and Bob will sweep his anchor output. Bob can't sweep his 186 // commitment output yet as it has incurred an additional CLTV due to 187 // being the initiator of a script-enforced leased channel. 188 case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE: 189 expectedTxes = 3 190 191 default: 192 t.Fatalf("unhandled commitment type %v", c) 193 } 194 txes, err := getNTxsFromMempool( 195 net.Miner.Node, expectedTxes, minerMempoolTimeout, 196 ) 197 require.NoError(t.t, err) 198 199 // Both Carol's second level transaction and Bob's sweep should be 200 // spending from the commitment transaction. 201 assertAllTxesSpendFrom(t, txes, closingTxid) 202 203 // At this point we suspend Alice to make sure she'll handle the 204 // on-chain settle after a restart. 205 restartAlice, err := net.SuspendNode(alice) 206 require.NoError(t.t, err) 207 208 // Mine a block to confirm the expected transactions (+ the coinbase). 209 block = mineBlocks(t, net, 1, expectedTxes)[0] 210 require.Len(t.t, block.Transactions, expectedTxes+1) 211 212 // For non-anchor channel types, the nursery will handle sweeping the 213 // second level output, and it will wait one extra block before 214 // sweeping it. 215 secondLevelMaturity := uint32(defaultCSV) 216 217 // If this is a channel of the anchor type, we will subtract one block 218 // from the default CSV, as the Sweeper will handle the input, and the 219 // Sweeper sweeps the input as soon as the lock expires. 220 if hasAnchors { 221 secondLevelMaturity = defaultCSV - 1 222 } 223 224 // Keep track of the second level tx maturity. 225 carolSecondLevelCSV := secondLevelMaturity 226 227 // When Bob notices Carol's second level transaction in the block, he 228 // will extract the preimage and broadcast a second level tx to claim 229 // the HTLC in his (already closed) channel with Alice. 230 bobSecondLvlTx, err := waitForTxInMempool( 231 net.Miner.Node, minerMempoolTimeout, 232 ) 233 require.NoError(t.t, err) 234 235 // It should spend from the commitment in the channel with Alice. 236 tx, err := net.Miner.Node.GetRawTransaction(context.Background(), bobSecondLvlTx) 237 require.NoError(t.t, err) 238 239 require.Equal( 240 t.t, *bobForceClose, tx.MsgTx().TxIn[0].PreviousOutPoint.Hash, 241 ) 242 243 // At this point, Bob should have broadcast his second layer success 244 // transaction, and should have sent it to the nursery for incubation. 245 numPendingChans := 1 246 if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE { 247 numPendingChans++ 248 } 249 err = waitForNumChannelPendingForceClose( 250 bob, numPendingChans, func(c *lnrpcForceCloseChannel) error { 251 if c.Channel.LocalBalance != 0 { 252 return nil 253 } 254 255 if len(c.PendingHtlcs) != 1 { 256 return fmt.Errorf("bob should have pending " + 257 "htlc but doesn't") 258 } 259 260 if c.PendingHtlcs[0].Stage != 1 { 261 return fmt.Errorf("bob's htlc should have "+ 262 "advanced to the first stage but was "+ 263 "stage: %v", c.PendingHtlcs[0].Stage) 264 } 265 266 return nil 267 }, 268 ) 269 require.NoError(t.t, err) 270 271 // We'll now mine a block which should confirm Bob's second layer 272 // transaction. 273 block = mineBlocks(t, net, 1, 1)[0] 274 require.Len(t.t, block.Transactions, 2) 275 assertTxInBlock(t, block, bobSecondLvlTx) 276 277 // Keep track of Bob's second level maturity, and decrement our track 278 // of Carol's. 279 bobSecondLevelCSV := secondLevelMaturity 280 carolSecondLevelCSV-- 281 282 // Now that the preimage from Bob has hit the chain, restart Alice to 283 // ensure she'll pick it up. 284 err = restartAlice() 285 require.NoError(t.t, err) 286 287 // If we then mine 3 additional blocks, Carol's second level tx should 288 // mature, and she can pull the funds from it with a sweep tx. 289 _, err = net.Generate(carolSecondLevelCSV) 290 require.NoError(t.t, err) 291 bobSecondLevelCSV -= carolSecondLevelCSV 292 293 carolSweep, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) 294 require.NoError(t.t, err) 295 296 // Mining one additional block, Bob's second level tx is mature, and he 297 // can sweep the output. 298 block = mineBlocks(t, net, bobSecondLevelCSV, 1)[0] 299 assertTxInBlock(t, block, carolSweep) 300 301 bobSweep, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) 302 require.NoError(t.t, err) 303 304 // Make sure it spends from the second level tx. 305 tx, err = net.Miner.Node.GetRawTransaction(context.Background(), bobSweep) 306 require.NoError(t.t, err) 307 require.Equal( 308 t.t, *bobSecondLvlTx, tx.MsgTx().TxIn[0].PreviousOutPoint.Hash, 309 ) 310 311 // When we mine one additional block, that will confirm Bob's sweep. 312 // Now Bob should have no pending channels anymore, as this just 313 // resolved it by the confirmation of the sweep transaction. 314 block = mineBlocks(t, net, 1, 1)[0] 315 assertTxInBlock(t, block, bobSweep) 316 317 // With the script-enforced lease commitment type, Alice and Bob still 318 // haven't been able to sweep their respective commit outputs due to the 319 // additional CLTV. We'll need to mine enough blocks for the timelock to 320 // expire and prompt their sweep. 321 if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE { 322 for _, node := range []*lntest.HarnessNode{alice, bob} { 323 err = waitForNumChannelPendingForceClose(node, 1, nil) 324 require.NoError(t.t, err) 325 } 326 327 // Due to the way the test is set up, Alice and Bob share the 328 // same CLTV for their commit outputs even though it's enforced 329 // on different channels (Alice-Bob and Bob-Carol). 330 ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) 331 resp, err := alice.PendingChannels( 332 ctxt, &lnrpc.PendingChannelsRequest{}, 333 ) 334 require.NoError(t.t, err) 335 require.Len(t.t, resp.PendingForceClosingChannels, 1) 336 forceCloseChan := resp.PendingForceClosingChannels[0] 337 require.Positive(t.t, forceCloseChan.BlocksTilMaturity) 338 339 // Mine enough blocks for the timelock to expire. 340 numBlocks := uint32(forceCloseChan.BlocksTilMaturity) 341 _, err = net.Generate(numBlocks) 342 require.NoError(t.t, err) 343 344 // Both Alice and Bob show broadcast their commit sweeps. 345 aliceCommitOutpoint := wire.OutPoint{Hash: *bobForceClose, Index: 3} 346 aliceCommitSweep := assertSpendingTxInMempool( 347 t, net.Miner.Node, minerMempoolTimeout, 348 aliceCommitOutpoint, 349 ) 350 bobCommitOutpoint := wire.OutPoint{Hash: closingTxid, Index: 3} 351 bobCommitSweep := assertSpendingTxInMempool( 352 t, net.Miner.Node, minerMempoolTimeout, 353 bobCommitOutpoint, 354 ) 355 356 // Confirm their sweeps. 357 block := mineBlocks(t, net, 1, 2)[0] 358 assertTxInBlock(t, block, &aliceCommitSweep) 359 assertTxInBlock(t, block, &bobCommitSweep) 360 } 361 362 // All nodes should show zero pending and open channels. 363 for _, node := range []*lntest.HarnessNode{alice, bob, carol} { 364 err = waitForNumChannelPendingForceClose(node, 0, nil) 365 require.NoError(t.t, err) 366 assertNodeNumChannels(t, node, 0) 367 } 368 369 // Finally, check that the Alice's payment is correctly marked 370 // succeeded. 371 err = checkPaymentStatus(alice, preimage, lnrpc.Payment_SUCCEEDED) 372 require.NoError(t.t, err) 373 }