From 6addf7e3a82a3ce1b22ef05c34cdb823727ef2bf Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Wed, 17 Jul 2024 07:49:17 +0800 Subject: [PATCH] itest: fix multi hop tests This commit fixes the multi hop tests to reflect the latest sweeper changes. --- itest/lnd_coop_close_with_htlcs_test.go | 2 +- itest/lnd_multi-hop_test.go | 498 +++++++++--------------- lntest/harness.go | 2 +- lntest/harness_assertion.go | 12 +- 4 files changed, 196 insertions(+), 318 deletions(-) diff --git a/itest/lnd_coop_close_with_htlcs_test.go b/itest/lnd_coop_close_with_htlcs_test.go index 56f9e801b0..752525d662 100644 --- a/itest/lnd_coop_close_with_htlcs_test.go +++ b/itest/lnd_coop_close_with_htlcs_test.go @@ -120,7 +120,7 @@ func coopCloseWithHTLCs(ht *lntest.HarnessTest) { ht.AssertTxInMempool(&closeTxid) // Wait for it to get mined and finish tearing down. - ht.AssertStreamChannelCoopClosed(alice, chanPoint, false, closeClient) + ht.AssertStreamChannelCoopClosed(alice, chanPoint, closeClient) } // coopCloseWithHTLCsWithRestart also tests the coop close flow when an HTLC diff --git a/itest/lnd_multi-hop_test.go b/itest/lnd_multi-hop_test.go index d095936196..4a4b08d303 100644 --- a/itest/lnd_multi-hop_test.go +++ b/itest/lnd_multi-hop_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "testing" + "time" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -16,7 +17,6 @@ import ( "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/rpc" - "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/routing" @@ -245,17 +245,15 @@ func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, ) ht.MineBlocks(int(numBlocks)) - // Bob's force close transaction should now be found in the mempool. - ht.AssertNumTxsInMempool(1) + // Bob's force close transaction should now be found in the mempool, + // along with his anchor sweeping tx. + ht.AssertNumTxsInMempool(2) op := ht.OutPointFromChannelPoint(bobChanPoint) closeTx := ht.AssertOutpointInMempool(op) - // Bob's anchor output should be offered to his sweep since Bob has - // time-sensitive HTLCs - we expect both anchors are offered. - ht.AssertNumPendingSweeps(bob, 2) - - // Mine a block to confirm the closing transaction. - ht.MineBlocksAndAssertNumTxes(1, 1) + // Mine a block to confirm the closing transaction and the anchor + // sweeping tx. + ht.MineBlocksAndAssertNumTxes(1, 2) // At this point, Bob should have canceled backwards the dust HTLC // that we sent earlier. This means Alice should now only have a single @@ -264,42 +262,30 @@ func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, // With the closing transaction confirmed, we should expect Bob's HTLC // timeout transaction to be offered to the sweeper due to the expiry - // being reached. we also expect Bon and Carol's anchor sweeps. - ht.AssertNumPendingSweeps(bob, 2) + // being reached. we also expect Carol's anchor sweeps. + ht.AssertNumPendingSweeps(bob, 1) ht.AssertNumPendingSweeps(carol, 1) - // Mine a block to trigger Bob's sweeper to sweep. - ht.MineEmptyBlocks(1) - // The above mined block would trigger Bob and Carol's sweepers to take - // action. We now expect two txns: - // 1. Bob's sweeping tx anchor sweep should now be found in the mempool. - // 2. Bob's HTLC timeout tx sweep should now be found in the mempool. + // action. We now expect one tx: + // 1. Bob's HTLC timeout tx sweep should now be found in the mempool. // Carol's anchor sweep should be failed due to output being dust. - ht.AssertNumTxsInMempool(2) + ht.AssertNumTxsInMempool(1) htlcOutpoint := wire.OutPoint{Hash: closeTx.TxHash(), Index: 2} commitOutpoint := wire.OutPoint{Hash: closeTx.TxHash(), Index: 3} - htlcTimeoutTxid := ht.AssertOutpointInMempool( - htlcOutpoint, - ).TxHash() - - // Mine a block to confirm the above two sweeping txns. - ht.MineBlocksAndAssertNumTxes(1, 2) + htlcTimeoutTxid := ht.AssertOutpointInMempool(htlcOutpoint).TxHash() - // With Bob's HTLC timeout transaction confirmed, there should be no - // active HTLC's on the commitment transaction from Alice -> Bob. - ht.AssertNumActiveHtlcs(alice, 0) + // Mine a block to confirm Bob's HTLC timeout sweeping txns. + ht.MineBlocksAndAssertNumTxes(1, 1) // At this point, Bob should show that the pending HTLC has advanced to // the second stage and is ready to be swept once the timelock is up. + ht.AssertNumHTLCsAndStage(bob, bobChanPoint, 1, 2) + pendingChanResp := bob.RPC.PendingChannels() require.Equal(ht, 1, len(pendingChanResp.PendingForceClosingChannels)) forceCloseChan := pendingChanResp.PendingForceClosingChannels[0] - require.NotZero(ht, forceCloseChan.LimboBalance) - require.Positive(ht, forceCloseChan.BlocksTilMaturity) - require.Equal(ht, 1, len(forceCloseChan.PendingHtlcs)) - require.Equal(ht, uint32(2), forceCloseChan.PendingHtlcs[0].Stage) ht.Logf("Bob's timelock on commit=%v, timelock on htlc=%v", forceCloseChan.BlocksTilMaturity, @@ -315,57 +301,25 @@ func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, // We now mine enough blocks to trigger the sweep of the HTLC // timeout tx. - ht.MineEmptyBlocks(blocksTilMaturity - 1) - - // Check that Bob has one pending sweeping tx - the HTLC - // timeout tx. - ht.AssertNumPendingSweeps(bob, 1) + ht.MineEmptyBlocks(blocksTilMaturity) - // Mine one more blocks, then his commit output will mature. - // This will also trigger the sweeper to sweep his HTLC timeout - // tx. - ht.MineEmptyBlocks(1) - - // Check that Bob has two pending sweeping txns. + // Check that Bob has two pending sweeping requests - the HTLC + // timeout tx and his to_local output. ht.AssertNumPendingSweeps(bob, 2) // Assert that the HTLC timeout tx is now in the mempool. ht.AssertOutpointInMempool(htlcTimeoutOutpoint) - // We now wait for 30 seconds to overcome the flake - there's a - // block race between contractcourt and sweeper, causing the - // sweep to be broadcast earlier. - // - // TODO(yy): remove this once `blockbeat` is in place. - numExpected := 1 - err := wait.NoError(func() error { - mem := ht.GetRawMempool() - if len(mem) == 2 { - numExpected = 2 - return nil - } + // Assert the to_local output is also in the mempool. + ht.AssertOutpointInMempool(commitOutpoint) - return fmt.Errorf("want %d, got %v in mempool: %v", - numExpected, len(mem), mem) - }, wait.DefaultTimeout) - ht.Logf("Checking mempool got: %v", err) - - // Mine a block to trigger the sweep of his commit output and - // confirm his HTLC timeout sweep. - ht.MineBlocksAndAssertNumTxes(1, numExpected) - - // For leased channels, we need to mine one more block to - // confirm Bob's commit output sweep. - // - // NOTE: we mine this block conditionally, as the commit output - // may have already been swept one block earlier due to the - // race in block consumption among subsystems. - pendingChanResp := bob.RPC.PendingChannels() - if len(pendingChanResp.PendingForceClosingChannels) != 0 { - // Check that the sweep spends the expected inputs. - ht.AssertOutpointInMempool(commitOutpoint) - ht.MineBlocksAndAssertNumTxes(1, 1) - } + // Mine a block to confirm the sweeping tx. + ht.MineBlocksAndAssertNumTxes(1, 1) + + // With Bob's HTLC timeout transaction confirmed, there should + // be no active HTLC's on the commitment transaction from Alice + // -> Bob. + ht.AssertNumActiveHtlcs(alice, 0) } else { // Since Bob force closed the channel between him and Carol, he // will incur the usual CSV delay on any outputs that he can @@ -383,16 +337,19 @@ func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, // Check that the sweep spends from the mined commitment. ht.AssertOutpointInMempool(commitOutpoint) - // Mine one more block to trigger the timeout path. - ht.MineBlocksAndAssertNumTxes(1, 1) - // Bob's sweeper should now broadcast his second layer sweep - // due to the CSV on the HTLC timeout output. - ht.AssertOutpointInMempool(htlcTimeoutOutpoint) + // due to the CSV on the HTLC timeout output, along with his + // to_local output sweep. + ht.MineBlocksAndAssertNumTxes(1, 2) // Next, we'll mine a final block that should confirm the // sweeping transactions left. - ht.MineBlocksAndAssertNumTxes(1, 1) + // ht.MineBlocksAndAssertNumTxes(1, 2) + + // With Bob's HTLC timeout transaction confirmed, there should + // be no active HTLC's on the commitment transaction from Alice + // -> Bob. + ht.AssertNumActiveHtlcs(alice, 0) } // Once this transaction has been confirmed, Bob should detect that he @@ -500,38 +457,22 @@ func runMultiHopReceiverChainClaim(ht *lntest.HarnessTest, ht.MineEmptyBlocks(int(numBlocks)) // At this point, Carol should broadcast her active commitment - // transaction in order to go to the chain and sweep her HTLC. - ht.AssertNumTxsInMempool(1) + // transaction in order to go to the chain and sweep her HTLC, along + // with her anchor sweeping tx. + ht.AssertNumTxsInMempool(2) closingTx := ht.AssertOutpointInMempool( ht.OutPointFromChannelPoint(bobChanPoint), ) closingTxid := closingTx.TxHash() - // Carol's anchor should have been offered to her sweeper as she has - // time-sensitive HTLCs. Assert that we have two anchors - one for the - // anchor on the local commitment and the other for the anchor on the - // remote commitment (invalid). - ht.AssertNumPendingSweeps(carol, 2) - // Confirm the commitment. - ht.MineBlocksAndAssertNumTxes(1, 1) - - // The above mined block will trigger Carol's sweeper to publish the - // anchor sweeping tx. - // - // TODO(yy): should instead cancel the broadcast of the anchor sweeping - // tx to save fees since we know the force close tx has been confirmed? - // This is very difficult as it introduces more complicated RBF - // scenarios, as we are using a wallet utxo, which means any txns using - // that wallet utxo must pay more fees. On the other hand, there's no - // way to remove that anchor-CPFP tx from the mempool. - ht.AssertNumTxsInMempool(1) + ht.MineBlocksAndAssertNumTxes(1, 2) // After the force close transaction is mined, Carol should offer her - // second level HTLC tx to the sweeper, which means we should see two - // pending inputs now - the anchor and the htlc. - ht.AssertNumPendingSweeps(carol, 2) + // second level HTLC tx to the sweeper, which means we should see one + // pending input now - the htlc. + ht.AssertNumPendingSweeps(carol, 1) // Restart bob again. require.NoError(ht, restartBob()) @@ -543,25 +484,23 @@ func runMultiHopReceiverChainClaim(ht *lntest.HarnessTest, // second level transaction in the mempool, he will extract the // preimage and settle the HTLC back off-chain. switch c { - // We expect to see three txns in the mempool: + // We expect to see two txns in the mempool: // 1. Carol should broadcast her second level HTLC tx. - // 2. Carol should broadcast her anchor sweeping tx. - // 3. Bob should broadcast a sweep tx to sweep his output in the + // 2. Bob should broadcast a sweep tx to sweep his output in the // channel with Carol, and in the same sweep tx to sweep his anchor // output. case lnrpc.CommitmentType_ANCHORS, lnrpc.CommitmentType_SIMPLE_TAPROOT: - expectedTxes = 3 + expectedTxes = 2 ht.AssertNumPendingSweeps(bob, 2) - // We expect to see two txns in the mempool: + // We expect to see one tx in the mempool: // 1. Carol should broadcast her second level HTLC tx. - // 2. Carol should broadcast her anchor sweeping tx. // Bob would offer his anchor output to his sweeper, but it cannot be // swept due to it being uneconomical. Bob's commit output can't be // swept yet as he's incurring an additional CLTV from being the // channel initiator of a script-enforced leased channel. case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE: - expectedTxes = 2 + expectedTxes = 1 ht.AssertNumPendingSweeps(bob, 1) default: @@ -596,9 +535,6 @@ func runMultiHopReceiverChainClaim(ht *lntest.HarnessTest, // Assert Carol has the pending HTLC sweep. ht.AssertNumPendingSweeps(carol, 1) - // Mine one block to trigger the sweeper to sweep. - ht.MineEmptyBlocks(1) - // We should have a new transaction in the mempool. ht.AssertNumTxsInMempool(1) @@ -730,12 +666,6 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, ht.AssertNumPendingSweeps(carol, 1) ht.AssertNumPendingSweeps(bob, 1) - // Mine a block to confirm Bob's anchor sweep - Carol's anchor sweep - // won't succeed because it's not used for CPFP, so there's no wallet - // utxo used, resulting it to be uneconomical. - ht.MineBlocksAndAssertNumTxes(1, 1) - blocksMined++ - htlcOutpoint := wire.OutPoint{Hash: *closeTx, Index: 2} bobCommitOutpoint := wire.OutPoint{Hash: *closeTx, Index: 3} @@ -752,9 +682,7 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, blocksMined = defaultCSV // Assert Bob has the sweep and trigger it. - ht.AssertNumPendingSweeps(bob, 1) - ht.MineEmptyBlocks(1) - blocksMined++ + ht.AssertNumPendingSweeps(bob, 2) commitSweepTx := ht.AssertOutpointInMempool( bobCommitOutpoint, @@ -776,8 +704,8 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // that's now in stage one. ht.AssertNumHTLCsAndStage(bob, bobChanPoint, 1, 1) - // Bob should have a pending sweep request. - ht.AssertNumPendingSweeps(bob, 1) + // Bob should have two pending sweep requests. + ht.AssertNumPendingSweeps(bob, 2) // Mine one block to trigger Bob's sweeper to sweep it. ht.MineEmptyBlocks(1) @@ -791,10 +719,6 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] ht.AssertTxInBlock(block, &timeoutTx) - // With the second layer timeout transaction confirmed, Bob should have - // canceled backwards the HTLC that carol sent. - ht.AssertNumActiveHtlcs(bob, 0) - // Additionally, Bob should now show that HTLC as being advanced to the // second stage. ht.AssertNumHTLCsAndStage(bob, bobChanPoint, 1, 2) @@ -821,20 +745,13 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // commitment type. if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE { // Assert the expected number of pending sweeps are found. - sweeps := ht.AssertNumPendingSweeps(bob, 2) - + ht.AssertNumPendingSweeps(bob, 3) numExpected = 1 - if sweeps[0].DeadlineHeight != sweeps[1].DeadlineHeight { - numExpected = 2 - } } else { - ht.AssertNumPendingSweeps(bob, 1) + ht.AssertNumPendingSweeps(bob, 2) numExpected = 1 } - // Mine a block to trigger the sweep. - ht.MineEmptyBlocks(1) - // Assert the sweeping tx is found in the mempool. htlcTimeoutOutpoint := wire.OutPoint{Hash: timeoutTx, Index: 0} ht.AssertOutpointInMempool(htlcTimeoutOutpoint) @@ -842,6 +759,10 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // Mine a block to confirm the sweep. ht.MineBlocksAndAssertNumTxes(1, numExpected) + // With the sweeping of second layer timeout tx output confirmed, Bob + // should have canceled backwards the HTLC that Carol sent. + ht.AssertNumActiveHtlcs(bob, 0) + // At this point, Bob should no longer show any channels as pending // close. ht.AssertNumPendingForceClose(bob, 0) @@ -949,31 +870,28 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, ht.AssertNumPendingSweeps(carol, 1) // We expect to see only one sweeping tx to be published from - // Bob, which sweeps his commit and anchor outputs in the same - // tx. For Carol, since her anchor is not used for CPFP, it'd - // be uneconomical to sweep so it will fail. + // Bob, which sweeps his commit output. For Carol, since her + // anchor is not used for CPFP, it'd be uneconomical to sweep + // so it will fail. expectedTxes = 1 // Bob can't sweep his commit output yet as he was the initiator of a // script-enforced leased channel, so he'll always incur the additional - // CLTV. He can still offer his anchor output to his sweeper however. + // CLTV. He can still offer his anchor output to his sweeper however + // but not able to create a valid sweeping tx. case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE: ht.AssertNumPendingSweeps(bob, 1) ht.AssertNumPendingSweeps(carol, 1) - // We expect to see only no sweeping txns to be published, - // neither Bob's or Carol's anchor sweep can succeed due to - // it's uneconomical. + // We expect to see no sweeping txns to be published, neither + // Bob's or Carol's anchor sweep can succeed due to it's + // uneconomical. expectedTxes = 0 default: ht.Fatalf("unhandled commitment type %v", c) } - // Mine one block to trigger the sweeps. - ht.MineEmptyBlocks(1) - blocksMined++ - // We now mine a block to clear up the mempool. ht.MineBlocksAndAssertNumTxes(1, expectedTxes) blocksMined++ @@ -990,17 +908,18 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // initial first stage since this is a direct HTLC. ht.AssertNumHTLCsAndStage(bob, bobChanPoint, 1, 2) - // We need to generate an additional block to expire the CSV 1. + // Bob has failed to sweep his anchor output before, so it's still + // pending. He should have two pending sweep requests - the anchor + // output and the outgoing HTLC. + // + // NOTE: because the direct-timeout spending is handled by the legacy + // sweeping system (UtxoNursery), it's not bounded by the blockbeat. We + // mine one more block to make sure the sweep request is made. + // + // TODO(yy): once UtxoNursery is deprecated, we should remove this + // extra block mining. ht.MineEmptyBlocks(1) - - // For script-enforced leased channels, Bob has failed to sweep his - // anchor output before, so it's still pending. - if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE { - ht.AssertNumPendingSweeps(bob, 2) - } else { - // Bob should have a pending sweep request. - ht.AssertNumPendingSweeps(bob, 1) - } + ht.AssertNumPendingSweeps(bob, 2) // Mine a block to trigger the sweeper to sweep it. ht.MineEmptyBlocks(1) @@ -1038,9 +957,6 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // commit output and one for the anchor output. ht.AssertNumPendingSweeps(bob, 2) - // Mine a block to trigger the sweep. - ht.MineEmptyBlocks(1) - bobCommitOutpoint := wire.OutPoint{Hash: *closeTx, Index: 3} bobCommitSweep := ht.AssertOutpointInMempool( bobCommitOutpoint, @@ -1214,8 +1130,9 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, lncfg.DefaultIncomingBroadcastDelta)) ht.MineEmptyBlocks(int(numBlocks - blocksMined)) - // Carol's commitment transaction should now be in the mempool. - ht.AssertNumTxsInMempool(1) + // Carol's commitment transaction should now be in the mempool, along + // with her anchor sweeping tx. + ht.AssertNumTxsInMempool(2) // Look up the closing transaction. It should be spending from the // funding transaction, @@ -1224,13 +1141,14 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, ) closingTxid := closingTx.TxHash() - // Mine a block that should confirm the commit tx. - block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] + // Mine a block that should confirm the commit tx and the anchor + // sweeping tx. + block := ht.MineBlocksAndAssertNumTxes(1, 2)[0] ht.AssertTxInBlock(block, &closingTxid) - // After the force close transaction is mined, Carol should offer her - // second-level success HTLC tx and anchor to the sweeper. - ht.AssertNumPendingSweeps(carol, 2) + // After the force close tx and anchor sweeping tx are mined, Carol + // should offer her second-level success HTLC tx to the sweeper. + ht.AssertNumPendingSweeps(carol, 1) // Restart bob again. require.NoError(ht, restartBob()) @@ -1245,25 +1163,23 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, // Carol will broadcast her sweeping txns and Bob will sweep his // commitment and anchor outputs, we'd expect to see three txns, // - Carol's second level HTLC transaction. - // - Carol's anchor sweeping txns since it's used for CPFP. // - Bob's sweep tx spending his commitment output, and two anchor // outputs, one from channel Alice to Bob and the other from channel // Bob to Carol. case lnrpc.CommitmentType_ANCHORS, lnrpc.CommitmentType_SIMPLE_TAPROOT: ht.AssertNumPendingSweeps(bob, 3) - expectedTxes = 3 + expectedTxes = 2 // Carol will broadcast her sweeping txns and Bob will sweep his // anchor outputs. Bob can't sweep his commitment output yet as it has // incurred an additional CLTV due to being the initiator of a // script-enforced leased channel: // - Carol's second level HTLC transaction. - // - Carol's anchor sweeping txns since it's used for CPFP. // - Bob's sweep tx spending his two anchor outputs, one from channel // Alice to Bob and the other from channel Bob to Carol. case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE: ht.AssertNumPendingSweeps(bob, 2) - expectedTxes = 3 + expectedTxes = 2 default: ht.Fatalf("unhandled commitment type %v", c) @@ -1347,14 +1263,14 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, // Carol's sweep tx should be broadcast. carolSweep := ht.AssertNumTxsInMempool(1)[0] - // Bob should offer his second level tx to his sweeper. - ht.AssertNumPendingSweeps(bob, 1) - // Mining one additional block, Bob's second level tx is mature, and he // can sweep the output. block = ht.MineBlocksAndAssertNumTxes(bobSecondLevelCSV, 1)[0] ht.AssertTxInBlock(block, carolSweep) + // Bob should offer his second level tx to his sweeper. + ht.AssertNumPendingSweeps(bob, 1) + bobSweep := ht.GetNumTxsFromMempool(1)[0] bobSweepTxid := bobSweep.TxHash() @@ -1557,8 +1473,10 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, )) ht.MineEmptyBlocks(int(numBlocks) - blocksMined) - // Carol's commitment transaction should now be in the mempool. - ht.AssertNumTxsInMempool(1) + // Carol's commitment transaction should now be in the mempool, along + // with her anchor sweeping tx since Carol has time-sensitive HTLCs, + // she will use the anchor for CPFP purpose. + ht.AssertNumTxsInMempool(2) // The closing transaction should be spending from the funding // transaction. @@ -1567,18 +1485,25 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, ) closingTxid := closingTx.TxHash() - // Since Carol has time-sensitive HTLCs, she will use the anchor for - // CPFP purpose. Assert she has two pending anchor sweep requests - one - // from local commit and the other from remote commit. - ht.AssertNumPendingSweeps(carol, 2) + // Assert she has one pending sweep request - the anchor from local + // commit. + if ht.IsNeutrinoBackend() { + // For neutrino there's no `testmempoolaccept`, so Caro will + // try to publish two anchor sweeping txns. + ht.AssertNumPendingSweeps(carol, 2) + } else { + ht.AssertNumPendingSweeps(carol, 1) + } - // Mine a block, which should contain: the commitment. - block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] + // Mine a block, which should contain: + // - the commitment tx. + // - the anchor sweeping tx. + block := ht.MineBlocksAndAssertNumTxes(1, 2)[0] ht.AssertTxInBlock(block, &closingTxid) // After the force close transaction is mined, Carol should offer her - // second level HTLC tx to the sweeper, along with her anchor output. - ht.AssertNumPendingSweeps(carol, 2) + // second level HTLC tx to the sweeper. + ht.AssertNumPendingSweeps(carol, 1) // Restart bob again. require.NoError(ht, restartBob()) @@ -1608,32 +1533,29 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, // Keep track of the second level tx maturity. carolSecondLevelCSV := uint32(defaultCSV) - // Mine a block to trigger the sweeps, also confirms Carol's CPFP - // anchor sweeping. - ht.MineBlocksAndAssertNumTxes(1, 1) + // Mine a block to trigger the sweeps. + ht.MineEmptyBlocks(1) carolSecondLevelCSV-- - ht.AssertNumTxsInMempool(2) - // Mine a block to confirm the expected transactions. + // Mine a block to confirm Carol's HTLC success tx sweep and Bob's + // anchor sweeping tx. ht.MineBlocksAndAssertNumTxes(1, 2) + carolSecondLevelCSV-- // When Bob notices Carol's second level transaction in the block, he // will extract the preimage and offer the HTLC to his sweeper. ht.AssertNumPendingSweeps(bob, 1) - // NOTE: after Bob is restarted, the sweeping of the direct preimage - // spent will happen immediately so we don't need to mine a block to - // trigger Bob's sweeper to sweep it. + // Mine a block to trigger Bob's sweep. + ht.MineEmptyBlocks(1) bobHtlcSweep := ht.GetNumTxsFromMempool(1)[0] - bobHtlcSweepTxid := bobHtlcSweep.TxHash() // It should spend from the commitment in the channel with Alice. ht.AssertTxSpendFrom(bobHtlcSweep, *aliceForceClose) // We'll now mine a block which should confirm Bob's HTLC sweep // transaction. - block = ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &bobHtlcSweepTxid) + ht.MineBlocksAndAssertNumTxes(1, 1) carolSecondLevelCSV-- // Now that the sweeping transaction has been confirmed, Bob should now @@ -1880,12 +1802,12 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, ht.MineEmptyBlocks(int(numBlocks)) // Bob's force close transaction should now be found in the mempool. If - // there are anchors, we expect it to be offered to Bob's sweeper. - ht.AssertNumTxsInMempool(1) + // there are anchors, we expect it to be offered to Bob's sweeper and + // being swept. + ht.AssertNumTxsInMempool(2) - // Bob has two anchor sweep requests, one for remote (invalid) and the - // other for local. - ht.AssertNumPendingSweeps(bob, 2) + // Bob has one sweep request for his local anchor. + ht.AssertNumPendingSweeps(bob, 1) closeTx := ht.AssertOutpointInMempool( ht.OutPointFromChannelPoint(bobChanPoint), @@ -1921,11 +1843,9 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, // Once bob has force closed, we can restart carol. require.NoError(ht, restartCarol()) - // Mine a block to confirm the closing transaction. - ht.MineBlocksAndAssertNumTxes(1, 1) - - // The above mined block will trigger Bob to sweep his anchor output. - ht.AssertNumTxsInMempool(1) + // Mine a block to confirm the closing transaction and the anchor + // sweeping tx. + ht.MineBlocksAndAssertNumTxes(1, 2) // Let Alice settle her invoices. When Bob now gets the preimages, he // has no other option than to broadcast his second-level transactions @@ -1942,7 +1862,7 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, // preimages from Alice. We also expect Carol to sweep her commitment // output. case lnrpc.CommitmentType_LEGACY: - ht.AssertNumPendingSweeps(bob, numInvoices*2+1) + ht.AssertNumPendingSweeps(bob, numInvoices*2) ht.AssertNumPendingSweeps(carol, 1) expectedTxes = 2*numInvoices + 1 @@ -1956,8 +1876,11 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, lnrpc.CommitmentType_SIMPLE_TAPROOT: // Bob should have `numInvoices` for both HTLC success and - // timeout txns, plus one anchor sweep. - ht.AssertNumPendingSweeps(bob, numInvoices*2+1) + // timeout txns. + ht.AssertNumPendingSweeps(bob, numInvoices*2) + + // Mine one block to trigger the sweep of HTLC success. + ht.MineEmptyBlocks(1) // Carol should have commit and anchor outputs. ht.AssertNumPendingSweeps(carol, 2) @@ -1965,17 +1888,13 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, // We expect to see three sweeping txns: // 1. Bob's sweeping tx for all timeout HTLCs. // 2. Bob's sweeping tx for all success HTLCs. - // 3. Carol's sweeping tx for her commit and anchor outputs. + // 3. Carol's sweeping tx for her commit output. expectedTxes = 3 default: ht.Fatalf("unhandled commitment type %v", c) } - // Mine a block to confirm Bob's anchor sweeping, which will also - // trigger his sweeper to sweep HTLCs. - ht.MineBlocksAndAssertNumTxes(1, 1) - // Assert the sweeping txns are found in the mempool. txes := ht.GetNumTxsFromMempool(expectedTxes) @@ -2044,9 +1963,6 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, // Assert the tx has been offered to the sweeper. ht.AssertNumPendingSweeps(bob, 1) - // Mine one block to trigger the sweep. - ht.MineEmptyBlocks(1) - // Find the commitment sweep. bobCommitSweep := ht.GetNumTxsFromMempool(1)[0] ht.AssertTxSpendFrom(bobCommitSweep, closeTxid) @@ -2074,11 +1990,11 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, case lnrpc.CommitmentType_LEGACY: ht.MineBlocksAndAssertNumTxes(2, 1) - // 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. + // Mining two additional blocks, 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, lnrpc.CommitmentType_SIMPLE_TAPROOT: - ht.MineBlocksAndAssertNumTxes(1, 1) + ht.MineBlocksAndAssertNumTxes(2, 1) // Since Bob is the initiator of the Bob-Carol script-enforced leased // channel, he incurs an additional CLTV when sweeping outputs back to @@ -2095,47 +2011,24 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, height := ht.CurrentHeight() bob.AddToLogf("itest: now mine %d blocks at height %d", numBlocks, height) - ht.MineEmptyBlocks(int(numBlocks) - 1) + ht.MineEmptyBlocks(int(numBlocks)) default: ht.Fatalf("unhandled commitment type %v", c) } // Make sure Bob's sweeper has received all the sweeping requests. - ht.AssertNumPendingSweeps(bob, numInvoices*2) - - // Mine one block to trigger the sweeps. - ht.MineEmptyBlocks(1) - + // // For leased channels, Bob's commit output will mature after the above // block. if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE { ht.AssertNumPendingSweeps(bob, numInvoices*2+1) + } else { + ht.AssertNumPendingSweeps(bob, numInvoices*2) } - // We now wait for 30 seconds to overcome the flake - there's a block - // race between contractcourt and sweeper, causing the sweep to be - // broadcast earlier. - // - // TODO(yy): remove this once `blockbeat` is in place. - numExpected := 1 - err := wait.NoError(func() error { - mem := ht.GetRawMempool() - if len(mem) == numExpected { - return nil - } - - if len(mem) > 0 { - numExpected = len(mem) - } - - return fmt.Errorf("want %d, got %v in mempool: %v", numExpected, - len(mem), mem) - }, wait.DefaultTimeout) - ht.Logf("Checking mempool got: %v", err) - // Make sure it spends from the second level tx. - secondLevelSweep := ht.GetNumTxsFromMempool(numExpected)[0] + secondLevelSweep := ht.GetNumTxsFromMempool(1)[0] bobSweep := secondLevelSweep.TxHash() // It should be sweeping all the second-level outputs. @@ -2154,26 +2047,14 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, } } - // TODO(yy): bring the following check back when `blockbeat` is in - // place - atm we may have two sweeping transactions in the mempool. - // require.Equal(ht, 2*numInvoices, secondLvlSpends) + require.Equal(ht, 2*numInvoices, secondLvlSpends) // When we mine one additional block, that will confirm Bob's second // level sweep. Now Bob should have no pending channels anymore, as // this just resolved it by the confirmation of the sweep transaction. - block := ht.MineBlocksAndAssertNumTxes(1, numExpected)[0] + block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] ht.AssertTxInBlock(block, &bobSweep) - // For leased channels, we need to mine one more block to confirm Bob's - // commit output sweep. - // - // NOTE: we mine this block conditionally, as the commit output may - // have already been swept one block earlier due to the race in block - // consumption among subsystems. - pendingChanResp := bob.RPC.PendingChannels() - if len(pendingChanResp.PendingForceClosingChannels) != 0 { - ht.MineBlocksAndAssertNumTxes(1, 1) - } ht.AssertNumPendingForceClose(bob, 0) // THe channel with Alice is still open. @@ -2333,10 +2214,9 @@ func createThreeHopNetwork(ht *lntest.HarnessTest, // testHtlcTimeoutResolverExtractPreimageRemote tests that in the multi-hop // setting, Alice->Bob->Carol, when Bob's outgoing HTLC is swept by Carol using -// the 2nd level success tx2nd level success tx, Bob's timeout resolver will -// extract the preimage from the sweep tx found in mempool or blocks(for -// neutrino). The 2nd level success tx is broadcast by Carol and spends the -// outpoint on her commit tx. +// the 2nd level success tx, Bob's timeout resolver will extract the preimage +// from the sweep tx found in mempool or blocks(for neutrino). The 2nd level +// success tx is broadcast by Carol and spends the outpoint on her commit tx. func testHtlcTimeoutResolverExtractPreimageRemote(ht *lntest.HarnessTest) { runMultiHopHtlcClaimTest(ht, runExtraPreimageFromRemoteCommit) } @@ -2423,30 +2303,51 @@ func runExtraPreimageFromRemoteCommit(ht *lntest.HarnessTest, )) ht.MineEmptyBlocks(int(numBlocks)) - // Carol's force close transaction should now be found in the mempool. - // If there are anchors, we also expect Carol's contractcourt to offer - // the anchors to her sweeper - one from the local commitment and the - // other from the remote. - ht.AssertNumPendingSweeps(carol, 2) + // Carol's anchor sweep request should be found. + if ht.IsNeutrinoBackend() { + // For neutrino there's no `testmempoolaccept`, so Caro will + // try to publish two anchor sweeping txns. + ht.AssertNumPendingSweeps(carol, 2) + } else { + ht.AssertNumPendingSweeps(carol, 1) + } - // We now mine a block to confirm Carol's closing transaction, which - // will trigger her sweeper to sweep her CPFP anchor sweeping. - ht.MineClosingTx(bobChanPoint) + // We now mine a block to confirm two txns: + // 1. Carol's closing transaction. + // 2. Carol's anchor sweeping tx. + ht.MineBlocksAndAssertNumTxes(1, 2) // With the closing transaction confirmed, we should expect Carol's - // HTLC success transaction to be offered to the sweeper along with her - // anchor output. - ht.AssertNumPendingSweeps(carol, 2) + // HTLC success transaction to be offered to the sweeper. + ht.AssertNumPendingSweeps(carol, 1) - // Mine a block to trigger the sweep, and clean up the anchor sweeping - // tx. - ht.MineBlocksAndAssertNumTxes(1, 1) - ht.AssertNumTxsInMempool(1) + // Mine a block to trigger the sweep. + ht.MineEmptyBlocks(1) + + // We should now have Carol's htlc success tx in the mempool. + numTxesMempool := 1 + ht.AssertNumTxsInMempool(numTxesMempool) + + // For neutrino backend, the timeout resolver needs to extract the + // preimage from the blocks. + if ht.IsNeutrinoBackend() { + // Mine a block to confirm Carol's 2nd level success tx. + ht.MineBlocksAndAssertNumTxes(1, 1) + } // Restart Bob. Once he finishes syncing the channel state, he should // notice the force close from Carol. require.NoError(ht, restartBob()) + // Neutrino has a much slower sync when it comes to rescan the chain + // for a given outpoint. Thus we give an extra 10s to let it catch up. + // + // TODO(yy): improve the performance by following this log: + // - [DBG] BTCN: Enqueuing request for ... with birth height 531 + if ht.IsNeutrinoBackend() { + time.Sleep(10 * time.Second) + } + // Get the current height to compute number of blocks to mine to // trigger the htlc timeout resolver from Bob. height := ht.CurrentHeight() @@ -2454,18 +2355,7 @@ func runExtraPreimageFromRemoteCommit(ht *lntest.HarnessTest, // We'll now mine enough blocks to trigger Bob's timeout resolver. numBlocks = htlc.ExpirationHeight - height - lncfg.DefaultOutgoingBroadcastDelta - - // We should now have Carol's htlc success tx in the mempool. - numTxesMempool := 1 - ht.AssertNumTxsInMempool(numTxesMempool) - - // For neutrino backend, the timeout resolver needs to extract the - // preimage from the blocks. - if ht.IsNeutrinoBackend() { - // Mine a block to confirm Carol's 2nd level success tx. - ht.MineBlocksAndAssertNumTxes(1, 1) - numBlocks-- - } + ht.Logf("Mining %d blocks, current=%v", numBlocks, ht.CurrentHeight()) // Mine empty blocks so Carol's htlc success tx stays in mempool. Once // the height is reached, Bob's timeout resolver will resolve the htlc @@ -2598,16 +2488,12 @@ func runExtraPreimageFromLocalCommit(ht *lntest.HarnessTest, // Bob force closes the channel, which gets his commitment tx into the // mempool. - ht.CloseChannelAssertPending(bob, bobChanPoint, true) + _, closeTxid := ht.CloseChannelAssertPending(bob, bobChanPoint, true) - // Bob should now has offered his anchors to his sweeper - both local - // and remote versions. + // Bob should now has offered his anchor to his sweeper. ht.AssertNumPendingSweeps(bob, 2) - // Mine Bob's force close tx. - closeTx := ht.MineClosingTx(bobChanPoint) - - // Mine Bob's anchor sweeping tx. + // Mine Bob's force close tx and anchor sweeping tx. ht.MineBlocksAndAssertNumTxes(1, 1) blocksMined := 1 @@ -2658,7 +2544,7 @@ func runExtraPreimageFromLocalCommit(ht *lntest.HarnessTest, // Construct the htlc output on Bob's commitment tx, and decide its // index based on the commit type below. - htlcOutpoint := wire.OutPoint{Hash: closeTx.TxHash()} + htlcOutpoint := wire.OutPoint{Hash: *closeTxid} // Check the current mempool state and we should see, // - Carol's direct spend tx. @@ -2678,14 +2564,10 @@ func runExtraPreimageFromLocalCommit(ht *lntest.HarnessTest, ht.AssertNumTxsInMempool(1) } - // Get the current height to compute number of blocks to mine to - // trigger the timeout resolver from Bob. - height := ht.CurrentHeight() - // We'll now mine enough blocks to trigger Bob's htlc timeout resolver // to act. Once his timeout resolver starts, it will extract the // preimage from Carol's direct spend tx found in the mempool. - numBlocks = htlc.ExpirationHeight - height - + numBlocks = htlc.ExpirationHeight - ht.CurrentHeight() - lncfg.DefaultOutgoingBroadcastDelta // Decrease the fee rate used by the sweeper so Bob's timeout tx will diff --git a/lntest/harness.go b/lntest/harness.go index 6e80de42d7..3fdc015608 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -1316,7 +1316,7 @@ func (h *HarnessTest) CloseChannel(hn *node.HarnessNode, stream, _ := h.CloseChannelAssertPending(hn, cp, false) - return h.AssertStreamChannelCoopClosed(hn, cp, false, stream) + return h.AssertStreamChannelCoopClosed(hn, cp, stream) } // ForceCloseChannel attempts to force close a non-anchored channel identified diff --git a/lntest/harness_assertion.go b/lntest/harness_assertion.go index c9f5fffcce..ee91d1ee28 100644 --- a/lntest/harness_assertion.go +++ b/lntest/harness_assertion.go @@ -55,7 +55,8 @@ func (h *HarnessTest) WaitForBlockchainSync(hn *node.HarnessNode) { return fmt.Errorf("%s is not synced to chain", hn.Name()) }, DefaultTimeout) - require.NoError(h, err, "timeout waiting for blockchain sync") + require.NoError(h, err, "%s: timeout waiting for blockchain sync", + hn.Name()) } // WaitForBlockchainSyncTo waits until the node is synced to bestBlock. @@ -633,8 +634,7 @@ func (h *HarnessTest) AssertNumPendingForceClose(hn *node.HarnessNode, // - assert the node has zero waiting close channels. // - assert the node has seen the channel close update. func (h *HarnessTest) AssertStreamChannelCoopClosed(hn *node.HarnessNode, - cp *lnrpc.ChannelPoint, anchors bool, - stream rpc.CloseChanClient) *chainhash.Hash { + cp *lnrpc.ChannelPoint, stream rpc.CloseChanClient) *chainhash.Hash { // Assert the channel is waiting close. resp := h.AssertChannelWaitingClose(hn, cp) @@ -647,11 +647,7 @@ func (h *HarnessTest) AssertStreamChannelCoopClosed(hn *node.HarnessNode, // We'll now, generate a single block, wait for the final close status // update, then ensure that the closing transaction was included in the // block. If there are anchors, we also expect an anchor sweep. - expectedTxes := 1 - if anchors { - expectedTxes = 2 - } - block := h.MineBlocksAndAssertNumTxes(1, expectedTxes)[0] + block := h.MineBlocksAndAssertNumTxes(1, 1)[0] // Consume one close event and assert the closing txid can be found in // the block.