@@ -15,6 +15,7 @@ import (
15
15
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
16
16
"github.com/lightningnetwork/lnd/lntest"
17
17
"github.com/lightningnetwork/lnd/lntest/node"
18
+ "github.com/lightningnetwork/lnd/lntest/rpc"
18
19
"github.com/lightningnetwork/lnd/lntest/wait"
19
20
"github.com/lightningnetwork/lnd/lntypes"
20
21
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
@@ -2081,3 +2082,255 @@ func testBumpForceCloseFee(ht *lntest.HarnessTest) {
2081
2082
// This is needed to clean up the mempool.
2082
2083
ht .MineBlocksAndAssertNumTxes (1 , 2 )
2083
2084
}
2085
+
2086
+ // testFeeReplacement tests that when a sweeping txns aggregates multiple
2087
+ // outgoing HTLCs, and one of the outgoing HTLCs has been spent via the direct
2088
+ // preimage path by the remote peer, the remaining HTLCs will be grouped again
2089
+ // and swept immediately.
2090
+ //
2091
+ // Setup:
2092
+ // 1. Fund Alice with 1 UTXOs - she only needs one for the funding process,
2093
+ // 2. Fund Bob with 3 UTXOs - he needs one for the funding process, one for
2094
+ // his CPFP anchor sweeping, and one for sweeping his outgoing HTLCs.
2095
+ // 3. Create a linear network from Alice -> Bob -> Carol.
2096
+ // 4. Alice pays two invoices to Carol, with Carol holding the settlement.
2097
+ // 5. Bob goes offline.
2098
+ // 6. Carol settles one of the invoices, so she can later spend Bob's outgoing
2099
+ // HTLC via the direct preimage path.
2100
+ // 7. Carol goes offline and Bob comes online.
2101
+ // 8. Mine enough blocks so Bob will force close Bob=>Carol to claim his
2102
+ // outgoing HTLCs.
2103
+ // 9. Carol comes online, sweeps one of Bob's outgoing HTLCs and it confirms.
2104
+ // 10. Bob creates a new sweeping tx to sweep his remaining HTLC with a
2105
+ // previous fee rate.
2106
+ //
2107
+ // Test:
2108
+ // 1. Bob will immediately sweeps his remaining outgoing HTLC given that the
2109
+ // other one has been spent by Carol.
2110
+ // 2. Bob's new sweeping tx will use the previous fee rate instead of
2111
+ // initializing a new starting fee rate.
2112
+ func testFeeReplacement (ht * lntest.HarnessTest ) {
2113
+ // Set the min relay feerate to be 10 sat/vbyte so the non-CPFP anchor
2114
+ // is never swept.
2115
+ //
2116
+ // TODO(yy): delete this line once the normal anchor sweeping is
2117
+ // removed.
2118
+ ht .SetMinRelayFeerate (10_000 )
2119
+
2120
+ // Setup testing params.
2121
+ //
2122
+ // Invoice is 100k sats.
2123
+ invoiceAmt := btcutil .Amount (100_000 )
2124
+
2125
+ // Alice will send two payments.
2126
+ numPayments := 2
2127
+
2128
+ // Use the smallest CLTV so we can mine fewer blocks.
2129
+ cltvDelta := routing .MinCLTVDelta
2130
+
2131
+ // Prepare params.
2132
+ cfg := []string {
2133
+ "--protocol.anchors" ,
2134
+ // Use a small CLTV to mine less blocks.
2135
+ fmt .Sprintf ("--bitcoin.timelockdelta=%d" , cltvDelta ),
2136
+ // Use a very large CSV, this way to_local outputs are never
2137
+ // swept so we can focus on testing HTLCs.
2138
+ fmt .Sprintf ("--bitcoin.defaultremotedelay=%v" , cltvDelta * 10 ),
2139
+ }
2140
+ cfgs := [][]string {cfg , cfg , cfg }
2141
+
2142
+ openChannelParams := lntest.OpenChannelParams {
2143
+ Amt : invoiceAmt * 100 ,
2144
+ }
2145
+
2146
+ // Create a three hop network: Alice -> Bob -> Carol.
2147
+ _ , nodes := ht .CreateSimpleNetwork (cfgs , openChannelParams )
2148
+
2149
+ // Unwrap the results.
2150
+ alice , bob , carol := nodes [0 ], nodes [1 ], nodes [2 ]
2151
+
2152
+ // Bob needs two more wallet utxos:
2153
+ // - when sweeping anchors, he needs one utxo for each sweep.
2154
+ // - when sweeping HTLCs, he needs one utxo for each sweep.
2155
+ numUTXOs := 2
2156
+
2157
+ // Bob should have enough wallet UTXOs here to sweep the HTLC in the
2158
+ // end of this test. However, due to a known issue, Bob's wallet may
2159
+ // report there's no UTXO available. For details,
2160
+ // - https://github.com/lightningnetwork/lnd/issues/8786
2161
+ //
2162
+ // TODO(yy): remove this extra UTXO once the issue is resolved.
2163
+ numUTXOs ++
2164
+
2165
+ // For neutrino backend, we need two more UTXOs for Bob to create his
2166
+ // sweeping txns.
2167
+ if ht .IsNeutrinoBackend () {
2168
+ numUTXOs += 2
2169
+ }
2170
+
2171
+ ht .FundNumCoins (bob , numUTXOs )
2172
+
2173
+ // We also give Carol 2 coins to create her sweeping txns.
2174
+ ht .FundNumCoins (carol , 2 )
2175
+
2176
+ // Create numPayments HTLCs on Bob's incoming and outgoing channels.
2177
+ preimages := make ([][]byte , 0 , numPayments )
2178
+ streams := make ([]rpc.SingleInvoiceClient , 0 , numPayments )
2179
+ for i := 0 ; i < numPayments ; i ++ {
2180
+ // Create the preimage.
2181
+ var preimage lntypes.Preimage
2182
+ copy (preimage [:], ht .Random32Bytes ())
2183
+ payHashHold := preimage .Hash ()
2184
+ preimages = append (preimages , preimage [:])
2185
+
2186
+ // Subscribe the invoices.
2187
+ stream := carol .RPC .SubscribeSingleInvoice (payHashHold [:])
2188
+ streams = append (streams , stream )
2189
+
2190
+ // Carol create the hold invoice.
2191
+ invoiceReqHold := & invoicesrpc.AddHoldInvoiceRequest {
2192
+ Value : int64 (invoiceAmt ),
2193
+ CltvExpiry : finalCltvDelta ,
2194
+ Hash : payHashHold [:],
2195
+ }
2196
+ invoiceHold := carol .RPC .AddHoldInvoice (invoiceReqHold )
2197
+
2198
+ // Let Alice pay the invoices.
2199
+ req := & routerrpc.SendPaymentRequest {
2200
+ PaymentRequest : invoiceHold .PaymentRequest ,
2201
+ TimeoutSeconds : 60 ,
2202
+ FeeLimitMsat : noFeeLimitMsat ,
2203
+ }
2204
+
2205
+ // Assert the payments are inflight.
2206
+ ht .SendPaymentAndAssertStatus (
2207
+ alice , req , lnrpc .Payment_IN_FLIGHT ,
2208
+ )
2209
+
2210
+ // Wait for Carol to mark invoice as accepted. There is a small
2211
+ // gap to bridge between adding the htlc to the channel and
2212
+ // executing the exit hop logic.
2213
+ ht .AssertInvoiceState (stream , lnrpc .Invoice_ACCEPTED )
2214
+ }
2215
+
2216
+ // At this point, all 3 nodes should now have an active channel with
2217
+ // the created HTLCs pending on all of them.
2218
+ //
2219
+ // Alice should have numPayments outgoing HTLCs on channel Alice -> Bob.
2220
+ ht .AssertNumActiveHtlcs (alice , numPayments )
2221
+
2222
+ // Bob should have 2 * numPayments HTLCs,
2223
+ // - numPayments incoming HTLCs on channel Alice -> Bob.
2224
+ // - numPayments outgoing HTLCs on channel Bob -> Carol.
2225
+ ht .AssertNumActiveHtlcs (bob , numPayments * 2 )
2226
+
2227
+ // Carol should have numPayments incoming HTLCs on channel Bob -> Carol.
2228
+ ht .AssertNumActiveHtlcs (carol , numPayments )
2229
+
2230
+ // Suspend Bob so he won't get the preimage from Carol.
2231
+ restartBob := ht .SuspendNode (bob )
2232
+
2233
+ // Carol settles the first invoice.
2234
+ carol .RPC .SettleInvoice (preimages [0 ])
2235
+ ht .AssertInvoiceState (streams [0 ], lnrpc .Invoice_SETTLED )
2236
+
2237
+ // Carol goes offline so the preimage won't be sent to Bob.
2238
+ restartCarol := ht .SuspendNode (carol )
2239
+
2240
+ // Bob comes online.
2241
+ require .NoError (ht , restartBob ())
2242
+
2243
+ // We'll now mine enough blocks to trigger Bob to force close channel
2244
+ // Bob->Carol due to his outgoing HTLC is about to timeout. With the
2245
+ // default outgoing broadcast delta of zero, this will be the same
2246
+ // height as the outgoing htlc's expiry height.
2247
+ numBlocks := padCLTV (uint32 (
2248
+ finalCltvDelta - lncfg .DefaultOutgoingBroadcastDelta ,
2249
+ ))
2250
+ ht .MineEmptyBlocks (int (numBlocks ))
2251
+
2252
+ // Assert Bob's force closing tx has been broadcast. We should see two
2253
+ // txns in the mempool:
2254
+ // 1. Bob's force closing tx.
2255
+ // 2. Bob's anchor sweeping tx CPFPing the force close tx.
2256
+ ht .AssertForceCloseAndAnchorTxnsInMempool ()
2257
+
2258
+ // Mine a block to confirm Bob's force close tx and anchor sweeping tx
2259
+ // so we can focus on testing his outgoing HTLCs.
2260
+ ht .MineBlocksAndAssertNumTxes (1 , 2 )
2261
+
2262
+ // Bob should have numPayments pending sweep for the outgoing HTLCs.
2263
+ ht .AssertNumPendingSweeps (bob , numPayments )
2264
+
2265
+ // Bob should have one sweeping tx in the mempool, which sweeps all his
2266
+ // outgoing HTLCs.
2267
+ outgoingSweep0 := ht .GetNumTxsFromMempool (1 )[0 ]
2268
+
2269
+ // We now mine one empty block so Bob will perform one fee bump, after
2270
+ // which his sweeping tx should be updated with a new fee rate. We do
2271
+ // this so we can test later when Bob sweeps his remaining HTLC, the new
2272
+ // sweeping tx will start with the current fee rate.
2273
+ //
2274
+ // Calculate Bob's initial sweeping fee rate.
2275
+ initialFeeRate := ht .CalculateTxFeeRate (outgoingSweep0 )
2276
+
2277
+ // Mine one block to trigger Bob's RBF.
2278
+ ht .MineEmptyBlocks (1 )
2279
+
2280
+ // Make sure Bob's old sweeping tx has been removed from the mempool.
2281
+ ht .AssertTxNotInMempool (outgoingSweep0 .TxHash ())
2282
+
2283
+ // Get the feerate of Bob's current sweeping tx.
2284
+ outgoingSweep1 := ht .GetNumTxsFromMempool (1 )[0 ]
2285
+ currentFeeRate := ht .CalculateTxFeeRate (outgoingSweep1 )
2286
+
2287
+ // Assert the Bob has updated the fee rate.
2288
+ require .Greater (ht , currentFeeRate , initialFeeRate )
2289
+
2290
+ delta := currentFeeRate - initialFeeRate
2291
+
2292
+ // Check the shape of the sweeping tx - we expect it to be
2293
+ // 3-input-3-output as a wallet utxo is used and a required output is
2294
+ // made.
2295
+ require .Len (ht , outgoingSweep1 .TxIn , numPayments + 1 )
2296
+ require .Len (ht , outgoingSweep1 .TxOut , numPayments + 1 )
2297
+
2298
+ // Restart Carol, once she is online, she will try to settle the HTLCs
2299
+ // via the direct preimage spend.
2300
+ require .NoError (ht , restartCarol ())
2301
+
2302
+ // Carol should have 1 incoming HTLC and 1 anchor output to sweep.
2303
+ ht .AssertNumPendingSweeps (carol , 2 )
2304
+
2305
+ // Assert Bob's sweeping tx has been replaced by Carol's.
2306
+ ht .AssertTxNotInMempool (outgoingSweep1 .TxHash ())
2307
+ carolSweepTx := ht .GetNumTxsFromMempool (1 )[0 ]
2308
+
2309
+ // Assume the miner is now happy with Carol's fee, and it gets included
2310
+ // in the next block.
2311
+ ht .MineBlockWithTx (carolSweepTx )
2312
+
2313
+ // Upon receiving the above block, Bob should immediately create a
2314
+ // sweeping tx and broadcast it using the remaining outgoing HTLC.
2315
+ //
2316
+ // Bob should have numPayments-1 pending sweep for the outgoing HTLCs.
2317
+ ht .AssertNumPendingSweeps (bob , numPayments - 1 )
2318
+
2319
+ // Assert Bob immediately sweeps his remaining HTLC with the previous
2320
+ // fee rate.
2321
+ outgoingSweep2 := ht .GetNumTxsFromMempool (1 )[0 ]
2322
+
2323
+ // Calculate the fee rate.
2324
+ feeRate := ht .CalculateTxFeeRate (outgoingSweep2 )
2325
+
2326
+ // We expect the current fee rate to be equal to the last fee rate he
2327
+ // used plus the delta, as we expect the fee rate to stay on the initial
2328
+ // line given by his fee function.
2329
+ expectedFeeRate := currentFeeRate + delta
2330
+ require .InEpsilonf (ht , uint64 (expectedFeeRate ),
2331
+ uint64 (feeRate ), 0.02 , "want %d, got %d in tx=%v" ,
2332
+ currentFeeRate , feeRate , outgoingSweep2 .TxHash ())
2333
+
2334
+ // Finally, clean the mempol.
2335
+ ht .MineBlocksAndAssertNumTxes (1 , 1 )
2336
+ }
0 commit comments