itest: enable anchor commitment for multi-hop test

These tests exercise the different ways of sweeping a commitment, so
we'll cover the modified scripts used for anchor commitments and
spending the anchor itself by both parties.

Co-authored-by: Johan T. Halseth <johanth@gmail.com>
This commit is contained in:
Joost Jager 2020-03-04 13:21:29 +01:00
parent 23c81949cc
commit 8628a989a6
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
8 changed files with 265 additions and 165 deletions

@ -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)

@ -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,
)
}

@ -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,
)
}

@ -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)

@ -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,
)
}

@ -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,
)
}

@ -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)
}
}
}

@ -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 {