From 7c5be4d0561031a4481c835c37c5988a4747d372 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 10 Aug 2023 21:53:47 -0700 Subject: [PATCH] itest: add taproot support for all variants of runMultiHopHtlcClaimTest --- itest/lnd_multi-hop_test.go | 322 +++++++++++++++++++++++++++++++----- lntest/harness.go | 10 +- lntest/utils.go | 1 + 3 files changed, 290 insertions(+), 43 deletions(-) diff --git a/itest/lnd_multi-hop_test.go b/itest/lnd_multi-hop_test.go index 572748a07..846e45958 100644 --- a/itest/lnd_multi-hop_test.go +++ b/itest/lnd_multi-hop_test.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" @@ -49,6 +50,67 @@ var commitWithZeroConf = []struct { commitType: lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE, zeroConf: true, }, + { + commitType: lnrpc.CommitmentType_SIMPLE_TAPROOT, + zeroConf: false, + }, + { + commitType: lnrpc.CommitmentType_SIMPLE_TAPROOT, + zeroConf: true, + }, +} + +// makeRouteHints creates a route hints that will allow Carol to be reached +// using an unadvertised channel created by Bob (Bob -> Carol). If the zeroConf +// bool is set, then the scid alias of Bob will be used in place. +func makeRouteHints(bob, carol *node.HarnessNode, + zeroConf bool) []*lnrpc.RouteHint { + + carolChans := carol.RPC.ListChannels( + &lnrpc.ListChannelsRequest{}, + ) + + carolChan := carolChans.Channels[0] + + hopHint := &lnrpc.HopHint{ + NodeId: carolChan.RemotePubkey, + ChanId: carolChan.ChanId, + FeeBaseMsat: uint32( + chainreg.DefaultBitcoinBaseFeeMSat, + ), + FeeProportionalMillionths: uint32( + chainreg.DefaultBitcoinFeeRate, + ), + CltvExpiryDelta: chainreg.DefaultBitcoinTimeLockDelta, + } + + if zeroConf { + bobChans := bob.RPC.ListChannels( + &lnrpc.ListChannelsRequest{}, + ) + + // Now that we have Bob's channels, scan for the channel he has + // open to Carol so we can use the proper scid. + var found bool + for _, bobChan := range bobChans.Channels { + if bobChan.RemotePubkey == carol.PubKeyStr { + hopHint.ChanId = bobChan.AliasScids[0] + + found = true + + break + } + } + if !found { + bob.Fatalf("unable to create route hint") + } + } + + return []*lnrpc.RouteHint{ + { + HopHints: []*lnrpc.HopHint{hopHint}, + }, + } } // caseRunner defines a single test case runner. @@ -131,6 +193,13 @@ func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, dustPayHash := ht.Random32Bytes() payHash := ht.Random32Bytes() + // If this is a taproot channel, then we'll need to make some manual + // route hints so Alice can actually find a route. + var routeHints []*lnrpc.RouteHint + if c == lnrpc.CommitmentType_SIMPLE_TAPROOT { + routeHints = makeRouteHints(bob, carol, zeroConf) + } + alice.RPC.SendPayment(&routerrpc.SendPaymentRequest{ Dest: carolPubKey, Amt: int64(dustHtlcAmt), @@ -138,6 +207,7 @@ func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, FinalCltvDelta: finalCltvDelta, TimeoutSeconds: 60, FeeLimitMsat: noFeeLimitMsat, + RouteHints: routeHints, }) alice.RPC.SendPayment(&routerrpc.SendPaymentRequest{ @@ -147,6 +217,7 @@ func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, FinalCltvDelta: finalCltvDelta, TimeoutSeconds: 60, FeeLimitMsat: noFeeLimitMsat, + RouteHints: routeHints, }) // Verify that all nodes in the path now have two HTLC's with the @@ -155,6 +226,18 @@ func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, ht.AssertActiveHtlcs(bob, dustPayHash, payHash) ht.AssertActiveHtlcs(carol, dustPayHash, payHash) + // blocksMined records how many blocks have mined after the creation of + // the invoice so it can be used to calculate how many more blocks need + // to be mined to trigger a force close later on. + var blocksMined uint32 + + // If this is a private channel, and it was zero conf, then we'll need + // to mine one block to confirm the channels created above. + if zeroConf && routeHints != nil { + ht.MineBlocksAndAssertNumTxes(1, 2) + blocksMined++ + } + // Increase the fee estimate so that the following force close tx will // be cpfp'ed. ht.SetFeeEstimate(30000) @@ -166,7 +249,7 @@ func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, numBlocks := padCLTV( uint32(finalCltvDelta - lncfg.DefaultOutgoingBroadcastDelta), ) - ht.MineBlocks(numBlocks) + ht.MineBlocks(numBlocks - blocksMined) // Bob's force close transaction should now be found in the mempool. If // there are anchors, we also expect Bob's anchor sweep. @@ -286,6 +369,13 @@ func runMultiHopReceiverChainClaim(ht *lntest.HarnessTest, ht, alice, bob, false, c, zeroConf, ) + // If this is a taproot channel, then we'll need to make some manual + // route hints so Alice can actually find a route. + var routeHints []*lnrpc.RouteHint + if c == lnrpc.CommitmentType_SIMPLE_TAPROOT { + routeHints = makeRouteHints(bob, carol, zeroConf) + } + // With the network active, we'll now add a new hodl invoice at Carol's // end. Make sure the cltv expiry delta is large enough, otherwise Bob // won't send out the outgoing htlc. @@ -297,6 +387,7 @@ func runMultiHopReceiverChainClaim(ht *lntest.HarnessTest, Value: invoiceAmt, CltvExpiry: finalCltvDelta, Hash: payHash[:], + RouteHints: routeHints, } carolInvoice := carol.RPC.AddHoldInvoice(invoiceReq) @@ -319,6 +410,18 @@ func runMultiHopReceiverChainClaim(ht *lntest.HarnessTest, ht.AssertActiveHtlcs(bob, payHash[:]) ht.AssertActiveHtlcs(carol, payHash[:]) + // blocksMined records how many blocks have mined after the creation of + // the invoice so it can be used to calculate how many more blocks need + // to be mined to trigger a force close later on. + var blocksMined uint32 + + // If this is a private channel, and it was zero conf, then we'll need + // to mine one block to confirm the channels created above. + if zeroConf && routeHints != nil { + ht.MineBlocksAndAssertNumTxes(1, 2) + blocksMined++ + } + // Wait for carol to mark invoice as accepted. There is a small gap to // bridge between adding the htlc to the channel and executing the exit // hop logic. @@ -337,12 +440,16 @@ func runMultiHopReceiverChainClaim(ht *lntest.HarnessTest, // be cpfp'ed. ht.SetFeeEstimate(30000) - // Now we'll mine enough blocks to prompt carol to actually go to the - // chain in order to sweep her HTLC since the value is high enough. + // We now advance the block height to the point where Carol will force + // close her channel with Bob, broadcast the closing tx but keep it + // unconfirmed. numBlocks := padCLTV(uint32( invoiceReq.CltvExpiry - lncfg.DefaultIncomingBroadcastDelta, )) - ht.MineBlocks(numBlocks) + + // Now we'll mine enough blocks to prompt carol to actually go to the + // chain in order to sweep her HTLC since the value is high enough. + ht.MineBlocks(numBlocks - blocksMined) // At this point, Carol should broadcast her active commitment // transaction in order to go to the chain and sweep her HTLC. If there @@ -379,7 +486,7 @@ func runMultiHopReceiverChainClaim(ht *lntest.HarnessTest, // Carol should broadcast her second level HTLC transaction and Bob // should broadcast a sweep tx to sweep his output in the channel with // Carol, and another sweep tx to sweep his anchor output. - case lnrpc.CommitmentType_ANCHORS: + case lnrpc.CommitmentType_ANCHORS, lnrpc.CommitmentType_SIMPLE_TAPROOT: expectedTxes = 3 // Carol should broadcast her second level HTLC transaction and Bob @@ -491,6 +598,13 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // opens up the base for out tests. const htlcAmt = btcutil.Amount(300_000) + // If this is a taproot channel, then we'll need to make some manual + // route hints so Alice can actually find a route. + var routeHints []*lnrpc.RouteHint + if c == lnrpc.CommitmentType_SIMPLE_TAPROOT { + routeHints = makeRouteHints(bob, carol, zeroConf) + } + // We'll now send a single HTLC across our multi-hop network. carolPubKey := carol.PubKey[:] payHash := ht.Random32Bytes() @@ -501,6 +615,7 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, FinalCltvDelta: finalCltvDelta, TimeoutSeconds: 60, FeeLimitMsat: noFeeLimitMsat, + RouteHints: routeHints, } alice.RPC.SendPayment(req) @@ -510,6 +625,18 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, ht.AssertActiveHtlcs(bob, payHash) ht.AssertActiveHtlcs(carol, payHash) + // blocksMined records how many blocks have mined after the creation of + // the invoice so it can be used to calculate how many more blocks need + // to be mined to trigger a force close later on. + var blocksMined uint32 + + // If this is a private channel, and it was zero conf, then we'll need + // to mine one block to confirm the channels created above. + if zeroConf && routeHints != nil { + ht.MineBlocksAndAssertNumTxes(1, 2) + blocksMined++ + } + // Increase the fee estimate so that the following force close tx will // be cpfp'ed. ht.SetFeeEstimate(30000) @@ -523,9 +650,9 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, bob, bobChanPoint, hasAnchors, stream, ) - // Record how many blocks have mined. At this step + // Increase the blocks mined. At this step // AssertStreamChannelForceClosed mines one block. - blocksMined := uint32(1) + blocksMined++ // If the channel closed has anchors, we should expect to see a sweep // transaction for Carol's anchor. @@ -546,7 +673,7 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // CSV expires and the commitment was already mined inside // AssertStreamChannelForceClosed(), so mine one block less // than defaultCSV in order to perform mempool assertions. - ht.MineBlocks(defaultCSV - blocksMined) + ht.MineBlocks(defaultCSV - 1) commitSweepTx := ht.Miner.AssertOutpointInMempool( bobCommitOutpoint, @@ -555,12 +682,13 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] ht.Miner.AssertTxInBlock(block, &txid) - blocksMined = defaultCSV + 1 + blocksMined += defaultCSV } // We'll now mine enough blocks for the HTLC to expire. After this, Bob // should hand off the now expired HTLC output to the utxo nursery. - numBlocks := padCLTV(finalCltvDelta) + numBlocks := padCLTV(uint32(finalCltvDelta) - + lncfg.DefaultOutgoingBroadcastDelta) ht.MineBlocks(numBlocks - blocksMined) // Bob's pending channel report should show that he has a single HTLC @@ -647,6 +775,13 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // opens up the base for out tests. const htlcAmt = btcutil.Amount(30000) + // If this is a taproot channel, then we'll need to make some manual + // route hints so Alice can actually find a route. + var routeHints []*lnrpc.RouteHint + if c == lnrpc.CommitmentType_SIMPLE_TAPROOT { + routeHints = makeRouteHints(bob, carol, zeroConf) + } + // We'll now send a single HTLC across our multi-hop network. var preimage lntypes.Preimage copy(preimage[:], ht.Random32Bytes()) @@ -655,6 +790,7 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, Value: int64(htlcAmt), CltvExpiry: finalCltvDelta, Hash: payHash[:], + RouteHints: routeHints, } carolInvoice := carol.RPC.AddHoldInvoice(invoiceReq) @@ -668,6 +804,18 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, } alice.RPC.SendPayment(req) + // blocksMined records how many blocks have mined after the creation of + // the invoice so it can be used to calculate how many more blocks need + // to be mined to trigger a force close later on. + var blocksMined uint32 + + // If this is a private channel, and it was zero conf, then we'll need + // to mine one block to confirm the channels created above. + if zeroConf && routeHints != nil { + ht.MineBlocksAndAssertNumTxes(1, 2) + blocksMined++ + } + // Once the HTLC has cleared, all the nodes in our mini network should // show that the HTLC has been locked in. ht.AssertActiveHtlcs(alice, payHash[:]) @@ -690,6 +838,10 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, carol, bobChanPoint, hasAnchors, closeStream, ) + // Increase the blocks mined. At this step + // AssertStreamChannelForceClosed mines one block. + blocksMined++ + // At this point, Bob should have a pending force close channel as // Carol has gone directly to chain. ht.AssertNumPendingForceClose(bob, 1) @@ -701,7 +853,7 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, expectedTxes = 1 // Bob can sweep his commit and anchor outputs immediately. - case lnrpc.CommitmentType_ANCHORS: + case lnrpc.CommitmentType_ANCHORS, lnrpc.CommitmentType_SIMPLE_TAPROOT: expectedTxes = 2 // Bob can't sweep his commit output yet as he was the initiator of a @@ -719,12 +871,13 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // Next, we'll mine enough blocks for the HTLC to expire. At this // point, Bob should hand off the output to his internal utxo nursery, // which will broadcast a sweep transaction. - numBlocks := padCLTV(finalCltvDelta - 1) - ht.MineBlocks(numBlocks) + numBlocks := padCLTV(uint32(finalCltvDelta) - + lncfg.DefaultOutgoingBroadcastDelta) + ht.MineBlocks(numBlocks - blocksMined) // If we check Bob's pending channel report, it should show that he has - // a single HTLC that's now in the second stage, as skip the initial - // first stage since this is a direct HTLC. + // a single HTLC that's now in the second stage, as it skipped the + // initial first stage since this is a direct HTLC. ht.AssertNumHTLCsAndStage(bob, bobChanPoint, 1, 2) // We need to generate an additional block to trigger the sweep. @@ -796,6 +949,13 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, ht, alice, bob, false, c, zeroConf, ) + // If this is a taproot channel, then we'll need to make some manual + // route hints so Alice can actually find a route. + var routeHints []*lnrpc.RouteHint + if c == lnrpc.CommitmentType_SIMPLE_TAPROOT { + routeHints = makeRouteHints(bob, carol, zeroConf) + } + // With the network active, we'll now add a new hodl invoice at Carol's // end. Make sure the cltv expiry delta is large enough, otherwise Bob // won't send out the outgoing htlc. @@ -807,6 +967,7 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, Value: invoiceAmt, CltvExpiry: finalCltvDelta, Hash: payHash[:], + RouteHints: routeHints, } carolInvoice := carol.RPC.AddHoldInvoice(invoiceReq) @@ -834,6 +995,18 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, // hop logic. ht.AssertInvoiceState(stream, lnrpc.Invoice_ACCEPTED) + // blocksMined records how many blocks have mined after the creation of + // the invoice so it can be used to calculate how many more blocks need + // to be mined to trigger a force close later on. + var blocksMined uint32 + + // If this is a private channel, and it was zero conf, then we'll need + // to mine one block to confirm the channels created above. + if zeroConf && routeHints != nil { + ht.MineBlocksAndAssertNumTxes(1, 2) + blocksMined++ + } + // Increase the fee estimate so that the following force close tx will // be cpfp'ed. ht.SetFeeEstimate(30000) @@ -848,6 +1021,10 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, bob, aliceChanPoint, hasAnchors, closeStream, ) + // Increase the blocks mined. At this step + // AssertStreamChannelForceClosed mines one block. + blocksMined++ + var expectedTxes int switch c { // Alice will sweep her commitment output immediately. @@ -855,7 +1032,7 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, expectedTxes = 1 // Alice will sweep her commitment and anchor output immediately. - case lnrpc.CommitmentType_ANCHORS: + case lnrpc.CommitmentType_ANCHORS, lnrpc.CommitmentType_SIMPLE_TAPROOT: expectedTxes = 2 // Alice will sweep her anchor output immediately. Her commitment @@ -880,11 +1057,12 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, // channel arbitrator won't go to chain. carol.RPC.SettleInvoice(preimage[:]) - // We'll now mine enough blocks so Carol decides that she needs to go - // on-chain to claim the HTLC as Bob has been inactive. - numBlocks := padCLTV(uint32(invoiceReq.CltvExpiry- - lncfg.DefaultIncomingBroadcastDelta) - 1) - ht.MineBlocks(numBlocks) + // We now advance the block height to the point where Carol will force + // close her channel with Bob, broadcast the closing tx but keep it + // unconfirmed. + numBlocks := padCLTV(uint32(invoiceReq.CltvExpiry - + lncfg.DefaultIncomingBroadcastDelta)) + ht.MineBlocks(numBlocks - blocksMined) // Carol's commitment transaction should now be in the mempool. If // there is an anchor, Carol will sweep that too. @@ -918,7 +1096,7 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, // Carol will broadcast her second level HTLC transaction and Bob will // sweep his commitment and anchor output. - case lnrpc.CommitmentType_ANCHORS: + case lnrpc.CommitmentType_ANCHORS, lnrpc.CommitmentType_SIMPLE_TAPROOT: expectedTxes = 3 // Carol will broadcast her second level HTLC transaction, and Bob will @@ -1090,6 +1268,13 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, ht, alice, bob, false, c, zeroConf, ) + // If this is a taproot channel, then we'll need to make some manual + // route hints so Alice can actually find a route. + var routeHints []*lnrpc.RouteHint + if c == lnrpc.CommitmentType_SIMPLE_TAPROOT { + routeHints = makeRouteHints(bob, carol, zeroConf) + } + // With the network active, we'll now add a new hodl invoice at Carol's // end. Make sure the cltv expiry delta is large enough, otherwise Bob // won't send out the outgoing htlc. @@ -1101,6 +1286,7 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, Value: invoiceAmt, CltvExpiry: finalCltvDelta, Hash: payHash[:], + RouteHints: routeHints, } carolInvoice := carol.RPC.AddHoldInvoice(invoiceReq) @@ -1128,6 +1314,18 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, // hop logic. ht.AssertInvoiceState(stream, lnrpc.Invoice_ACCEPTED) + // blocksMined records how many blocks have mined after the creation of + // the invoice so it can be used to calculate how many more blocks need + // to be mined to trigger a force close later on. + var blocksMined uint32 + + // If this is a private channel, and it was zero conf, then we'll need + // to mine one block to confirm the channels created above. + if zeroConf && routeHints != nil { + ht.MineBlocksAndAssertNumTxes(1, 2) + blocksMined++ + } + // Increase the fee estimate so that the following force close tx will // be cpfp'ed. ht.SetFeeEstimate(30000) @@ -1143,9 +1341,9 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, alice, aliceChanPoint, hasAnchors, closeStream, ) - // Record how many blocks have mined. At this step + // Increase the blocks mined. At this step // AssertStreamChannelForceClosed mines one block. - blocksMined := uint32(1) + blocksMined++ // Wait for the channel to be marked pending force close. ht.AssertChannelPendingForceClose(alice, aliceChanPoint) @@ -1164,8 +1362,8 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, // commit sweep tx will be broadcast immediately before it can // be included in a block, so mine one less than defaultCSV in // order to perform mempool assertions. - ht.MineBlocks(defaultCSV - blocksMined) - blocksMined = defaultCSV + ht.MineBlocks(defaultCSV - 1) + blocksMined += (defaultCSV - 1) // Alice should now sweep her funds. ht.Miner.AssertNumTxsInMempool(1) @@ -1224,7 +1422,7 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, // Carol should broadcast her second level HTLC transaction and Bob // should broadcast a transaction to sweep his commitment output and // another to sweep his anchor output. - case lnrpc.CommitmentType_ANCHORS: + case lnrpc.CommitmentType_ANCHORS, lnrpc.CommitmentType_SIMPLE_TAPROOT: expectedTxes = 3 // Carol should broadcast her second level HTLC transaction and Bob @@ -1353,21 +1551,35 @@ func testMultiHopHtlcAggregation(ht *lntest.HarnessTest) { func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) { - // First, we'll create a three hop network: Alice -> Bob -> Carol. - aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( - ht, alice, bob, false, c, zeroConf, - ) - // For neutrino backend, we need one additional UTXO to create // the sweeping tx for the second-level success txes. if ht.IsNeutrinoBackend() { ht.FundCoins(btcutil.SatoshiPerBitcoin, bob) } + // First, we'll create a three hop network: Alice -> Bob -> Carol. + aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork( + ht, alice, bob, false, c, zeroConf, + ) + + // If this is a taproot channel, then we'll need to make some manual + // route hints so Alice+Carol can actually find a route. + var ( + carolRouteHints []*lnrpc.RouteHint + aliceRouteHints []*lnrpc.RouteHint + ) + if c == lnrpc.CommitmentType_SIMPLE_TAPROOT { + carolRouteHints = makeRouteHints(bob, carol, zeroConf) + aliceRouteHints = makeRouteHints(bob, alice, zeroConf) + } + // To ensure we have capacity in both directions of the route, we'll // make a fairly large payment Alice->Carol and settle it. const reBalanceAmt = 500_000 - invoice := &lnrpc.Invoice{Value: reBalanceAmt} + invoice := &lnrpc.Invoice{ + Value: reBalanceAmt, + RouteHints: carolRouteHints, + } resp := carol.RPC.AddInvoice(invoice) ht.CompletePaymentRequests(alice, []string{resp.PaymentRequest}) @@ -1398,6 +1610,7 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, Value: invoiceAmt, CltvExpiry: finalCltvDelta, Hash: payHash[:], + RouteHints: carolRouteHints, } carolInvoice := carol.RPC.AddHoldInvoice(invoiceReq) @@ -1419,6 +1632,7 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, Value: invoiceAmt, CltvExpiry: thawHeightDelta - 4, Hash: payHash[:], + RouteHints: aliceRouteHints, } aliceInvoice := alice.RPC.AddHoldInvoice(invoiceReq) @@ -1472,6 +1686,18 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, ht.AssertInvoiceState(stream, lnrpc.Invoice_ACCEPTED) } + // blocksMined records how many blocks have mined after the creation of + // the invoice so it can be used to calculate how many more blocks need + // to be mined to trigger a force close later on. + var blocksMined uint32 + + // If this is a private channel, and it was zero conf, then we'll need + // to mine one block to confirm the channels created above. + if zeroConf && carolRouteHints != nil { + ht.MineBlocksAndAssertNumTxes(1, 2) + blocksMined++ + } + // Increase the fee estimate so that the following force close tx will // be cpfp'ed. ht.SetFeeEstimate(30000) @@ -1488,7 +1714,7 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, numBlocks := padCLTV( uint32(finalCltvDelta - lncfg.DefaultOutgoingBroadcastDelta), ) - ht.MineBlocks(numBlocks) + ht.MineBlocks(numBlocks - blocksMined) // Bob's force close transaction should now be found in the mempool. If // there are anchors, we also expect Bob's anchor sweep. @@ -1563,7 +1789,9 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, // Carol will also sweep her commitment and anchor output as separate // txs (since it will be low fee). case lnrpc.CommitmentType_ANCHORS, - lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE: + lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE, + lnrpc.CommitmentType_SIMPLE_TAPROOT: + expectedTxes = 4 default: @@ -1666,7 +1894,7 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, // Mining one additional block, Bob's second level tx is mature, and he // can sweep the output. Before the blocks are mined, we should expect // to see Bob's commit sweep in the mempool. - case lnrpc.CommitmentType_ANCHORS: + case lnrpc.CommitmentType_ANCHORS, lnrpc.CommitmentType_SIMPLE_TAPROOT: ht.MineBlocksAndAssertNumTxes(1, 1) // Since Bob is the initiator of the Bob-Carol script-enforced leased @@ -1782,8 +2010,13 @@ func createThreeHopNetwork(ht *lntest.HarnessTest, ) } - // Prepare params for Alice. + var privateChan bool + if c == lnrpc.CommitmentType_SIMPLE_TAPROOT { + privateChan = true + } + aliceParams := lntest.OpenChannelParams{ + Private: privateChan, Amt: chanAmt, CommitmentType: c, FundingShim: aliceFundingShim, @@ -1802,6 +2035,7 @@ func createThreeHopNetwork(ht *lntest.HarnessTest, // Prepare params for Bob. bobParams := lntest.OpenChannelParams{ Amt: chanAmt, + Private: privateChan, CommitmentType: c, FundingShim: bobFundingShim, ZeroConf: zeroConf, @@ -1840,8 +2074,18 @@ func createThreeHopNetwork(ht *lntest.HarnessTest, } // Make sure alice and carol know each other's channels. - ht.AssertTopologyChannelOpen(alice, bobChanPoint) - ht.AssertTopologyChannelOpen(carol, aliceChanPoint) + // + // We'll only do this though if it wasn't a private channel we opened + // earlier. + if !privateChan { + ht.AssertTopologyChannelOpen(alice, bobChanPoint) + ht.AssertTopologyChannelOpen(carol, aliceChanPoint) + } else { + // Otherwise, we want to wait for all the channels to be shown + // as active before we proceed. + ht.AssertChannelExists(alice, aliceChanPoint) + ht.AssertChannelExists(carol, bobChanPoint) + } return aliceChanPoint, bobChanPoint, carol } diff --git a/lntest/harness.go b/lntest/harness.go index b12255957..3c4374303 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -1782,10 +1782,12 @@ func (h *HarnessTest) OpenMultiChannelsAsync( // Wait for the channel open event from the stream. cp := h.WaitForChannelOpenEvent(req.stream) - // Check that both alice and bob have seen the channel - // from their channel watch request. - h.AssertTopologyChannelOpen(req.Local, cp) - h.AssertTopologyChannelOpen(req.Remote, cp) + if !req.Param.Private { + // Check that both alice and bob have seen the channel + // from their channel watch request. + h.AssertTopologyChannelOpen(req.Local, cp) + h.AssertTopologyChannelOpen(req.Remote, cp) + } // Finally, check that the channel can be seen in their // ListChannels. diff --git a/lntest/utils.go b/lntest/utils.go index 76ffaf6b0..5ac6c355e 100644 --- a/lntest/utils.go +++ b/lntest/utils.go @@ -137,6 +137,7 @@ func CommitTypeHasTaproot(commitType lnrpc.CommitmentType) bool { func CommitTypeHasAnchors(commitType lnrpc.CommitmentType) bool { switch commitType { case lnrpc.CommitmentType_ANCHORS, + lnrpc.CommitmentType_SIMPLE_TAPROOT, lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE: return true default: