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 }