From 9cb8b120ba8b78cf3223f4c1e39e1d19fc3a2395 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sun, 7 Aug 2022 06:06:02 +0800 Subject: [PATCH] lntemp+itest: refactor `testAnchorThirdPartySpend` --- lntemp/harness.go | 13 + lntemp/harness_assertion.go | 42 ++ lntemp/harness_miner.go | 32 ++ lntest/itest/assertions.go | 24 -- lntest/itest/list_on_test.go | 4 + lntest/itest/lnd_channel_force_close_test.go | 52 --- lntest/itest/lnd_onchain_test.go | 386 +++++++++---------- lntest/itest/lnd_test_list_on_test.go | 4 - 8 files changed, 278 insertions(+), 279 deletions(-) diff --git a/lntemp/harness.go b/lntemp/harness.go index d014aec07..06c9922fc 100644 --- a/lntemp/harness.go +++ b/lntemp/harness.go @@ -1391,6 +1391,19 @@ func (h *HarnessTest) MineBlocksAndAssertNumTxes(num uint32, return blocks } +// MineEmptyBlocks mines a given number of empty blocks. +// +// NOTE: this differs from miner's `MineEmptyBlocks` as it requires the nodes +// to be synced. +func (h *HarnessTest) MineEmptyBlocks(num int) []*wire.MsgBlock { + blocks := h.Miner.MineEmptyBlocks(num) + + // Finally, make sure all the active nodes are synced. + h.AssertActiveNodesSynced() + + return blocks +} + // QueryChannelByChanPoint tries to find a channel matching the channel point // and asserts. It returns the channel found. func (h *HarnessTest) QueryChannelByChanPoint(hn *node.HarnessNode, diff --git a/lntemp/harness_assertion.go b/lntemp/harness_assertion.go index d4c645d79..8a3c47e31 100644 --- a/lntemp/harness_assertion.go +++ b/lntemp/harness_assertion.go @@ -1927,3 +1927,45 @@ func (h *HarnessTest) AssertHtlcEvents(client rpc.HtlcEventsClient, return events } + +// AssertTransactionInWallet asserts a given txid can be found in the node's +// wallet. +func (h *HarnessTest) AssertTransactionInWallet(hn *node.HarnessNode, + txid chainhash.Hash) { + + req := &lnrpc.GetTransactionsRequest{} + err := wait.NoError(func() error { + txResp := hn.RPC.GetTransactions(req) + for _, txn := range txResp.Transactions { + if txn.TxHash == txid.String() { + return nil + } + } + + return fmt.Errorf("%s: expected txid=%v not found in wallet", + hn.Name(), txid) + }, DefaultTimeout) + + require.NoError(h, err, "failed to find tx") +} + +// AssertTransactionNotInWallet asserts a given txid can NOT be found in the +// node's wallet. +func (h *HarnessTest) AssertTransactionNotInWallet(hn *node.HarnessNode, + txid chainhash.Hash) { + + req := &lnrpc.GetTransactionsRequest{} + err := wait.NoError(func() error { + txResp := hn.RPC.GetTransactions(req) + for _, txn := range txResp.Transactions { + if txn.TxHash == txid.String() { + return fmt.Errorf("expected txid=%v to be "+ + "not found", txid) + } + } + + return nil + }, DefaultTimeout) + + require.NoErrorf(h, err, "%s: failed to assert tx not found", hn.Name()) +} diff --git a/lntemp/harness_miner.go b/lntemp/harness_miner.go index bcccb8656..430779897 100644 --- a/lntemp/harness_miner.go +++ b/lntemp/harness_miner.go @@ -383,3 +383,35 @@ func (h *HarnessMiner) NewMinerAddress() btcutil.Address { require.NoError(h, err, "failed to create new miner address") return addr } + +// MineBlocksWithTxes mines a single block to include the specifies +// transactions only. +func (h *HarnessMiner) MineBlockWithTxes(txes []*btcutil.Tx) *wire.MsgBlock { + var emptyTime time.Time + + // Generate a block. + b, err := h.GenerateAndSubmitBlock(txes, -1, emptyTime) + require.NoError(h, err, "unable to mine block") + + block, err := h.Client.GetBlock(b.Hash()) + require.NoError(h, err, "unable to get block") + + return block +} + +// MineEmptyBlocks mines a given number of empty blocks. +func (h *HarnessMiner) MineEmptyBlocks(num int) []*wire.MsgBlock { + var emptyTime time.Time + + blocks := make([]*wire.MsgBlock, num) + for i := 0; i < num; i++ { + // Generate an empty block. + b, err := h.GenerateAndSubmitBlock(nil, -1, emptyTime) + require.NoError(h, err, "unable to mine empty block") + + block := h.GetBlock(b.Hash()) + blocks[i] = block + } + + return blocks +} diff --git a/lntest/itest/assertions.go b/lntest/itest/assertions.go index 0be40d324..f7eb35e5b 100644 --- a/lntest/itest/assertions.go +++ b/lntest/itest/assertions.go @@ -1418,30 +1418,6 @@ func assertTransactionNotInWallet(t *testing.T, node *lntest.HarnessNode, ) } -func assertAnchorOutputLost(t *harnessTest, node *lntest.HarnessNode, - chanPoint wire.OutPoint) { - - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - err := wait.Predicate(func() bool { - resp, pErr := node.PendingChannels( - context.Background(), pendingChansRequest, - ) - if pErr != nil { - return false - } - - for _, pendingChan := range resp.PendingForceClosingChannels { - if pendingChan.Channel.ChannelPoint == chanPoint.String() { - return (pendingChan.Anchor == - lnrpc.PendingChannelsResponse_ForceClosedChannel_LOST) - } - } - - return false - }, defaultTimeout) - require.NoError(t.t, err, "anchor doesn't show as being lost") -} - // assertNodeAnnouncement compares that two node announcements match. func assertNodeAnnouncement(t *harnessTest, n1, n2 *lnrpc.NodeUpdate) { // Alias should match. diff --git a/lntest/itest/list_on_test.go b/lntest/itest/list_on_test.go index 2c1373c9c..0c53a09ee 100644 --- a/lntest/itest/list_on_test.go +++ b/lntest/itest/list_on_test.go @@ -267,4 +267,8 @@ var allTestCasesTemp = []*lntemp.TestCase{ Name: "anchors reserved value", TestFunc: testAnchorReservedValue, }, + { + Name: "3rd party anchor spend", + TestFunc: testAnchorThirdPartySpend, + }, } diff --git a/lntest/itest/lnd_channel_force_close_test.go b/lntest/itest/lnd_channel_force_close_test.go index 4c87ef973..0857751d4 100644 --- a/lntest/itest/lnd_channel_force_close_test.go +++ b/lntest/itest/lnd_channel_force_close_test.go @@ -15,7 +15,6 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntemp/node" - "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -984,57 +983,6 @@ func padCLTV(cltv uint32) uint32 { return cltv + uint32(routing.BlockPadding) } -type sweptOutput struct { - OutPoint wire.OutPoint - SweepTx *wire.MsgTx -} - -// findCommitAndAnchor looks for a commitment sweep and anchor sweep in the -// mempool. Our anchor output is identified by having multiple inputs, because -// we have to bring another input to add fees to the anchor. Note that the -// anchor swept output may be nil if the channel did not have anchors. -// TODO(yy): delete. -func findCommitAndAnchor(t *harnessTest, net *lntest.NetworkHarness, - sweepTxns []*wire.MsgTx, closeTx string) (*sweptOutput, *sweptOutput) { - - var commitSweep, anchorSweep *sweptOutput - - for _, tx := range sweepTxns { - txHash := tx.TxHash() - sweepTx, err := net.Miner.Client.GetRawTransaction(&txHash) - require.NoError(t.t, err) - - // We expect our commitment sweep to have a single input, and, - // our anchor sweep to have more inputs (because the wallet - // needs to add balance to the anchor amount). We find their - // sweep txids here to setup appropriate resolutions. We also - // need to find the outpoint for our resolution, which we do by - // matching the inputs to the sweep to the close transaction. - inputs := sweepTx.MsgTx().TxIn - if len(inputs) == 1 { - commitSweep = &sweptOutput{ - OutPoint: inputs[0].PreviousOutPoint, - SweepTx: tx, - } - } else { - // Since we have more than one input, we run through - // them to find the outpoint that spends from the close - // tx. This will be our anchor output. - for _, txin := range inputs { - outpointStr := txin.PreviousOutPoint.Hash.String() - if outpointStr == closeTx { - anchorSweep = &sweptOutput{ - OutPoint: txin.PreviousOutPoint, - SweepTx: tx, - } - } - } - } - } - - return commitSweep, anchorSweep -} - // testFailingChannel tests that we will fail the channel by force closing it // in the case where a counterparty tries to settle an HTLC with the wrong // preimage. diff --git a/lntest/itest/lnd_onchain_test.go b/lntest/itest/lnd_onchain_test.go index 4444caa70..a2d024aa6 100644 --- a/lntest/itest/lnd_onchain_test.go +++ b/lntest/itest/lnd_onchain_test.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "time" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -14,6 +13,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet" @@ -396,11 +396,191 @@ func testAnchorReservedValue(ht *lntemp.HarnessTest) { assertNumTxInAndTxOut(sweepTx, 4, 1) } +// testAnchorThirdPartySpend tests that if we force close a channel, but then +// don't sweep the anchor in time and a 3rd party spends it, that we remove any +// transactions that are a descendent of that sweep. +func testAnchorThirdPartySpend(ht *lntemp.HarnessTest) { + // First, we'll create two new nodes that both default to anchor + // channels. + // + // NOTE: The itests differ here as anchors is default off vs the normal + // lnd binary. + args := nodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + alice := ht.NewNode("Alice", args) + defer ht.Shutdown(alice) + + bob := ht.NewNode("Bob", args) + defer ht.Shutdown(bob) + + ht.EnsureConnected(alice, bob) + + // We'll fund our Alice with coins, as she'll be opening the channel. + // We'll fund her with *just* enough coins to open the channel and + // sweep the anchor. + const ( + firstChanSize = 1_000_000 + anchorFeeBuffer = 500_000 + ) + ht.FundCoins(firstChanSize+anchorFeeBuffer, alice) + + // Open the channel between the two nodes and wait for it to confirm + // fully. + aliceChanPoint1 := ht.OpenChannel( + alice, bob, lntemp.OpenChannelParams{ + Amt: firstChanSize, + }, + ) + + // Send another UTXO if this is a neutrino backend. When sweeping + // anchors, there are two transactions created, `local_sweep_tx` for + // sweeping Alice's anchor on the local commitment, `remote_sweep_tx` + // for sweeping her anchor on the remote commitment. Whenever the force + // close transaction is published, Alice will always create these two + // transactions to sweep her anchor. + // On the other hand, when creating the sweep txes, the anchor itself + // is not able to cover the fee, so another wallet UTXO is needed. In + // our test case, there's a change output that can be used from the + // above funding process. And it's used by both sweep txes - when `lnd` + // happens to create the `remote_sweep_tx` first, it will receive an + // error since its parent tx, the remote commitment, is not known, + // hence freeing the change output to be used by `local_sweep_tx`. + // For neutrino client, however, it will consider the transaction which + // sweeps the remote anchor as an orphan tx, and it will neither send + // it to the mempool nor return an error to free the change output. + // Thus, if the change output is already used in `remote_sweep_tx`, we + // won't have UTXO to create `local_sweep_tx`. + // + // NOTE: the order of the sweep requests for the two anchors cannot be + // guaranteed. If the sweeper happens to sweep the remote anchor first, + // then the test won't pass without the extra UTXO, which is the source + // of the flakeness. + // + // TODO(yy): make a RPC server for sweeper so we can explicitly check + // and control its state. + if ht.IsNeutrinoBackend() { + ht.FundCoins(anchorFeeBuffer, alice) + } + + // With the channel open, we'll actually immediately force close it. We + // don't care about network announcements here since there's no routing + // in this test. + ht.CloseChannelAssertPending(alice, aliceChanPoint1, true) + + // Now that the channel has been force closed, it should show up in the + // PendingChannels RPC under the waiting close section. + waitingClose := ht.AssertChannelWaitingClose(alice, aliceChanPoint1) + + // At this point, the channel is waiting close, and we have both the + // commitment transaction and anchor sweep in the mempool. + const expectedTxns = 2 + sweepTxns := ht.Miner.GetNumTxsFromMempool(expectedTxns) + aliceCloseTx := waitingClose.Commitments.LocalTxid + _, aliceAnchor := ht.FindCommitAndAnchor(sweepTxns, aliceCloseTx) + + // We'll now mine _only_ the commitment force close transaction, as we + // want the anchor sweep to stay unconfirmed. + forceCloseTxID, _ := chainhash.NewHashFromStr(aliceCloseTx) + commitTxn := ht.Miner.GetRawTransaction(forceCloseTxID) + ht.Miner.MineBlockWithTxes([]*btcutil.Tx{commitTxn}) + + // With the anchor output located, and the main commitment mined we'll + // instruct the wallet to send all coins in the wallet to a new address + // (to the miner), including unconfirmed change. + minerAddr := ht.Miner.NewMinerAddress() + sweepReq := &lnrpc.SendCoinsRequest{ + Addr: minerAddr.String(), + SendAll: true, + MinConfs: 0, + SpendUnconfirmed: true, + } + sweepAllResp := alice.RPC.SendCoins(sweepReq) + + // Both the original anchor sweep transaction, as well as the + // transaction we created to sweep all the coins from Alice's wallet + // should be found in her transaction store. + sweepAllTxID, _ := chainhash.NewHashFromStr(sweepAllResp.Txid) + ht.AssertTransactionInWallet(alice, aliceAnchor.SweepTx.TxHash()) + ht.AssertTransactionInWallet(alice, *sweepAllTxID) + + // Next, we'll shutdown Alice, and allow 16 blocks to pass so that the + // anchor output can be swept by anyone. Rather than use the normal API + // call, we'll generate a series of _empty_ blocks here. + aliceRestart := ht.SuspendNode(alice) + const anchorCsv = 16 + ht.MineEmptyBlocks(anchorCsv) + + // Before we sweep the anchor, we'll restart Alice. + require.NoErrorf(ht, aliceRestart(), "unable to restart alice") + + // Now that the channel has been closed, and Alice has an unconfirmed + // transaction spending the output produced by her anchor sweep, we'll + // mine a transaction that double spends the output. + thirdPartyAnchorSweep := genAnchorSweep(ht, aliceAnchor, anchorCsv) + ht.Miner.MineBlockWithTxes([]*btcutil.Tx{thirdPartyAnchorSweep}) + + // At this point, we should no longer find Alice's transaction that + // tried to sweep the anchor in her wallet. + ht.AssertTransactionNotInWallet(alice, aliceAnchor.SweepTx.TxHash()) + + // In addition, the transaction she sent to sweep all her coins to the + // miner also should no longer be found. + ht.AssertTransactionNotInWallet(alice, *sweepAllTxID) + + // The anchor should now show as being "lost", while the force close + // response is still present. + assertAnchorOutputLost(ht, alice, aliceChanPoint1) + + // At this point Alice's CSV output should already be fully spent and + // the channel marked as being resolved. We mine a block first, as so + // far we've been generating custom blocks this whole time. + commitSweepOp := wire.OutPoint{ + Hash: *forceCloseTxID, + Index: 1, + } + ht.Miner.AssertOutpointInMempool(commitSweepOp) + ht.MineBlocks(1) + + ht.AssertNumWaitingClose(alice, 0) +} + +// assertAnchorOutputLost asserts that the anchor output for the given channel +// has the state of being lost. +func assertAnchorOutputLost(ht *lntemp.HarnessTest, hn *node.HarnessNode, + chanPoint *lnrpc.ChannelPoint) { + + cp := ht.OutPointFromChannelPoint(chanPoint) + + expected := lnrpc.PendingChannelsResponse_ForceClosedChannel_LOST + + err := wait.NoError(func() error { + resp := hn.RPC.PendingChannels() + channels := resp.PendingForceClosingChannels + + for _, c := range channels { + // Not the wanted channel, skipped. + if c.Channel.ChannelPoint != cp.String() { + continue + } + + // Found the channel, check the anchor state. + if c.Anchor == expected { + return nil + } + + return fmt.Errorf("unexpected anchor state, want %v, "+ + "got %v", expected, c.Anchor) + } + + return fmt.Errorf("channel not found using cp=%v", cp) + }, defaultTimeout) + require.NoError(ht, err, "anchor doesn't show as being lost") +} + // genAnchorSweep generates a "3rd party" anchor sweeping from an existing one. // In practice, we just re-use the existing witness, and track on our own // output producing a 1-in-1-out transaction. -func genAnchorSweep(t *harnessTest, net *lntest.NetworkHarness, - aliceAnchor *sweptOutput, anchorCsv uint32) *btcutil.Tx { +func genAnchorSweep(ht *lntemp.HarnessTest, + aliceAnchor *lntemp.SweptOutput, anchorCsv uint32) *btcutil.Tx { // At this point, we have the transaction that Alice used to try to // sweep her anchor. As this is actually just something anyone can @@ -414,7 +594,8 @@ func genAnchorSweep(t *harnessTest, net *lntest.NetworkHarness, } } - t.Fatalf("anchor op not found") + require.FailNow(ht, "anchor op not found") + return wire.TxIn{} }() @@ -423,14 +604,9 @@ func genAnchorSweep(t *harnessTest, net *lntest.NetworkHarness, aliceAnchorTxIn.Witness[0] = nil aliceAnchorTxIn.Sequence = anchorCsv - minerAddr, err := net.Miner.NewAddress() - if err != nil { - t.Fatalf("unable to get miner addr: %v", err) - } + minerAddr := ht.Miner.NewMinerAddress() addrScript, err := txscript.PayToAddrScript(minerAddr) - if err != nil { - t.Fatalf("unable to gen addr script: %v", err) - } + require.NoError(ht, err, "unable to gen addr script") // Now that we have the txIn, we can just make a new transaction that // uses a different script for the output. @@ -443,191 +619,3 @@ func genAnchorSweep(t *harnessTest, net *lntest.NetworkHarness, return btcutil.NewTx(tx) } - -// testAnchorThirdPartySpend tests that if we force close a channel, but then -// don't sweep the anchor in time and a 3rd party spends it, that we remove any -// transactions that are a descendent of that sweep. -func testAnchorThirdPartySpend(net *lntest.NetworkHarness, t *harnessTest) { - // First, we'll create two new nodes that both default to anchor - // channels. - // - // NOTE: The itests differ here as anchors is default off vs the normal - // lnd binary. - args := nodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) - alice := net.NewNode(t.t, "Alice", args) - defer shutdownAndAssert(net, t, alice) - - bob := net.NewNode(t.t, "Bob", args) - defer shutdownAndAssert(net, t, bob) - - ctxb := context.Background() - net.ConnectNodes(t.t, alice, bob) - - // We'll fund our Alice with coins, as she'll be opening the channel. - // We'll fund her with *just* enough coins to open the channel. - const ( - firstChanSize = 1_000_000 - anchorFeeBuffer = 500_000 - ) - net.SendCoins(t.t, firstChanSize, alice) - - // We'll give Alice another spare UTXO as well so she can use it to - // help sweep all coins. - net.SendCoins(t.t, anchorFeeBuffer, alice) - - // Open the channel between the two nodes and wait for it to confirm - // fully. - aliceChanPoint1 := openChannelAndAssert( - t, net, alice, bob, lntest.OpenChannelParams{ - Amt: firstChanSize, - }, - ) - - // With the channel open, we'll actually immediately force close it. We - // don't care about network announcements here since there's no routing - // in this test. - _, _, err := net.CloseChannel(alice, aliceChanPoint1, true) - if err != nil { - t.Fatalf("unable to execute force channel closure: %v", err) - } - - // Now that the channel has been force closed, it should show up in the - // PendingChannels RPC under the waiting close section. - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := alice.PendingChannels(ctxt, pendingChansRequest) - if err != nil { - t.Fatalf("unable to query for pending channels: %v", err) - } - err = checkNumWaitingCloseChannels(pendingChanResp, 1) - if err != nil { - t.Fatalf(err.Error()) - } - - // Get the normal channel outpoint so we can track it in the set of - // channels that are waiting to be closed. - fundingTxID, err := lnrpc.GetChanPointFundingTxid(aliceChanPoint1) - if err != nil { - t.Fatalf("unable to get txid: %v", err) - } - chanPoint := wire.OutPoint{ - Hash: *fundingTxID, - Index: aliceChanPoint1.OutputIndex, - } - waitingClose, err := findWaitingCloseChannel(pendingChanResp, &chanPoint) - if err != nil { - t.Fatalf(err.Error()) - } - - // At this point, the channel is waiting close, and we have both the - // commitment transaction and anchor sweep in the mempool. - const expectedTxns = 2 - sweepTxns, err := getNTxsFromMempool( - net.Miner.Client, expectedTxns, minerMempoolTimeout, - ) - require.NoError(t.t, err, "no sweep txns in miner mempool") - aliceCloseTx := waitingClose.Commitments.LocalTxid - _, aliceAnchor := findCommitAndAnchor(t, net, sweepTxns, aliceCloseTx) - - // We'll now mine _only_ the commitment force close transaction, as we - // want the anchor sweep to stay unconfirmed. - var emptyTime time.Time - forceCloseTxID, _ := chainhash.NewHashFromStr(aliceCloseTx) - commitTxn, err := net.Miner.Client.GetRawTransaction( - forceCloseTxID, - ) - if err != nil { - t.Fatalf("unable to get transaction: %v", err) - } - _, err = net.Miner.GenerateAndSubmitBlock( - []*btcutil.Tx{commitTxn}, -1, emptyTime, - ) - if err != nil { - t.Fatalf("unable to generate block: %v", err) - } - - // With the anchor output located, and the main commitment mined we'll - // instruct the wallet to send all coins in the wallet to a new address - // (to the miner), including unconfirmed change. - minerAddr, err := net.Miner.NewAddress() - if err != nil { - t.Fatalf("unable to create new miner addr: %v", err) - } - sweepReq := &lnrpc.SendCoinsRequest{ - Addr: minerAddr.String(), - SendAll: true, - MinConfs: 0, - SpendUnconfirmed: true, - } - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - sweepAllResp, err := alice.SendCoins(ctxt, sweepReq) - if err != nil { - t.Fatalf("unable to sweep coins: %v", err) - } - - // Both the original anchor sweep transaction, as well as the - // transaction we created to sweep all the coins from Alice's wallet - // should be found in her transaction store. - sweepAllTxID, _ := chainhash.NewHashFromStr(sweepAllResp.Txid) - assertTransactionInWallet(t.t, alice, aliceAnchor.SweepTx.TxHash()) - assertTransactionInWallet(t.t, alice, *sweepAllTxID) - - // Next, we'll shutdown Alice, and allow 16 blocks to pass so that the - // anchor output can be swept by anyone. Rather than use the normal API - // call, we'll generate a series of _empty_ blocks here. - aliceRestart, err := net.SuspendNode(alice) - if err != nil { - t.Fatalf("unable to shutdown alice: %v", err) - } - const anchorCsv = 16 - for i := 0; i < anchorCsv; i++ { - _, err := net.Miner.GenerateAndSubmitBlock(nil, -1, emptyTime) - if err != nil { - t.Fatalf("unable to generate block: %v", err) - } - } - - // Before we sweep the anchor, we'll restart Alice. - if err := aliceRestart(); err != nil { - t.Fatalf("unable to restart alice: %v", err) - } - - // Now that the channel has been closed, and Alice has an unconfirmed - // transaction spending the output produced by her anchor sweep, we'll - // mine a transaction that double spends the output. - thirdPartyAnchorSweep := genAnchorSweep(t, net, aliceAnchor, anchorCsv) - _, err = net.Miner.GenerateAndSubmitBlock( - []*btcutil.Tx{thirdPartyAnchorSweep}, -1, emptyTime, - ) - if err != nil { - t.Fatalf("unable to generate block: %v", err) - } - - // At this point, we should no longer find Alice's transaction that - // tried to sweep the anchor in her wallet. - assertTransactionNotInWallet(t.t, alice, aliceAnchor.SweepTx.TxHash()) - - // In addition, the transaction she sent to sweep all her coins to the - // miner also should no longer be found. - assertTransactionNotInWallet(t.t, alice, *sweepAllTxID) - - // The anchor should now show as being "lost", while the force close - // response is still present. - assertAnchorOutputLost(t, alice, chanPoint) - - // At this point Alice's CSV output should already be fully spent and - // the channel marked as being resolved. We mine a block first, as so - // far we've been generating custom blocks this whole time.. - commitSweepOp := wire.OutPoint{ - Hash: *forceCloseTxID, - Index: 1, - } - assertSpendingTxInMempool( - t, net.Miner.Client, minerMempoolTimeout, commitSweepOp, - ) - _, err = net.Miner.Client.Generate(1) - if err != nil { - t.Fatalf("unable to generate block: %v", err) - } - assertNumPendingChannels(t, alice, 0, 0) -} diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go index c2dde1c9c..589b576d9 100644 --- a/lntest/itest/lnd_test_list_on_test.go +++ b/lntest/itest/lnd_test_list_on_test.go @@ -194,10 +194,6 @@ var allTestCases = []*testCase{ name: "remote signer", test: testRemoteSigner, }, - { - name: "3rd party anchor spend", - test: testAnchorThirdPartySpend, - }, { name: "taproot", test: testTaproot,