Merge pull request #4073 from joostjager/anchor-sweep-itest
lntest/itest: select anchor commitment format and sweeping itests
This commit is contained in:
commit
0b59ded7ca
@ -842,8 +842,9 @@ func (c *ChannelArbitrator) stateStep(
|
|||||||
// With the close transaction in hand, broadcast the
|
// With the close transaction in hand, broadcast the
|
||||||
// transaction to the network, thereby entering the post
|
// transaction to the network, thereby entering the post
|
||||||
// channel resolution state.
|
// channel resolution state.
|
||||||
log.Infof("Broadcasting force close transaction, "+
|
log.Infof("Broadcasting force close transaction %v, "+
|
||||||
"ChannelPoint(%v): %v", c.cfg.ChanPoint,
|
"ChannelPoint(%v): %v", closeTx.TxHash(),
|
||||||
|
c.cfg.ChanPoint,
|
||||||
newLogClosure(func() string {
|
newLogClosure(func() string {
|
||||||
return spew.Sdump(closeTx)
|
return spew.Sdump(closeTx)
|
||||||
}))
|
}))
|
||||||
|
@ -96,11 +96,18 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
// At this point, Bob decides that he wants to exit the channel
|
// At this point, Bob decides that he wants to exit the channel
|
||||||
// immediately, so he force closes his commitment transaction.
|
// immediately, so he force closes his commitment transaction.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||||
bobForceClose := closeChannelAndAssert(ctxt, t, net, bob,
|
bobForceClose := closeChannelAndAssertType(ctxt, t, net, bob,
|
||||||
aliceChanPoint, true)
|
aliceChanPoint, c == commitTypeAnchors, true)
|
||||||
|
|
||||||
// Alice will sweep her output immediately.
|
// Alice will sweep her commitment output immediately. If there are
|
||||||
_, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout)
|
// anchors, Alice will also sweep hers.
|
||||||
|
expectedTxes := 1
|
||||||
|
if c == commitTypeAnchors {
|
||||||
|
expectedTxes = 2
|
||||||
|
}
|
||||||
|
_, err = waitForNTxsInMempool(
|
||||||
|
net.Miner.Node, expectedTxes, minerMempoolTimeout,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find alice's sweep tx in miner mempool: %v",
|
t.Fatalf("unable to find alice's sweep tx in miner mempool: %v",
|
||||||
err)
|
err)
|
||||||
@ -135,8 +142,11 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
t.Fatalf("unable to generate blocks")
|
t.Fatalf("unable to generate blocks")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carol's commitment transaction should now be in the mempool.
|
// Carol's commitment transaction should now be in the mempool. If there
|
||||||
txids, err := waitForNTxsInMempool(net.Miner.Node, 1, minerMempoolTimeout)
|
// is an anchor, Carol will sweep that too.
|
||||||
|
_, err = waitForNTxsInMempool(
|
||||||
|
net.Miner.Node, expectedTxes, minerMempoolTimeout,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("transactions not found in mempool: %v", err)
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
@ -149,53 +159,47 @@ func testMultiHopHtlcLocalChainClaim(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
Index: bobChanPoint.OutputIndex,
|
Index: bobChanPoint.OutputIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The tx should be spending from the funding transaction,
|
// Look up the closing transaction. It should be spending from the
|
||||||
commitHash := txids[0]
|
// funding transaction,
|
||||||
tx1, err := net.Miner.Node.GetRawTransaction(commitHash)
|
closingTx := getSpendingTxInMempool(
|
||||||
if err != nil {
|
t, net.Miner.Node, minerMempoolTimeout, carolFundingPoint,
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
)
|
||||||
}
|
closingTxid := closingTx.TxHash()
|
||||||
if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint {
|
|
||||||
t.Fatalf("commit transaction not spending fundingtx: %v",
|
|
||||||
spew.Sdump(tx1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mine a block that should confirm the commit tx.
|
// Mine a block that should confirm the commit tx, the anchor if present
|
||||||
block := mineBlocks(t, net, 1, 1)[0]
|
// and the coinbase.
|
||||||
if len(block.Transactions) != 2 {
|
block := mineBlocks(t, net, 1, expectedTxes)[0]
|
||||||
t.Fatalf("expected 2 transactions in block, got %v",
|
if len(block.Transactions) != expectedTxes+1 {
|
||||||
len(block.Transactions))
|
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.
|
// Restart bob again.
|
||||||
if err := restartBob(); err != nil {
|
if err := restartBob(); err != nil {
|
||||||
t.Fatalf("unable to restart bob: %v", err)
|
t.Fatalf("unable to restart bob: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// After the force close transacion is mined, Carol should broadcast
|
// After the force close transacion is mined, Carol should broadcast her
|
||||||
// her second level HTLC transacion. Bob will broadcast a sweep tx to
|
// second level HTLC transacion. Bob will broadcast a sweep tx to sweep
|
||||||
// sweep his output in the channel with Carol. He can do this
|
// his output in the channel with Carol. He can do this immediately, as
|
||||||
// immediately, as the output is not timelocked since Carol was the one
|
// the output is not timelocked since Carol was the one force closing.
|
||||||
// force closing.
|
// If there are anchors on the commitment, Bob will also sweep his
|
||||||
commitSpends, err := waitForNTxsInMempool(net.Miner.Node, 2,
|
// anchor.
|
||||||
minerMempoolTimeout)
|
expectedTxes = 2
|
||||||
|
if c == commitTypeAnchors {
|
||||||
|
expectedTxes = 3
|
||||||
|
}
|
||||||
|
txes, err := getNTxsFromMempool(
|
||||||
|
net.Miner.Node, expectedTxes, minerMempoolTimeout,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("transactions not found in mempool: %v", err)
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both Carol's second level transaction and Bob's sweep should be
|
// Both Carol's second level transaction and Bob's sweep should be
|
||||||
// spending from the commitment transaction.
|
// spending from the commitment transaction.
|
||||||
for _, txid := range commitSpends {
|
assertAllTxesSpendFrom(t, txes, closingTxid)
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point we suspend Alice to make sure she'll handle the
|
// At this point we suspend Alice to make sure she'll handle the
|
||||||
// on-chain settle after a restart.
|
// 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).
|
// Mine a block to confirm the two transactions (+ the coinbase).
|
||||||
block = mineBlocks(t, net, 1, 2)[0]
|
block = mineBlocks(t, net, 1, expectedTxes)[0]
|
||||||
if len(block.Transactions) != 3 {
|
if len(block.Transactions) != expectedTxes+1 {
|
||||||
t.Fatalf("expected 3 transactions in block, got %v",
|
t.Fatalf("expected 3 transactions in block, got %v",
|
||||||
len(block.Transactions))
|
len(block.Transactions))
|
||||||
}
|
}
|
||||||
for _, txid := range commitSpends {
|
|
||||||
assertTxInBlock(t, block, txid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep track of the second level tx maturity.
|
// Keep track of the second level tx maturity.
|
||||||
carolSecondLevelCSV := uint32(defaultCSV)
|
carolSecondLevelCSV := uint32(defaultCSV)
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"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)
|
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)
|
bobFundingTxid, err := lnd.GetChanPointFundingTxid(bobChanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to get txid: %v", err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unable to find closing txid: %v", err)
|
t.Fatalf("unable to find closing txid: %v", err)
|
||||||
}
|
}
|
||||||
assertSpendingTxInMempool(
|
closeTx := getSpendingTxInMempool(
|
||||||
t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{
|
t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{
|
||||||
Hash: *bobFundingTxid,
|
Hash: *bobFundingTxid,
|
||||||
Index: bobChanPoint.OutputIndex,
|
Index: bobChanPoint.OutputIndex,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
closeTxid := closeTx.TxHash()
|
||||||
|
|
||||||
// Mine a block to confirm the closing transaction.
|
// 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
|
// At this point, Bob should have canceled backwards the dust HTLC
|
||||||
// that we sent earlier. This means Alice should now only have a single
|
// 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
|
// With the closing transaction confirmed, we should expect Bob's HTLC
|
||||||
// timeout transaction to be broadcast due to the expiry being reached.
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("unable to find bob's htlc timeout tx: %v", err)
|
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
|
// We'll mine the remaining blocks in order to generate the sweep
|
||||||
// transaction of Bob's commitment output.
|
// transaction of Bob's commitment output.
|
||||||
mineBlocks(t, net, defaultCSV, 1)
|
mineBlocks(t, net, defaultCSV, expectedTxes)
|
||||||
assertSpendingTxInMempool(
|
|
||||||
t, net.Miner.Node, minerMempoolTimeout, wire.OutPoint{
|
// Check that the sweep spends from the mined commitment.
|
||||||
Hash: *closeTxid,
|
txes, err = getNTxsFromMempool(net.Miner.Node, 1, minerMempoolTimeout)
|
||||||
Index: 1,
|
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
|
// Bob's pending channel report should show that he has a commitment
|
||||||
// output awaiting sweeping, and also that there's an outgoing HTLC
|
// 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())
|
t.Fatalf(predErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Coop close channel, expect no anchors.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
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"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd"
|
"github.com/lightningnetwork/lnd"
|
||||||
@ -126,8 +125,15 @@ func testMultiHopReceiverChainClaim(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// At this point, Carol should broadcast her active commitment
|
// At this point, Carol should broadcast her active commitment
|
||||||
// transaction in order to go to the chain and sweep her HTLC.
|
// transaction in order to go to the chain and sweep her HTLC. If there
|
||||||
txids, err := waitForNTxsInMempool(net.Miner.Node, 1, minerMempoolTimeout)
|
// are anchors, Carol also sweeps hers.
|
||||||
|
expectedTxes := 1
|
||||||
|
if c == commitTypeAnchors {
|
||||||
|
expectedTxes = 2
|
||||||
|
}
|
||||||
|
txes, err := getNTxsFromMempool(
|
||||||
|
net.Miner.Node, expectedTxes, minerMempoolTimeout,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected transaction not found in mempool: %v", err)
|
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
|
// The commitment transaction should be spending from the funding
|
||||||
// transaction.
|
// transaction.
|
||||||
commitHash := txids[0]
|
closingTx := getSpendingTxInMempool(
|
||||||
tx, err := net.Miner.Node.GetRawTransaction(commitHash)
|
t, net.Miner.Node, minerMempoolTimeout, carolFundingPoint,
|
||||||
if err != nil {
|
)
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
closingTxid := closingTx.TxHash()
|
||||||
}
|
|
||||||
commitTx := tx.MsgTx()
|
|
||||||
|
|
||||||
if commitTx.TxIn[0].PreviousOutPoint != carolFundingPoint {
|
|
||||||
t.Fatalf("commit transaction not spending from expected "+
|
|
||||||
"outpoint: %v", spew.Sdump(commitTx))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the commitment.
|
// Confirm the commitment.
|
||||||
mineBlocks(t, net, 1, 1)
|
mineBlocks(t, net, 1, expectedTxes)
|
||||||
|
|
||||||
// Restart bob again.
|
// Restart bob again.
|
||||||
if err := restartBob(); err != nil {
|
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
|
// After the force close transaction is mined, Carol should broadcast
|
||||||
// her second level HTLC transaction. Bob will broadcast a sweep tx to
|
// 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
|
// sweep his output in the channel with Carol. When Bob notices Carol's
|
||||||
// second level transaction in the mempool, he will extract the
|
// second level transaction in the mempool, he will extract the preimage
|
||||||
// preimage and settle the HTLC back off-chain.
|
// and settle the HTLC back off-chain. Bob will also sweep his anchor,
|
||||||
secondLevelHashes, err := waitForNTxsInMempool(net.Miner.Node, 2,
|
// if present.
|
||||||
minerMempoolTimeout)
|
expectedTxes = 2
|
||||||
|
if c == commitTypeAnchors {
|
||||||
|
expectedTxes = 3
|
||||||
|
}
|
||||||
|
txes, err = getNTxsFromMempool(net.Miner.Node,
|
||||||
|
expectedTxes, minerMempoolTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("transactions not found in mempool: %v", err)
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carol's second level transaction should be spending from
|
// All transactions should be spending from the commitment transaction.
|
||||||
// the commitment transaction.
|
assertAllTxesSpendFrom(t, txes, closingTxid)
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll now mine an additional block which should confirm both the
|
// We'll now mine an additional block which should confirm both the
|
||||||
// second layer transactions.
|
// 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
|
// We'll close out the channel between Alice and Bob, then shutdown
|
||||||
// carol to conclude the test.
|
// carol to conclude the test.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
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
|
// immediately force close the channel by broadcast her commitment
|
||||||
// transaction.
|
// transaction.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
||||||
aliceForceClose := closeChannelAndAssert(ctxt, t, net, alice,
|
aliceForceClose := closeChannelAndAssertType(ctxt, t, net, alice,
|
||||||
aliceChanPoint, true)
|
aliceChanPoint, c == commitTypeAnchors, true)
|
||||||
|
|
||||||
// Wait for the channel to be marked pending force close.
|
// Wait for the channel to be marked pending force close.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
@ -114,8 +114,13 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
|
|||||||
t.Fatalf("unable to generate blocks: %v", err)
|
t.Fatalf("unable to generate blocks: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alice should now sweep her funds.
|
// Alice should now sweep her funds. If there are anchors, Alice should
|
||||||
_, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout)
|
// also sweep hers.
|
||||||
|
expectedTxes := 1
|
||||||
|
if c == commitTypeAnchors {
|
||||||
|
expectedTxes = 2
|
||||||
|
}
|
||||||
|
_, err = waitForNTxsInMempool(net.Miner.Node, expectedTxes, minerMempoolTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find sweeping tx in mempool: %v", err)
|
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")
|
t.Fatalf("unable to generate blocks")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carol's commitment transaction should now be in the mempool.
|
// Carol's commitment transaction should now be in the mempool. If there
|
||||||
txids, err := waitForNTxsInMempool(net.Miner.Node, 1, minerMempoolTimeout)
|
// are anchors, Carol also sweeps her anchor.
|
||||||
|
_, err = waitForNTxsInMempool(
|
||||||
|
net.Miner.Node, expectedTxes, minerMempoolTimeout,
|
||||||
|
)
|
||||||
if err != nil {
|
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)
|
bobFundingTxid, err := lnd.GetChanPointFundingTxid(bobChanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -163,63 +171,51 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
|
|||||||
Index: bobChanPoint.OutputIndex,
|
Index: bobChanPoint.OutputIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The transaction should be spending from the funding transaction
|
// The closing transaction should be spending from the funding
|
||||||
commitHash := txids[0]
|
// transaction.
|
||||||
tx1, err := net.Miner.Node.GetRawTransaction(commitHash)
|
closingTx := getSpendingTxInMempool(
|
||||||
if err != nil {
|
t, net.Miner.Node, minerMempoolTimeout, carolFundingPoint,
|
||||||
t.Fatalf("unable to get txn: %v", err)
|
)
|
||||||
}
|
closingTxid := closingTx.TxHash()
|
||||||
if tx1.MsgTx().TxIn[0].PreviousOutPoint != carolFundingPoint {
|
|
||||||
t.Fatalf("commit transaction not spending fundingtx: %v",
|
|
||||||
spew.Sdump(tx1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mine a block, which should contain the commitment.
|
// Mine a block, which should contain: the commitment, possibly an
|
||||||
block := mineBlocks(t, net, 1, 1)[0]
|
// anchor sweep and the coinbase tx.
|
||||||
if len(block.Transactions) != 2 {
|
block := mineBlocks(t, net, 1, expectedTxes)[0]
|
||||||
t.Fatalf("expected 2 transactions in block, got %v",
|
if len(block.Transactions) != expectedTxes+1 {
|
||||||
len(block.Transactions))
|
t.Fatalf("expected %v transactions in block, got %v",
|
||||||
|
expectedTxes, len(block.Transactions))
|
||||||
}
|
}
|
||||||
assertTxInBlock(t, block, commitHash)
|
assertTxInBlock(t, block, &closingTxid)
|
||||||
|
|
||||||
// Restart bob again.
|
// Restart bob again.
|
||||||
if err := restartBob(); err != nil {
|
if err := restartBob(); err != nil {
|
||||||
t.Fatalf("unable to restart bob: %v", err)
|
t.Fatalf("unable to restart bob: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// After the force close transacion is mined, Carol should broadcast
|
// After the force close transacion is mined, Carol should broadcast her
|
||||||
// her second level HTLC transacion. Bob will broadcast a sweep tx to
|
// second level HTLC transacion. Bob will broadcast a sweep tx to sweep
|
||||||
// sweep his output in the channel with Carol. He can do this
|
// his output in the channel with Carol. He can do this immediately, as
|
||||||
// immediately, as the output is not timelocked since Carol was the one
|
// the output is not timelocked since Carol was the one force closing.
|
||||||
// force closing.
|
// If there are anchors, Bob should also sweep his.
|
||||||
commitSpends, err := waitForNTxsInMempool(net.Miner.Node, 2,
|
expectedTxes = 2
|
||||||
|
if c == commitTypeAnchors {
|
||||||
|
expectedTxes = 3
|
||||||
|
}
|
||||||
|
txes, err := getNTxsFromMempool(net.Miner.Node, expectedTxes,
|
||||||
minerMempoolTimeout)
|
minerMempoolTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("transactions not found in mempool: %v", err)
|
t.Fatalf("transactions not found in mempool: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both Carol's second level transaction and Bob's sweep should be
|
// All transactions should be pending from the commitment transaction.
|
||||||
// spending from the commitment transaction.
|
assertAllTxesSpendFrom(t, txes, closingTxid)
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mine a block to confirm the two transactions (+ coinbase).
|
// Mine a block to confirm the two transactions (+ coinbase).
|
||||||
block = mineBlocks(t, net, 1, 2)[0]
|
block = mineBlocks(t, net, 1, expectedTxes)[0]
|
||||||
if len(block.Transactions) != 3 {
|
if len(block.Transactions) != expectedTxes+1 {
|
||||||
t.Fatalf("expected 3 transactions in block, got %v",
|
t.Fatalf("expected 3 transactions in block, got %v",
|
||||||
len(block.Transactions))
|
len(block.Transactions))
|
||||||
}
|
}
|
||||||
for _, txid := range commitSpends {
|
|
||||||
assertTxInBlock(t, block, txid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep track of the second level tx maturity.
|
// Keep track of the second level tx maturity.
|
||||||
carolSecondLevelCSV := uint32(defaultCSV)
|
carolSecondLevelCSV := uint32(defaultCSV)
|
||||||
|
@ -82,7 +82,9 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
// force close the Bob -> Carol channel. This should trigger contract
|
// force close the Bob -> Carol channel. This should trigger contract
|
||||||
// resolution mode for both of them.
|
// resolution mode for both of them.
|
||||||
ctxt, _ := context.WithTimeout(ctxb, channelCloseTimeout)
|
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
|
// At this point, Bob should have a pending force close channel as he
|
||||||
// just went to chain.
|
// just went to chain.
|
||||||
@ -116,8 +118,16 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
t.Fatalf(predErr.Error())
|
t.Fatalf(predErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll mine defaultCSV blocks in order to generate the sweep transaction
|
// We'll mine defaultCSV blocks in order to generate the sweep
|
||||||
// of Bob's funding output.
|
// 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 {
|
if _, err := net.Miner.Node.Generate(defaultCSV); err != nil {
|
||||||
t.Fatalf("unable to generate blocks: %v", err)
|
t.Fatalf("unable to generate blocks: %v", err)
|
||||||
}
|
}
|
||||||
@ -275,6 +285,9 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
t.Fatalf(predErr.Error())
|
t.Fatalf(predErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Coop close, no anchors.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
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
|
// 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
|
// 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)
|
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
|
// At this point, Bob should have a pending force close channel as
|
||||||
// Carol has gone directly to chain.
|
// Carol has gone directly to chain.
|
||||||
@ -110,11 +114,18 @@ func testMultiHopRemoteForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
|||||||
t.Fatalf(predErr.Error())
|
t.Fatalf(predErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bob can sweep his output immediately.
|
// Bob can sweep his output immediately. If there is an anchor, Bob will
|
||||||
_, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout)
|
// sweep that as well.
|
||||||
|
expectedTxes := 1
|
||||||
|
if c == commitTypeAnchors {
|
||||||
|
expectedTxes = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = waitForNTxsInMempool(
|
||||||
|
net.Miner.Node, expectedTxes, minerMempoolTimeout,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to find bob's funding output sweep tx: %v",
|
t.Fatalf("failed to find txes in miner mempool: %v", err)
|
||||||
err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, we'll mine enough blocks for the HTLC to expire. At this
|
// 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,
|
// 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
|
// and then shutting down the new node we created as its no longer
|
||||||
// needed.
|
// needed. Coop close, no anchors.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
|
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"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
||||||
@ -65,6 +67,7 @@ func testMultiHopHtlcClaims(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
|
|
||||||
commitTypes := []commitType{
|
commitTypes := []commitType{
|
||||||
commitTypeLegacy,
|
commitTypeLegacy,
|
||||||
|
commitTypeAnchors,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, commitType := range commitTypes {
|
for _, commitType := range commitTypes {
|
||||||
@ -204,6 +207,8 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
t.Fatalf("unable to connect peers: %v", err)
|
t.Fatalf("unable to connect peers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure there are enough utxos for anchoring.
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout)
|
ctxt, _ = context.WithTimeout(context.Background(), defaultTimeout)
|
||||||
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, alice)
|
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, alice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -215,6 +220,7 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to send coins to Bob: %v", err)
|
t.Fatalf("unable to send coins to Bob: %v", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We'll start the test by creating a channel between Alice and Bob,
|
// We'll start the test by creating a channel between Alice and Bob,
|
||||||
// which will act as the first leg for out multi-hop HTLC.
|
// which will act as the first leg for out multi-hop HTLC.
|
||||||
@ -255,6 +261,18 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
t.Fatalf("unable to connect bob to carol: %v", err)
|
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
|
// We'll then create a channel from Bob to Carol. After this channel is
|
||||||
// open, our topology looks like: A -> B -> C.
|
// open, our topology looks like: A -> B -> C.
|
||||||
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout)
|
ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout)
|
||||||
@ -282,3 +300,16 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
|
|
||||||
return aliceChanPoint, bobChanPoint, carol
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -43,6 +43,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing"
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
)
|
)
|
||||||
@ -59,6 +60,7 @@ const (
|
|||||||
channelOpenTimeout = lntest.ChannelOpenTimeout
|
channelOpenTimeout = lntest.ChannelOpenTimeout
|
||||||
channelCloseTimeout = lntest.ChannelCloseTimeout
|
channelCloseTimeout = lntest.ChannelCloseTimeout
|
||||||
itestLndBinary = "../../lnd-itest"
|
itestLndBinary = "../../lnd-itest"
|
||||||
|
anchorSize = 330
|
||||||
)
|
)
|
||||||
|
|
||||||
// harnessTest wraps a regular testing.T providing enhanced error detection
|
// harnessTest wraps a regular testing.T providing enhanced error detection
|
||||||
@ -266,6 +268,13 @@ func closeChannelAndAssert(ctx context.Context, t *harnessTest,
|
|||||||
net *lntest.NetworkHarness, node *lntest.HarnessNode,
|
net *lntest.NetworkHarness, node *lntest.HarnessNode,
|
||||||
fundingChanPoint *lnrpc.ChannelPoint, force bool) *chainhash.Hash {
|
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
|
// Fetch the current channel policy. If the channel is currently
|
||||||
// enabled, we will register for graph notifications before closing to
|
// enabled, we will register for graph notifications before closing to
|
||||||
// assert that the node sends out a disabling update as a result of the
|
// assert that the node sends out a disabling update as a result of the
|
||||||
@ -299,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
|
// closeReorgedChannelAndAssert attempts to close a channel identified by the
|
||||||
@ -320,14 +331,16 @@ func closeReorgedChannelAndAssert(ctx context.Context, t *harnessTest,
|
|||||||
t.Fatalf("unable to close channel: %v", err)
|
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
|
// assertChannelClosed asserts that the channel is properly cleaned up after
|
||||||
// initiating a cooperative or local close.
|
// initiating a cooperative or local close.
|
||||||
func assertChannelClosed(ctx context.Context, t *harnessTest,
|
func assertChannelClosed(ctx context.Context, t *harnessTest,
|
||||||
net *lntest.NetworkHarness, node *lntest.HarnessNode,
|
net *lntest.NetworkHarness, node *lntest.HarnessNode,
|
||||||
fundingChanPoint *lnrpc.ChannelPoint,
|
fundingChanPoint *lnrpc.ChannelPoint, anchors bool,
|
||||||
closeUpdates lnrpc.Lightning_CloseChannelClient) *chainhash.Hash {
|
closeUpdates lnrpc.Lightning_CloseChannelClient) *chainhash.Hash {
|
||||||
|
|
||||||
txid, err := lnd.GetChanPointFundingTxid(fundingChanPoint)
|
txid, err := lnd.GetChanPointFundingTxid(fundingChanPoint)
|
||||||
@ -379,8 +392,13 @@ func assertChannelClosed(ctx context.Context, t *harnessTest,
|
|||||||
|
|
||||||
// We'll now, generate a single block, wait for the final close status
|
// We'll now, generate a single block, wait for the final close status
|
||||||
// update, then ensure that the closing transaction was included in the
|
// update, then ensure that the closing transaction was included in the
|
||||||
// block.
|
// block. If there are anchors, we also expect an anchor sweep.
|
||||||
block := mineBlocks(t, net, 1, 1)[0]
|
expectedTxes := 1
|
||||||
|
if anchors {
|
||||||
|
expectedTxes = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
block := mineBlocks(t, net, 1, expectedTxes)[0]
|
||||||
|
|
||||||
closingTxid, err := net.WaitForChannelClose(ctx, closeUpdates)
|
closingTxid, err := net.WaitForChannelClose(ctx, closeUpdates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -600,22 +618,6 @@ func shutdownAndAssert(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// calcStaticFee calculates appropriate fees for commitment transactions. This
|
|
||||||
// function provides a simple way to allow test balance assertions to take fee
|
|
||||||
// calculations into account.
|
|
||||||
//
|
|
||||||
// TODO(bvu): Refactor when dynamic fee estimation is added.
|
|
||||||
// TODO(conner) remove code duplication
|
|
||||||
func calcStaticFee(numHTLCs int) btcutil.Amount {
|
|
||||||
const (
|
|
||||||
commitWeight = btcutil.Amount(724)
|
|
||||||
htlcWeight = 172
|
|
||||||
feePerKw = btcutil.Amount(50 * 1000 / 4)
|
|
||||||
)
|
|
||||||
return feePerKw * (commitWeight +
|
|
||||||
btcutil.Amount(htlcWeight*numHTLCs)) / 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
// completePaymentRequests sends payments from a lightning node to complete all
|
// completePaymentRequests sends payments from a lightning node to complete all
|
||||||
// payment requests. If the awaitResponse parameter is true, this function
|
// payment requests. If the awaitResponse parameter is true, this function
|
||||||
// does not return until all payments successfully complete without errors.
|
// does not return until all payments successfully complete without errors.
|
||||||
@ -998,6 +1000,66 @@ func (c commitType) Args() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calcStaticFee calculates appropriate fees for commitment transactions. This
|
||||||
|
// function provides a simple way to allow test balance assertions to take fee
|
||||||
|
// calculations into account.
|
||||||
|
func (c commitType) calcStaticFee(numHTLCs int) btcutil.Amount {
|
||||||
|
const htlcWeight = input.HTLCWeight
|
||||||
|
var (
|
||||||
|
feePerKw = chainfee.SatPerKVByte(50000).FeePerKWeight()
|
||||||
|
commitWeight = input.CommitWeight
|
||||||
|
anchors = btcutil.Amount(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
// The anchor commitment type is slightly heavier, and we must also add
|
||||||
|
// the value of the two anchors to the resulting fee the initiator
|
||||||
|
// pays.
|
||||||
|
if c == commitTypeAnchors {
|
||||||
|
commitWeight = input.AnchorCommitWeight
|
||||||
|
anchors = 2 * anchorSize
|
||||||
|
}
|
||||||
|
|
||||||
|
return feePerKw.FeeForWeight(int64(commitWeight+htlcWeight*numHTLCs)) +
|
||||||
|
anchors
|
||||||
|
}
|
||||||
|
|
||||||
|
// channelCommitType retrieves the active channel commitment type for the given
|
||||||
|
// chan point.
|
||||||
|
func channelCommitType(node *lntest.HarnessNode,
|
||||||
|
chanPoint *lnrpc.ChannelPoint) (commitType, error) {
|
||||||
|
|
||||||
|
ctxb := context.Background()
|
||||||
|
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||||
|
|
||||||
|
req := &lnrpc.ListChannelsRequest{}
|
||||||
|
channels, err := node.ListChannels(ctxt, req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("listchannels failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range channels.Channels {
|
||||||
|
if c.ChannelPoint == txStr(chanPoint) {
|
||||||
|
switch c.CommitmentType {
|
||||||
|
|
||||||
|
// If the anchor output size is non-zero, we are
|
||||||
|
// dealing with the anchor type.
|
||||||
|
case lnrpc.CommitmentType_ANCHORS:
|
||||||
|
return commitTypeAnchors, nil
|
||||||
|
|
||||||
|
// StaticRemoteKey means it is tweakless,
|
||||||
|
case lnrpc.CommitmentType_STATIC_REMOTE_KEY:
|
||||||
|
return commitTypeTweakless, nil
|
||||||
|
|
||||||
|
// Otherwise legacy.
|
||||||
|
default:
|
||||||
|
return commitTypeLegacy, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("channel point %v not found", chanPoint)
|
||||||
|
}
|
||||||
|
|
||||||
// basicChannelFundingTest is a sub-test of the main testBasicChannelFunding
|
// basicChannelFundingTest is a sub-test of the main testBasicChannelFunding
|
||||||
// test. Given two nodes: Alice and Bob, it'll assert proper channel creation,
|
// test. Given two nodes: Alice and Bob, it'll assert proper channel creation,
|
||||||
// then return a function closure that should be called to assert proper
|
// then return a function closure that should be called to assert proper
|
||||||
@ -1038,6 +1100,12 @@ func basicChannelFundingTest(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
"channel: %v", err)
|
"channel: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cType, err := channelCommitType(alice, chanPoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("unable to get channel "+
|
||||||
|
"type: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// With the channel open, ensure that the amount specified above has
|
// With the channel open, ensure that the amount specified above has
|
||||||
// properly been pushed to Bob.
|
// properly been pushed to Bob.
|
||||||
balReq := &lnrpc.ChannelBalanceRequest{}
|
balReq := &lnrpc.ChannelBalanceRequest{}
|
||||||
@ -1054,7 +1122,7 @@ func basicChannelFundingTest(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
"balance: %v", err)
|
"balance: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expBalanceAlice := chanAmt - pushAmt - calcStaticFee(0)
|
expBalanceAlice := chanAmt - pushAmt - cType.calcStaticFee(0)
|
||||||
aliceBalance := btcutil.Amount(aliceBal.Balance)
|
aliceBalance := btcutil.Amount(aliceBal.Balance)
|
||||||
if aliceBalance != expBalanceAlice {
|
if aliceBalance != expBalanceAlice {
|
||||||
return nil, nil, nil, fmt.Errorf("alice's balance is "+
|
return nil, nil, nil, fmt.Errorf("alice's balance is "+
|
||||||
@ -1104,6 +1172,7 @@ func testBasicChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
allTypes := []commitType{
|
allTypes := []commitType{
|
||||||
commitTypeLegacy,
|
commitTypeLegacy,
|
||||||
commitTypeTweakless,
|
commitTypeTweakless,
|
||||||
|
commitTypeAnchors,
|
||||||
}
|
}
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@ -1156,27 +1225,58 @@ test:
|
|||||||
t.Fatalf("failed funding flow: %v", err)
|
t.Fatalf("failed funding flow: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
carolTweakless := carolCommitType == commitTypeTweakless
|
// Both nodes should report the same commitment
|
||||||
|
// type.
|
||||||
|
chansCommitType := carolChannel.CommitmentType
|
||||||
|
if daveChannel.CommitmentType != chansCommitType {
|
||||||
|
t.Fatalf("commit types don't match, "+
|
||||||
|
"carol got %v, dave got %v",
|
||||||
|
carolChannel.CommitmentType,
|
||||||
|
daveChannel.CommitmentType,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
daveTweakless := daveCommitType == commitTypeTweakless
|
// Now check that the commitment type reported
|
||||||
|
// by both nodes is what we expect. It will be
|
||||||
|
// the minimum of the two nodes' preference, in
|
||||||
|
// the order Legacy, Tweakless, Anchors.
|
||||||
|
expType := carolCommitType
|
||||||
|
|
||||||
tweaklessSignalled := carolTweakless && daveTweakless
|
switch daveCommitType {
|
||||||
tweaklessChans := (carolChannel.StaticRemoteKey &&
|
|
||||||
daveChannel.StaticRemoteKey)
|
// Dave supports anchors, type will be what
|
||||||
|
// Carol supports.
|
||||||
|
case commitTypeAnchors:
|
||||||
|
|
||||||
|
// Dave only supports tweakless, channel will
|
||||||
|
// be downgraded to this type if Carol supports
|
||||||
|
// anchors.
|
||||||
|
case commitTypeTweakless:
|
||||||
|
if expType == commitTypeAnchors {
|
||||||
|
expType = commitTypeTweakless
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dave only supoprts legacy type, channel will
|
||||||
|
// be downgraded to this type.
|
||||||
|
case commitTypeLegacy:
|
||||||
|
expType = commitTypeLegacy
|
||||||
|
|
||||||
|
default:
|
||||||
|
t.Fatalf("invalid commit type %v",
|
||||||
|
daveCommitType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the signalled type matches what we
|
||||||
|
// expect.
|
||||||
switch {
|
switch {
|
||||||
// If both sides signalled a tweakless channel, and the
|
case expType == commitTypeAnchors && chansCommitType == lnrpc.CommitmentType_ANCHORS:
|
||||||
// resulting channel doesn't reflect this, then this
|
case expType == commitTypeTweakless && chansCommitType == lnrpc.CommitmentType_STATIC_REMOTE_KEY:
|
||||||
// is a failed case.
|
case expType == commitTypeLegacy && chansCommitType == lnrpc.CommitmentType_LEGACY:
|
||||||
case tweaklessSignalled && !tweaklessChans:
|
|
||||||
t.Fatalf("expected tweakless channnel, got " +
|
|
||||||
"non-tweaked channel")
|
|
||||||
|
|
||||||
// If both sides didn't signal a tweakless
|
default:
|
||||||
// channel, and the resulting channel is
|
t.Fatalf("expected nodes to signal "+
|
||||||
// tweakless, and this is also a failed case.
|
"commit type %v, instead got "+
|
||||||
case !tweaklessSignalled && tweaklessChans:
|
"%v", expType, chansCommitType)
|
||||||
t.Fatalf("expected non-tweaked channel, got " +
|
|
||||||
"tweakless channel")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// As we've concluded this sub-test case we'll
|
// As we've concluded this sub-test case we'll
|
||||||
@ -1275,6 +1375,11 @@ func testUnconfirmedChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
t.Fatalf("error while waiting for channel open: %v", err)
|
t.Fatalf("error while waiting for channel open: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cType, err := channelCommitType(net.Alice, chanPoint)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get channel type: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// With the channel open, we'll check the balances on each side of the
|
// With the channel open, we'll check the balances on each side of the
|
||||||
// channel as a sanity check to ensure things worked out as intended.
|
// channel as a sanity check to ensure things worked out as intended.
|
||||||
balReq := &lnrpc.ChannelBalanceRequest{}
|
balReq := &lnrpc.ChannelBalanceRequest{}
|
||||||
@ -1288,9 +1393,9 @@ func testUnconfirmedChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to get alice's balance: %v", err)
|
t.Fatalf("unable to get alice's balance: %v", err)
|
||||||
}
|
}
|
||||||
if carolBal.Balance != int64(chanAmt-pushAmt-calcStaticFee(0)) {
|
if carolBal.Balance != int64(chanAmt-pushAmt-cType.calcStaticFee(0)) {
|
||||||
t.Fatalf("carol's balance is incorrect: expected %v got %v",
|
t.Fatalf("carol's balance is incorrect: expected %v got %v",
|
||||||
chanAmt-pushAmt-calcStaticFee(0), carolBal)
|
chanAmt-pushAmt-cType.calcStaticFee(0), carolBal)
|
||||||
}
|
}
|
||||||
if aliceBal.Balance != int64(pushAmt) {
|
if aliceBal.Balance != int64(pushAmt) {
|
||||||
t.Fatalf("alice's balance is incorrect: expected %v got %v",
|
t.Fatalf("alice's balance is incorrect: expected %v got %v",
|
||||||
@ -2708,9 +2813,14 @@ func testChannelBalance(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
"timeout: %v", err)
|
"timeout: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cType, err := channelCommitType(net.Alice, chanPoint)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get channel type: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// As this is a single funder channel, Alice's balance should be
|
// As this is a single funder channel, Alice's balance should be
|
||||||
// exactly 0.5 BTC since now state transitions have taken place yet.
|
// exactly 0.5 BTC since now state transitions have taken place yet.
|
||||||
checkChannelBalance(net.Alice, amount-calcStaticFee(0))
|
checkChannelBalance(net.Alice, amount-cType.calcStaticFee(0))
|
||||||
|
|
||||||
// Ensure Bob currently has no available balance within the channel.
|
// Ensure Bob currently has no available balance within the channel.
|
||||||
checkChannelBalance(net.Bob, 0)
|
checkChannelBalance(net.Bob, 0)
|
||||||
@ -2987,6 +3097,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
// outputs can be swept.
|
// outputs can be swept.
|
||||||
commitTypes := []commitType{
|
commitTypes := []commitType{
|
||||||
commitTypeLegacy,
|
commitTypeLegacy,
|
||||||
|
commitTypeAnchors,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, channelType := range commitTypes {
|
for _, channelType := range commitTypes {
|
||||||
@ -3026,7 +3137,19 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
err)
|
err)
|
||||||
}
|
}
|
||||||
|
|
||||||
channelForceClosureTest(net, ht, alice, carol)
|
// Also give Carol some coins to allow her to sweep her
|
||||||
|
// anchor.
|
||||||
|
err = net.SendCoins(
|
||||||
|
ctxt, btcutil.SatoshiPerBitcoin, carol,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to send coins to Alice: %v",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
channelForceClosureTest(
|
||||||
|
net, ht, alice, carol, channelType,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
if !success {
|
if !success {
|
||||||
return
|
return
|
||||||
@ -3035,7 +3158,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func channelForceClosureTest(net *lntest.NetworkHarness, t *harnessTest,
|
func channelForceClosureTest(net *lntest.NetworkHarness, t *harnessTest,
|
||||||
alice, carol *lntest.HarnessNode) {
|
alice, carol *lntest.HarnessNode, channelType commitType) {
|
||||||
|
|
||||||
ctxb := context.Background()
|
ctxb := context.Background()
|
||||||
|
|
||||||
@ -3211,8 +3334,16 @@ func channelForceClosureTest(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mine a block which should confirm the commitment transaction
|
// Mine a block which should confirm the commitment transaction
|
||||||
// broadcast as a result of the force closure.
|
// broadcast as a result of the force closure. If there are anchors, we
|
||||||
_, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout)
|
// also expect the anchor sweep tx to be in the mempool.
|
||||||
|
expectedTxes := 1
|
||||||
|
if channelType == commitTypeAnchors {
|
||||||
|
expectedTxes = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = waitForNTxsInMempool(
|
||||||
|
net.Miner.Node, expectedTxes, minerMempoolTimeout,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to find commitment in miner mempool: %v", err)
|
t.Fatalf("failed to find commitment in miner mempool: %v", err)
|
||||||
}
|
}
|
||||||
@ -3223,52 +3354,52 @@ func channelForceClosureTest(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
|
|
||||||
// Now that the commitment has been confirmed, the channel should be
|
// Now that the commitment has been confirmed, the channel should be
|
||||||
// marked as force closed.
|
// marked as force closed.
|
||||||
err = wait.Predicate(func() bool {
|
err = wait.NoError(func() error {
|
||||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||||
pendingChanResp, err := alice.PendingChannels(
|
pendingChanResp, err := alice.PendingChannels(
|
||||||
ctxt, pendingChansRequest,
|
ctxt, pendingChansRequest,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
predErr = fmt.Errorf("unable to query for pending "+
|
return fmt.Errorf("unable to query for pending "+
|
||||||
"channels: %v", err)
|
"channels: %v", err)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
predErr = checkNumForceClosedChannels(pendingChanResp, 1)
|
err = checkNumForceClosedChannels(pendingChanResp, 1)
|
||||||
if predErr != nil {
|
if err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
forceClose, predErr := findForceClosedChannel(
|
forceClose, err := findForceClosedChannel(pendingChanResp, &op)
|
||||||
pendingChanResp, &op,
|
if err != nil {
|
||||||
)
|
return err
|
||||||
if predErr != nil {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that the channel has been force closed, it should now
|
// Now that the channel has been force closed, it should now
|
||||||
// have the height and number of blocks to confirm populated.
|
// have the height and number of blocks to confirm populated.
|
||||||
predErr = checkCommitmentMaturity(
|
err = checkCommitmentMaturity(
|
||||||
forceClose, commCsvMaturityHeight, int32(defaultCSV),
|
forceClose, commCsvMaturityHeight, int32(defaultCSV),
|
||||||
)
|
)
|
||||||
if predErr != nil {
|
if err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// None of our outputs have been swept, so they should all be in
|
// None of our outputs have been swept, so they should all be in
|
||||||
// limbo.
|
// limbo. For anchors, we expect the anchor amount to be
|
||||||
|
// recovered.
|
||||||
if forceClose.LimboBalance == 0 {
|
if forceClose.LimboBalance == 0 {
|
||||||
predErr = errors.New("all funds should still be in " +
|
return errors.New("all funds should still be in " +
|
||||||
"limbo")
|
"limbo")
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
if forceClose.RecoveredBalance != 0 {
|
expectedRecoveredBalance := int64(0)
|
||||||
predErr = errors.New("no funds should yet be shown " +
|
if channelType == commitTypeAnchors {
|
||||||
|
expectedRecoveredBalance = anchorSize
|
||||||
|
}
|
||||||
|
if forceClose.RecoveredBalance != expectedRecoveredBalance {
|
||||||
|
return errors.New("no funds should yet be shown " +
|
||||||
"as recovered")
|
"as recovered")
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return nil
|
||||||
}, 15*time.Second)
|
}, 15*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(predErr.Error())
|
t.Fatalf(predErr.Error())
|
||||||
@ -3283,8 +3414,11 @@ func channelForceClosureTest(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Carol's sweep tx should be in the mempool already, as her output is
|
// Carol's sweep tx should be in the mempool already, as her output is
|
||||||
// not timelocked.
|
// not timelocked. If there are anchors, we also expect Carol's anchor
|
||||||
_, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout)
|
// sweep now.
|
||||||
|
_, err = waitForNTxsInMempool(
|
||||||
|
net.Miner.Node, expectedTxes, minerMempoolTimeout,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to find Carol's sweep in miner mempool: %v",
|
t.Fatalf("failed to find Carol's sweep in miner mempool: %v",
|
||||||
err)
|
err)
|
||||||
@ -3346,7 +3480,11 @@ func channelForceClosureTest(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
return errors.New("all funds should still be in " +
|
return errors.New("all funds should still be in " +
|
||||||
"limbo")
|
"limbo")
|
||||||
}
|
}
|
||||||
if forceClose.RecoveredBalance != 0 {
|
expectedRecoveredBalance := int64(0)
|
||||||
|
if channelType == commitTypeAnchors {
|
||||||
|
expectedRecoveredBalance = anchorSize
|
||||||
|
}
|
||||||
|
if forceClose.RecoveredBalance != expectedRecoveredBalance {
|
||||||
return errors.New("no funds should yet be shown " +
|
return errors.New("no funds should yet be shown " +
|
||||||
"as recovered")
|
"as recovered")
|
||||||
}
|
}
|
||||||
@ -6855,6 +6993,27 @@ func waitForNTxsInMempool(miner *rpcclient.Client, n int,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getNTxsFromMempool polls until finding the desired number of transactions in
|
||||||
|
// the provided miner's mempool and returns the full transactions to the caller.
|
||||||
|
func getNTxsFromMempool(miner *rpcclient.Client, n int,
|
||||||
|
timeout time.Duration) ([]*wire.MsgTx, error) {
|
||||||
|
|
||||||
|
txids, err := waitForNTxsInMempool(miner, n, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var txes []*wire.MsgTx
|
||||||
|
for _, txid := range txids {
|
||||||
|
tx, err := miner.GetRawTransaction(txid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
txes = append(txes, tx.MsgTx())
|
||||||
|
}
|
||||||
|
return txes, nil
|
||||||
|
}
|
||||||
|
|
||||||
// testFailingChannel tests that we will fail the channel by force closing ii
|
// testFailingChannel tests that we will fail the channel by force closing ii
|
||||||
// in the case where a counterparty tries to settle an HTLC with the wrong
|
// in the case where a counterparty tries to settle an HTLC with the wrong
|
||||||
// preimage.
|
// preimage.
|
||||||
@ -9156,7 +9315,12 @@ func testHtlcErrorPropagation(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
t.Fatalf("channel not seen by alice before timeout: %v", err)
|
t.Fatalf("channel not seen by alice before timeout: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
commitFee := calcStaticFee(0)
|
cType, err := channelCommitType(net.Alice, chanPointAlice)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get channel type: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
commitFee := cType.calcStaticFee(0)
|
||||||
assertBaseBalance := func() {
|
assertBaseBalance := func() {
|
||||||
balReq := &lnrpc.ChannelBalanceRequest{}
|
balReq := &lnrpc.ChannelBalanceRequest{}
|
||||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||||
@ -10573,7 +10737,16 @@ func assertNumActiveHtlcs(nodes []*lntest.HarnessNode, numHtlcs int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func assertSpendingTxInMempool(t *harnessTest, miner *rpcclient.Client,
|
func assertSpendingTxInMempool(t *harnessTest, miner *rpcclient.Client,
|
||||||
timeout time.Duration, chanPoint wire.OutPoint) {
|
timeout time.Duration, chanPoint wire.OutPoint) chainhash.Hash {
|
||||||
|
|
||||||
|
tx := getSpendingTxInMempool(t, miner, timeout, chanPoint)
|
||||||
|
return tx.TxHash()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSpendingTxInMempool waits for a transaction spending the given outpoint to
|
||||||
|
// appear in the mempool and returns that tx in full.
|
||||||
|
func getSpendingTxInMempool(t *harnessTest, miner *rpcclient.Client,
|
||||||
|
timeout time.Duration, chanPoint wire.OutPoint) *wire.MsgTx {
|
||||||
|
|
||||||
breakTimeout := time.After(timeout)
|
breakTimeout := time.After(timeout)
|
||||||
ticker := time.NewTicker(50 * time.Millisecond)
|
ticker := time.NewTicker(50 * time.Millisecond)
|
||||||
@ -10599,9 +10772,10 @@ func assertSpendingTxInMempool(t *harnessTest, miner *rpcclient.Client,
|
|||||||
t.Fatalf("unable to fetch tx: %v", err)
|
t.Fatalf("unable to fetch tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, txIn := range tx.MsgTx().TxIn {
|
msgTx := tx.MsgTx()
|
||||||
|
for _, txIn := range msgTx.TxIn {
|
||||||
if txIn.PreviousOutPoint == chanPoint {
|
if txIn.PreviousOutPoint == chanPoint {
|
||||||
return
|
return msgTx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,10 +138,6 @@ func createSweepTx(inputs []input.Input, outputPkScript []byte,
|
|||||||
|
|
||||||
txFee := feePerKw.FeeForWeight(txWeight)
|
txFee := feePerKw.FeeForWeight(txWeight)
|
||||||
|
|
||||||
log.Infof("Creating sweep transaction for %v inputs (%s) "+
|
|
||||||
"using %v sat/kw, tx_fee=%v", len(inputs),
|
|
||||||
inputTypeSummary(inputs), int64(feePerKw), txFee)
|
|
||||||
|
|
||||||
// Sum up the total value contained in the inputs.
|
// Sum up the total value contained in the inputs.
|
||||||
var totalSum btcutil.Amount
|
var totalSum btcutil.Amount
|
||||||
for _, o := range inputs {
|
for _, o := range inputs {
|
||||||
@ -211,6 +207,10 @@ func createSweepTx(inputs []input.Input, outputPkScript []byte,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("Creating sweep transaction %v for %v inputs (%s) "+
|
||||||
|
"using %v sat/kw, tx_fee=%v", sweepTx.TxHash(), len(inputs),
|
||||||
|
inputTypeSummary(inputs), int64(feePerKw), txFee)
|
||||||
|
|
||||||
return sweepTx, nil
|
return sweepTx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,27 +254,19 @@ func getWeightEstimate(inputs []input.Input) ([]input.Input, int64) {
|
|||||||
// inputSummary returns a string containing a human readable summary about the
|
// inputSummary returns a string containing a human readable summary about the
|
||||||
// witness types of a list of inputs.
|
// witness types of a list of inputs.
|
||||||
func inputTypeSummary(inputs []input.Input) string {
|
func inputTypeSummary(inputs []input.Input) string {
|
||||||
// Count each input by the string representation of its witness type.
|
// Sort inputs by witness type.
|
||||||
// We also keep track of the keys so we can later sort by them to get
|
sortedInputs := make([]input.Input, len(inputs))
|
||||||
// a stable output.
|
copy(sortedInputs, inputs)
|
||||||
counts := make(map[string]uint32)
|
sort.Slice(sortedInputs, func(i, j int) bool {
|
||||||
keys := make([]string, 0, len(inputs))
|
return sortedInputs[i].WitnessType().String() <
|
||||||
for _, i := range inputs {
|
sortedInputs[j].WitnessType().String()
|
||||||
key := i.WitnessType().String()
|
})
|
||||||
_, ok := counts[key]
|
|
||||||
if !ok {
|
|
||||||
counts[key] = 0
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
counts[key]++
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
|
|
||||||
// Return a nice string representation of the counts by comma joining a
|
|
||||||
// slice.
|
|
||||||
var parts []string
|
var parts []string
|
||||||
for _, witnessType := range keys {
|
for _, i := range sortedInputs {
|
||||||
part := fmt.Sprintf("%d %s", counts[witnessType], witnessType)
|
part := fmt.Sprintf("%v (%v)",
|
||||||
|
*i.OutPoint(), i.WitnessType())
|
||||||
|
|
||||||
parts = append(parts, part)
|
parts = append(parts, part)
|
||||||
}
|
}
|
||||||
return strings.Join(parts, ", ")
|
return strings.Join(parts, ", ")
|
||||||
|
@ -3,6 +3,7 @@ package sweep
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
)
|
)
|
||||||
@ -15,9 +16,10 @@ var (
|
|||||||
input.WitnessKeyHash,
|
input.WitnessKeyHash,
|
||||||
}
|
}
|
||||||
expectedWeight = int64(1462)
|
expectedWeight = int64(1462)
|
||||||
expectedSummary = "1 CommitmentTimeLock, 1 " +
|
expectedSummary = "0000000000000000000000000000000000000000000000000000000000000000:10 (CommitmentTimeLock), " +
|
||||||
"HtlcAcceptedSuccessSecondLevel, 1 HtlcOfferedRemoteTimeout, " +
|
"0000000000000000000000000000000000000000000000000000000000000001:11 (HtlcAcceptedSuccessSecondLevel), " +
|
||||||
"1 WitnessKeyHash"
|
"0000000000000000000000000000000000000000000000000000000000000002:12 (HtlcOfferedRemoteTimeout), " +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003:13 (WitnessKeyHash)"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWeightEstimate tests that the estimated weight and number of CSVs/CLTVs
|
// TestWeightEstimate tests that the estimated weight and number of CSVs/CLTVs
|
||||||
@ -27,9 +29,12 @@ func TestWeightEstimate(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
var inputs []input.Input
|
var inputs []input.Input
|
||||||
for _, witnessType := range witnessTypes {
|
for i, witnessType := range witnessTypes {
|
||||||
inputs = append(inputs, input.NewBaseInput(
|
inputs = append(inputs, input.NewBaseInput(
|
||||||
&wire.OutPoint{}, witnessType,
|
&wire.OutPoint{
|
||||||
|
Hash: chainhash.Hash{byte(i)},
|
||||||
|
Index: uint32(i) + 10,
|
||||||
|
}, witnessType,
|
||||||
&input.SignDescriptor{}, 0,
|
&input.SignDescriptor{}, 0,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user