diff --git a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go index 37634a28..16ac3987 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_chain_claim_test.go @@ -96,11 +96,18 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest, // At this point, Bob decides that he wants to exit the channel // immediately, so he force closes his commitment transaction. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - bobForceClose := closeChannelAndAssert(ctxt, t, net, bob, - aliceChanPoint, true) + bobForceClose := closeChannelAndAssertType(ctxt, t, net, bob, + aliceChanPoint, c == commitTypeAnchors, true) - // Alice will sweep her output immediately. - _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + // Alice will sweep her commitment output immediately. If there are + // anchors, Alice will also sweep hers. + expectedTxes := 1 + if c == commitTypeAnchors { + expectedTxes = 2 + } + _, err = waitForNTxsInMempool( + net.Miner.Node, expectedTxes, minerMempoolTimeout, + ) if err != nil { t.Fatalf("unable to find alice's sweep tx in miner mempool: %v", err) @@ -135,8 +142,11 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest, t.Fatalf("unable to generate blocks") } - // Carol's commitment transaction should now be in the mempool. - txids, err := waitForNTxsInMempool(net.Miner.Node, 1, minerMempoolTimeout) + // Carol's commitment transaction should now be in the mempool. If there + // is an anchor, Carol will sweep that too. + _, err = waitForNTxsInMempool( + net.Miner.Node, expectedTxes, minerMempoolTimeout, + ) if err != nil { t.Fatalf("transactions not found in mempool: %v", err) } @@ -149,53 +159,47 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest, Index: bobChanPoint.OutputIndex, } - // The tx should be spending from the funding transaction, - commitHash := txids[0] - tx1, err := net.Miner.Node.GetRawTransaction(commitHash) - if err != nil { - t.Fatalf("unable to get txn: %v", err) - } - if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint { - t.Fatalf("commit transaction not spending fundingtx: %v", - spew.Sdump(tx1)) - } + // Look up the closing transaction. It should be spending from the + // funding transaction, + closingTx := getSpendingTxInMempool( + t, net.Miner.Node, minerMempoolTimeout, carolFundingPoint, + ) + closingTxid := closingTx.TxHash() - // Mine a block that should confirm the commit tx. - block := mineBlocks(t, net, 1, 1)[0] - if len(block.Transactions) != 2 { - t.Fatalf("expected 2 transactions in block, got %v", - len(block.Transactions)) + // Mine a block that should confirm the commit tx, the anchor if present + // and the coinbase. + block := mineBlocks(t, net, 1, expectedTxes)[0] + if len(block.Transactions) != expectedTxes+1 { + t.Fatalf("expected %v transactions in block, got %v", + expectedTxes+1, len(block.Transactions)) } - assertTxInBlock(t, block, commitHash) + assertTxInBlock(t, block, &closingTxid) // Restart bob again. if err := restartBob(); err != nil { t.Fatalf("unable to restart bob: %v", err) } - // After the force close transacion is mined, Carol should broadcast - // her second level HTLC transacion. Bob will broadcast a sweep tx to - // sweep his output in the channel with Carol. He can do this - // immediately, as the output is not timelocked since Carol was the one - // force closing. - commitSpends, err := waitForNTxsInMempool(net.Miner.Node, 2, - minerMempoolTimeout) + // After the force close transacion is mined, Carol should broadcast her + // second level HTLC transacion. Bob will broadcast a sweep tx to sweep + // his output in the channel with Carol. He can do this immediately, as + // the output is not timelocked since Carol was the one force closing. + // If there are anchors on the commitment, Bob will also sweep his + // anchor. + expectedTxes = 2 + if c == commitTypeAnchors { + expectedTxes = 3 + } + txes, err := getNTxsFromMempool( + net.Miner.Node, expectedTxes, minerMempoolTimeout, + ) if err != nil { t.Fatalf("transactions not found in mempool: %v", err) } // Both Carol's second level transaction and Bob's sweep should be // spending from the commitment transaction. - for _, txid := range commitSpends { - tx, err := net.Miner.Node.GetRawTransaction(txid) - if err != nil { - t.Fatalf("unable to get txn: %v", err) - } - - if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash { - t.Fatalf("tx did not spend from commitment tx") - } - } + assertAllTxesSpendFrom(t, txes, closingTxid) // At this point we suspend Alice to make sure she'll handle the // on-chain settle after a restart. @@ -205,14 +209,11 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest, } // Mine a block to confirm the two transactions (+ the coinbase). - block = mineBlocks(t, net, 1, 2)[0] - if len(block.Transactions) != 3 { + block = mineBlocks(t, net, 1, expectedTxes)[0] + if len(block.Transactions) != expectedTxes+1 { t.Fatalf("expected 3 transactions in block, got %v", len(block.Transactions)) } - for _, txid := range commitSpends { - assertTxInBlock(t, block, txid) - } // Keep track of the second level tx maturity. carolSecondLevelCSV := uint32(defaultCSV) diff --git a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go index 98b66bda..1a7feed4 100644 --- a/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_local_timeout_test.go @@ -7,6 +7,7 @@ import ( "fmt" "time" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/davecgh/go-spew/spew" @@ -106,24 +107,33 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest, t.Fatalf("unable to generate blocks: %v", err) } - // Bob's force close transaction should now be found in the mempool. + // Bob's force close transaction should now be found in the mempool. If + // there are anchors, we also expect Bob's anchor sweep. + expectedTxes := 1 + if c == commitTypeAnchors { + expectedTxes = 2 + } + bobFundingTxid, err := lnd.GetChanPointFundingTxid(bobChanPoint) if err != nil { t.Fatalf("unable to get txid: %v", err) } - closeTxid, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + _, err = waitForNTxsInMempool( + net.Miner.Node, expectedTxes, minerMempoolTimeout, + ) if err != nil { t.Fatalf("unable to find closing txid: %v", err) } - assertSpendingTxInMempool( + closeTx := getSpendingTxInMempool( t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{ Hash: *bobFundingTxid, Index: bobChanPoint.OutputIndex, }, ) + closeTxid := closeTx.TxHash() // Mine a block to confirm the closing transaction. - mineBlocks(t, net, 1, 1) + mineBlocks(t, net, 1, expectedTxes) // At this point, Bob should have canceled backwards the dust HTLC // that we sent earlier. This means Alice should now only have a single @@ -143,20 +153,42 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest, // With the closing transaction confirmed, we should expect Bob's HTLC // timeout transaction to be broadcast due to the expiry being reached. - htlcTimeout, err := waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + // If there are anchors, we also expect Carol's anchor sweep now. + txes, err := getNTxsFromMempool(net.Miner.Node, expectedTxes, minerMempoolTimeout) if err != nil { t.Fatalf("unable to find bob's htlc timeout tx: %v", err) } + // Lookup the timeout transaction that is expected to spend from the + // closing tx. We distinguish it from a possibly anchor sweep by value. + var htlcTimeout *chainhash.Hash + for _, tx := range txes { + prevOp := tx.TxIn[0].PreviousOutPoint + if prevOp.Hash != closeTxid { + t.Fatalf("tx not spending from closing tx") + } + + // Assume that the timeout tx doesn't spend an output of exactly + // the size of the anchor. + if closeTx.TxOut[prevOp.Index].Value != anchorSize { + hash := tx.TxHash() + htlcTimeout = &hash + } + } + if htlcTimeout == nil { + t.Fatalf("htlc timeout tx not found in mempool") + } + // We'll mine the remaining blocks in order to generate the sweep // transaction of Bob's commitment output. - mineBlocks(t, net, defaultCSV, 1) - assertSpendingTxInMempool( - t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{ - Hash: *closeTxid, - Index: 1, - }, - ) + mineBlocks(t, net, defaultCSV, expectedTxes) + + // Check that the sweep spends from the mined commitment. + txes, err = getNTxsFromMempool(net.Miner.Node, 1, minerMempoolTimeout) + if err != nil { + t.Fatalf("sweep not found: %v", err) + } + assertAllTxesSpendFrom(t, txes, closeTxid) // Bob's pending channel report should show that he has a commitment // output awaiting sweeping, and also that there's an outgoing HTLC @@ -248,6 +280,10 @@ func testMultiHopHtlcLocalTimeout(net *lntest.NetworkHarness, t *harnessTest, t.Fatalf(predErr.Error()) } + // Coop close channel, expect no anchors. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, alice, aliceChanPoint, false) + closeChannelAndAssertType( + ctxt, t, net, alice, aliceChanPoint, false, + false, + ) } diff --git a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go index e11b2ed7..d7335311 100644 --- a/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_receiver_chain_claim_test.go @@ -7,7 +7,6 @@ import ( "fmt" "time" - "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd" @@ -126,8 +125,15 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest, } // At this point, Carol should broadcast her active commitment - // transaction in order to go to the chain and sweep her HTLC. - txids, err := waitForNTxsInMempool(net.Miner.Node, 1, minerMempoolTimeout) + // transaction in order to go to the chain and sweep her HTLC. If there + // are anchors, Carol also sweeps hers. + expectedTxes := 1 + if c == commitTypeAnchors { + expectedTxes = 2 + } + txes, err := getNTxsFromMempool( + net.Miner.Node, expectedTxes, minerMempoolTimeout, + ) if err != nil { t.Fatalf("expected transaction not found in mempool: %v", err) } @@ -144,20 +150,13 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest, // The commitment transaction should be spending from the funding // transaction. - commitHash := txids[0] - tx, err := net.Miner.Node.GetRawTransaction(commitHash) - if err != nil { - t.Fatalf("unable to get txn: %v", err) - } - commitTx := tx.MsgTx() - - if commitTx.TxIn[0].PreviousOutPoint != carolFundingPoint { - t.Fatalf("commit transaction not spending from expected "+ - "outpoint: %v", spew.Sdump(commitTx)) - } + closingTx := getSpendingTxInMempool( + t, net.Miner.Node, minerMempoolTimeout, carolFundingPoint, + ) + closingTxid := closingTx.TxHash() // Confirm the commitment. - mineBlocks(t, net, 1, 1) + mineBlocks(t, net, 1, expectedTxes) // Restart bob again. if err := restartBob(); err != nil { @@ -167,30 +166,21 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest, // After the force close transaction is mined, Carol should broadcast // her second level HTLC transaction. Bob will broadcast a sweep tx to // sweep his output in the channel with Carol. When Bob notices Carol's - // second level transaction in the mempool, he will extract the - // preimage and settle the HTLC back off-chain. - secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 2, - minerMempoolTimeout) + // second level transaction in the mempool, he will extract the preimage + // and settle the HTLC back off-chain. Bob will also sweep his anchor, + // if present. + expectedTxes = 2 + if c == commitTypeAnchors { + expectedTxes = 3 + } + txes, err = getNTxsFromMempool(net.Miner.Node, + expectedTxes, minerMempoolTimeout) if err != nil { t.Fatalf("transactions not found in mempool: %v", err) } - // Carol's second level transaction should be spending from - // the commitment transaction. - var secondLevelHash *chainhash.Hash - for _, txid := range secondLevelHashes { - tx, err := net.Miner.Node.GetRawTransaction(txid) - if err != nil { - t.Fatalf("unable to get txn: %v", err) - } - - if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash == *commitHash { - secondLevelHash = txid - } - } - if secondLevelHash == nil { - t.Fatalf("Carol's second level tx not found") - } + // All transactions should be spending from the commitment transaction. + assertAllTxesSpendFrom(t, txes, closingTxid) // We'll now mine an additional block which should confirm both the // second layer transactions. @@ -314,5 +304,8 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest, // We'll close out the channel between Alice and Bob, then shutdown // carol to conclude the test. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, alice, aliceChanPoint, false) + closeChannelAndAssertType( + ctxt, t, net, alice, aliceChanPoint, + false, false, + ) } diff --git a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go index 257b7b97..c73a066b 100644 --- a/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go +++ b/lntest/itest/lnd_multi-hop_htlc_remote_chain_claim_test.go @@ -97,8 +97,8 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest // immediately force close the channel by broadcast her commitment // transaction. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - aliceForceClose := closeChannelAndAssert(ctxt, t, net, alice, - aliceChanPoint, true) + aliceForceClose := closeChannelAndAssertType(ctxt, t, net, alice, + aliceChanPoint, c == commitTypeAnchors, true) // Wait for the channel to be marked pending force close. ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) @@ -114,8 +114,13 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest t.Fatalf("unable to generate blocks: %v", err) } - // Alice should now sweep her funds. - _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + // Alice should now sweep her funds. If there are anchors, Alice should + // also sweep hers. + expectedTxes := 1 + if c == commitTypeAnchors { + expectedTxes = 2 + } + _, err = waitForNTxsInMempool(net.Miner.Node, expectedTxes, minerMempoolTimeout) if err != nil { t.Fatalf("unable to find sweeping tx in mempool: %v", err) } @@ -149,10 +154,13 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest t.Fatalf("unable to generate blocks") } - // Carol's commitment transaction should now be in the mempool. - txids, err := waitForNTxsInMempool(net.Miner.Node, 1, minerMempoolTimeout) + // Carol's commitment transaction should now be in the mempool. If there + // are anchors, Carol also sweeps her anchor. + _, err = waitForNTxsInMempool( + net.Miner.Node, expectedTxes, minerMempoolTimeout, + ) if err != nil { - t.Fatalf("transactions not found in mempool: %v", err) + t.Fatalf("unable to find carol's txes: %v", err) } bobFundingTxid, err := lnd.GetChanPointFundingTxid(bobChanPoint) if err != nil { @@ -163,63 +171,51 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest Index: bobChanPoint.OutputIndex, } - // The transaction should be spending from the funding transaction - commitHash := txids[0] - tx1, err := net.Miner.Node.GetRawTransaction(commitHash) - if err != nil { - t.Fatalf("unable to get txn: %v", err) - } - if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint { - t.Fatalf("commit transaction not spending fundingtx: %v", - spew.Sdump(tx1)) - } + // The closing transaction should be spending from the funding + // transaction. + closingTx := getSpendingTxInMempool( + t, net.Miner.Node, minerMempoolTimeout, carolFundingPoint, + ) + closingTxid := closingTx.TxHash() - // Mine a block, which should contain the commitment. - block := mineBlocks(t, net, 1, 1)[0] - if len(block.Transactions) != 2 { - t.Fatalf("expected 2 transactions in block, got %v", - len(block.Transactions)) + // Mine a block, which should contain: the commitment, possibly an + // anchor sweep and the coinbase tx. + block := mineBlocks(t, net, 1, expectedTxes)[0] + if len(block.Transactions) != expectedTxes+1 { + t.Fatalf("expected %v transactions in block, got %v", + expectedTxes, len(block.Transactions)) } - assertTxInBlock(t, block, commitHash) + assertTxInBlock(t, block, &closingTxid) // Restart bob again. if err := restartBob(); err != nil { t.Fatalf("unable to restart bob: %v", err) } - // After the force close transacion is mined, Carol should broadcast - // her second level HTLC transacion. Bob will broadcast a sweep tx to - // sweep his output in the channel with Carol. He can do this - // immediately, as the output is not timelocked since Carol was the one - // force closing. - commitSpends, err := waitForNTxsInMempool(net.Miner.Node, 2, + // After the force close transacion is mined, Carol should broadcast her + // second level HTLC transacion. Bob will broadcast a sweep tx to sweep + // his output in the channel with Carol. He can do this immediately, as + // the output is not timelocked since Carol was the one force closing. + // If there are anchors, Bob should also sweep his. + expectedTxes = 2 + if c == commitTypeAnchors { + expectedTxes = 3 + } + txes, err := getNTxsFromMempool(net.Miner.Node, expectedTxes, minerMempoolTimeout) if err != nil { t.Fatalf("transactions not found in mempool: %v", err) } - // Both Carol's second level transaction and Bob's sweep should be - // spending from the commitment transaction. - for _, txid := range commitSpends { - tx, err := net.Miner.Node.GetRawTransaction(txid) - if err != nil { - t.Fatalf("unable to get txn: %v", err) - } - - if tx.MsgTx().TxIn[0].PreviousOutPoint.Hash != *commitHash { - t.Fatalf("tx did not spend from commitment tx") - } - } + // All transactions should be pending from the commitment transaction. + assertAllTxesSpendFrom(t, txes, closingTxid) // Mine a block to confirm the two transactions (+ coinbase). - block = mineBlocks(t, net, 1, 2)[0] - if len(block.Transactions) != 3 { + block = mineBlocks(t, net, 1, expectedTxes)[0] + if len(block.Transactions) != expectedTxes+1 { t.Fatalf("expected 3 transactions in block, got %v", len(block.Transactions)) } - for _, txid := range commitSpends { - assertTxInBlock(t, block, txid) - } // Keep track of the second level tx maturity. carolSecondLevelCSV := uint32(defaultCSV) diff --git a/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go index b60cfda4..d51d2c3d 100644 --- a/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_local_force_close_on_chain_htlc_timeout_test.go @@ -82,7 +82,9 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // force close the Bob -> Carol channel. This should trigger contract // resolution mode for both of them. ctxt, _ := context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, bob, bobChanPoint, true) + closeChannelAndAssertType( + ctxt, t, net, bob, bobChanPoint, c == commitTypeAnchors, true, + ) // At this point, Bob should have a pending force close channel as he // just went to chain. @@ -116,8 +118,16 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, t.Fatalf(predErr.Error()) } - // We'll mine defaultCSV blocks in order to generate the sweep transaction - // of Bob's funding output. + // We'll mine defaultCSV blocks in order to generate the sweep + // transaction of Bob's funding output. If there are anchors, mine + // Carol's anchor sweep too. + if c == commitTypeAnchors { + _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + if err != nil { + t.Fatalf("unable to find carol's anchor sweep tx: %v", err) + } + } + if _, err := net.Miner.Node.Generate(defaultCSV); err != nil { t.Fatalf("unable to generate blocks: %v", err) } @@ -275,6 +285,9 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, t.Fatalf(predErr.Error()) } + // Coop close, no anchors. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, alice, aliceChanPoint, false) + closeChannelAndAssertType( + ctxt, t, net, alice, aliceChanPoint, false, false, + ) } diff --git a/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go index 2f70fe3c..ebff9187 100644 --- a/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go +++ b/lntest/itest/lnd_multi-hop_remote_force_close_on_chain_htlc_timeout_test.go @@ -81,9 +81,13 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // At this point, we'll now instruct Carol to force close the // transaction. This will let us exercise that Bob is able to sweep the - // expired HTLC on Carol's version of the commitment transaction. + // expired HTLC on Carol's version of the commitment transaction. If + // Carol has an anchor, it will be swept too. ctxt, _ := context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, carol, bobChanPoint, true) + closeChannelAndAssertType( + ctxt, t, net, carol, bobChanPoint, c == commitTypeAnchors, + true, + ) // At this point, Bob should have a pending force close channel as // Carol has gone directly to chain. @@ -110,11 +114,18 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, t.Fatalf(predErr.Error()) } - // Bob can sweep his output immediately. - _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) + // Bob can sweep his output immediately. If there is an anchor, Bob will + // sweep that as well. + expectedTxes := 1 + if c == commitTypeAnchors { + expectedTxes = 2 + } + + _, err = waitForNTxsInMempool( + net.Miner.Node, expectedTxes, minerMempoolTimeout, + ) if err != nil { - t.Fatalf("unable to find bob's funding output sweep tx: %v", - err) + t.Fatalf("failed to find txes in miner mempool: %v", err) } // Next, we'll mine enough blocks for the HTLC to expire. At this @@ -232,7 +243,10 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness, // We'll close out the test by closing the channel from Alice to Bob, // and then shutting down the new node we created as its no longer - // needed. + // needed. Coop close, no anchors. ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) - closeChannelAndAssert(ctxt, t, net, alice, aliceChanPoint, false) + closeChannelAndAssertType( + ctxt, t, net, alice, aliceChanPoint, false, + false, + ) } diff --git a/lntest/itest/lnd_multi-hop_test.go b/lntest/itest/lnd_multi-hop_test.go index 2d4a9e5f..ec73e187 100644 --- a/lntest/itest/lnd_multi-hop_test.go +++ b/lntest/itest/lnd_multi-hop_test.go @@ -7,6 +7,8 @@ import ( "fmt" "testing" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" @@ -65,6 +67,7 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) { commitTypes := []commitType{ commitTypeLegacy, + commitTypeAnchors, } for _, commitType := range commitTypes { @@ -204,16 +207,19 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, t.Fatalf("unable to connect peers: %v", err) } - ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout) - err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, alice) - if err != nil { - t.Fatalf("unable to send coins to Alice: %v", err) - } + // Make sure there are enough utxos for anchoring. + for i := 0; i < 2; i++ { + ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout) + err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, alice) + if err != nil { + t.Fatalf("unable to send coins to Alice: %v", err) + } - ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout) - err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, bob) - if err != nil { - t.Fatalf("unable to send coins to Bob: %v", err) + ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout) + err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, bob) + if err != nil { + t.Fatalf("unable to send coins to Bob: %v", err) + } } // We'll start the test by creating a channel between Alice and Bob, @@ -255,6 +261,18 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, t.Fatalf("unable to connect bob to carol: %v", err) } + // Make sure Carol has enough utxos for anchoring. Because the anchor by + // itself often doesn't meet the dust limit, a utxo from the wallet + // needs to be attached as an additional input. This can still lead to a + // positively-yielding transaction. + for i := 0; i < 2; i++ { + ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout) + err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, carol) + if err != nil { + t.Fatalf("unable to send coins to Alice: %v", err) + } + } + // We'll then create a channel from Bob to Carol. After this channel is // open, our topology looks like: A -> B -> C. ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout) @@ -282,3 +300,16 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness, return aliceChanPoint, bobChanPoint, carol } + +// assertAllTxesSpendFrom asserts that all txes in the list spend from the given +// tx. +func assertAllTxesSpendFrom(t *harnessTest, txes []*wire.MsgTx, + prevTxid chainhash.Hash) { + + for _, tx := range txes { + if tx.TxIn[0].PreviousOutPoint.Hash != prevTxid { + t.Fatalf("tx %v did not spend from %v", + tx.TxHash(), prevTxid) + } + } +} diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index c36e8a13..be87a3f8 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -268,6 +268,13 @@ func closeChannelAndAssert(ctx context.Context, t *harnessTest, net *lntest.NetworkHarness, node *lntest.HarnessNode, fundingChanPoint *lnrpc.ChannelPoint, force bool) *chainhash.Hash { + return closeChannelAndAssertType(ctx, t, net, node, fundingChanPoint, false, force) +} + +func closeChannelAndAssertType(ctx context.Context, t *harnessTest, + net *lntest.NetworkHarness, node *lntest.HarnessNode, + fundingChanPoint *lnrpc.ChannelPoint, anchors, force bool) *chainhash.Hash { + // Fetch the current channel policy. If the channel is currently // enabled, we will register for graph notifications before closing to // assert that the node sends out a disabling update as a result of the @@ -301,7 +308,9 @@ func closeChannelAndAssert(ctx context.Context, t *harnessTest, ) } - return assertChannelClosed(ctx, t, net, node, fundingChanPoint, closeUpdates) + return assertChannelClosed( + ctx, t, net, node, fundingChanPoint, anchors, closeUpdates, + ) } // closeReorgedChannelAndAssert attempts to close a channel identified by the @@ -322,14 +331,16 @@ func closeReorgedChannelAndAssert(ctx context.Context, t *harnessTest, t.Fatalf("unable to close channel: %v", err) } - return assertChannelClosed(ctx, t, net, node, fundingChanPoint, closeUpdates) + return assertChannelClosed( + ctx, t, net, node, fundingChanPoint, false, closeUpdates, + ) } // assertChannelClosed asserts that the channel is properly cleaned up after // initiating a cooperative or local close. func assertChannelClosed(ctx context.Context, t *harnessTest, net *lntest.NetworkHarness, node *lntest.HarnessNode, - fundingChanPoint *lnrpc.ChannelPoint, + fundingChanPoint *lnrpc.ChannelPoint, anchors bool, closeUpdates lnrpc.Lightning_CloseChannelClient) *chainhash.Hash { txid, err := lnd.GetChanPointFundingTxid(fundingChanPoint) @@ -381,8 +392,13 @@ func assertChannelClosed(ctx context.Context, t *harnessTest, // 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. - block := mineBlocks(t, net, 1, 1)[0] + // block. If there are anchors, we also expect an anchor sweep. + expectedTxes := 1 + if anchors { + expectedTxes = 2 + } + + block := mineBlocks(t, net, 1, expectedTxes)[0] closingTxid, err := net.WaitForChannelClose(ctx, closeUpdates) if err != nil {