itest: refactor testMultiHopLocalForceCloseOnChainHtlcTimeout
This commit is contained in:
4 files changed
+66
-132
No files matched your search
+1
-1
@@ -809,7 +809,7 @@ func (h *HarnessTest) CloseChannel(hn *node.HarnessNode,
|
||||
|
||||
stream, _ := h.CloseChannelAssertPending(hn, cp, false)
|
||||
|
||||
return h.assertChannelCoopClosed(hn, cp, false, stream)
|
||||
return h.AssertStreamChannelCoopClosed(hn, cp, false, stream)
|
||||
}
|
||||
|
||||
// ForceCloseChannel attempts to force close a non-anchored channel identified
|
||||
|
||||
@@ -454,9 +454,15 @@ func (h *HarnessTest) AssertNumPendingForceClose(hn *node.HarnessNode,
|
||||
return channels
|
||||
}
|
||||
|
||||
// assertChannelCoopClosed asserts that the channel is properly cleaned up
|
||||
// after initiating a cooperative close.
|
||||
func (h *HarnessTest) assertChannelCoopClosed(hn *node.HarnessNode,
|
||||
// AssertStreamChannelCoopClosed reads an update from the close channel client
|
||||
// stream and asserts that the mempool state and node's topology match a coop
|
||||
// close. In specific,
|
||||
// - assert the channel is waiting close and has the expected ChanStatusFlags.
|
||||
// - assert the mempool has the closing txes and anchor sweeps.
|
||||
// - mine a block and assert the closing txid is mined.
|
||||
// - assert the node has zero waiting close channels.
|
||||
// - assert the node has seen the channel close update.
|
||||
func (h *HarnessTest) AssertStreamChannelCoopClosed(hn *node.HarnessNode,
|
||||
cp *lnrpc.ChannelPoint, anchors bool,
|
||||
stream rpc.CloseChanClient) *chainhash.Hash {
|
||||
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package itest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||
"github.com/lightningnetwork/lnd/lntemp"
|
||||
"github.com/lightningnetwork/lnd/lntemp/node"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -18,22 +15,16 @@ import (
|
||||
// commitment on-chain early, then it eventually recognizes this HTLC as one
|
||||
// that's timed out. At this point, the node should timeout the HTLC using the
|
||||
// HTLC timeout transaction, then cancel it backwards as normal.
|
||||
func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
||||
t *harnessTest, alice, bob *lntest.HarnessNode, c lnrpc.CommitmentType,
|
||||
zeroConf bool) {
|
||||
|
||||
ctxb := context.Background()
|
||||
func testMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntemp.HarnessTest,
|
||||
alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) {
|
||||
|
||||
// First, we'll create a three hop network: Alice -> Bob -> Carol, with
|
||||
// Carol refusing to actually settle or directly cancel any HTLC's
|
||||
// self.
|
||||
aliceChanPoint, bobChanPoint, carol := createThreeHopNetworkOld(
|
||||
t, net, alice, bob, true, c, zeroConf,
|
||||
aliceChanPoint, bobChanPoint, carol := createThreeHopNetwork(
|
||||
ht, alice, bob, true, c, zeroConf,
|
||||
)
|
||||
|
||||
// Clean up carol's node when the test finishes.
|
||||
defer shutdownAndAssert(net, t, carol)
|
||||
|
||||
// With our channels set up, we'll then send a single HTLC from Alice
|
||||
// to Carol. As Carol is in hodl mode, she won't settle this HTLC which
|
||||
// opens up the base for out tests.
|
||||
@@ -41,59 +32,39 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
||||
finalCltvDelta = 40
|
||||
htlcAmt = btcutil.Amount(300_000)
|
||||
)
|
||||
ctx, cancel := context.WithCancel(ctxb)
|
||||
defer cancel()
|
||||
|
||||
// We'll now send a single HTLC across our multi-hop network.
|
||||
carolPubKey := carol.PubKey[:]
|
||||
payHash := makeFakePayHash(t)
|
||||
_, err := alice.RouterClient.SendPaymentV2(
|
||||
ctx, &routerrpc.SendPaymentRequest{
|
||||
Dest: carolPubKey,
|
||||
Amt: int64(htlcAmt),
|
||||
PaymentHash: payHash,
|
||||
FinalCltvDelta: finalCltvDelta,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
payHash := ht.Random32Bytes()
|
||||
req := &routerrpc.SendPaymentRequest{
|
||||
Dest: carolPubKey,
|
||||
Amt: int64(htlcAmt),
|
||||
PaymentHash: payHash,
|
||||
FinalCltvDelta: finalCltvDelta,
|
||||
TimeoutSeconds: 60,
|
||||
FeeLimitMsat: noFeeLimitMsat,
|
||||
}
|
||||
alice.RPC.SendPayment(req)
|
||||
|
||||
// Once the HTLC has cleared, all channels in our mini network should
|
||||
// have the it locked in.
|
||||
nodes := []*lntest.HarnessNode{alice, bob, carol}
|
||||
err = wait.NoError(func() error {
|
||||
return assertActiveHtlcs(nodes, payHash)
|
||||
}, defaultTimeout)
|
||||
require.NoError(t.t, err)
|
||||
ht.AssertActiveHtlcs(alice, payHash)
|
||||
ht.AssertActiveHtlcs(bob, payHash)
|
||||
ht.AssertActiveHtlcs(carol, payHash)
|
||||
|
||||
// Increase the fee estimate so that the following force close tx will
|
||||
// be cpfp'ed.
|
||||
net.SetFeeEstimate(30000)
|
||||
ht.SetFeeEstimate(30000)
|
||||
|
||||
// Now that all parties have the HTLC locked in, we'll immediately
|
||||
// force close the Bob -> Carol channel. This should trigger contract
|
||||
// resolution mode for both of them.
|
||||
hasAnchors := commitTypeHasAnchors(c)
|
||||
closeTx := closeChannelAndAssertType(
|
||||
t, net, bob, bobChanPoint, hasAnchors, true,
|
||||
stream, _ := ht.CloseChannelAssertPending(bob, bobChanPoint, true)
|
||||
closeTx := ht.AssertStreamChannelForceClosed(
|
||||
bob, bobChanPoint, hasAnchors, stream,
|
||||
)
|
||||
|
||||
// At this point, Bob should have a pending force close channel as he
|
||||
// just went to chain.
|
||||
err = waitForNumChannelPendingForceClose(
|
||||
bob, 1, func(c *lnrpcForceCloseChannel) error {
|
||||
if c.LimboBalance == 0 {
|
||||
return fmt.Errorf("bob should have nonzero "+
|
||||
"limbo balance instead has: %v",
|
||||
c.LimboBalance)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
// If the channel closed has anchors, we should expect to see a sweep
|
||||
// transaction for Carol's anchor.
|
||||
htlcOutpoint := wire.OutPoint{Hash: *closeTx, Index: 0}
|
||||
@@ -101,8 +72,7 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
||||
if hasAnchors {
|
||||
htlcOutpoint.Index = 2
|
||||
bobCommitOutpoint.Index = 3
|
||||
_, err = waitForTxInMempool(net.Miner.Client, minerMempoolTimeout)
|
||||
require.NoError(t.t, err)
|
||||
ht.Miner.AssertNumTxsInMempool(1)
|
||||
}
|
||||
|
||||
// Before the HTLC times out, we'll need to assert that Bob broadcasts a
|
||||
@@ -114,15 +84,14 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
||||
// CSV expires and the commitment was already mined inside
|
||||
// closeChannelAndAssertType(), so mine one block less than
|
||||
// defaultCSV in order to perform mempool assertions.
|
||||
_, err = net.Miner.Client.Generate(defaultCSV - 1)
|
||||
require.NoError(t.t, err)
|
||||
ht.MineBlocksAssertNodesSync(defaultCSV - 1)
|
||||
|
||||
commitSweepTx := assertSpendingTxInMempool(
|
||||
t, net.Miner.Client, minerMempoolTimeout,
|
||||
commitSweepTx := ht.Miner.AssertOutpointInMempool(
|
||||
bobCommitOutpoint,
|
||||
)
|
||||
blocks := mineBlocks(t, net, 1, 1)
|
||||
assertTxInBlock(t, blocks[0], &commitSweepTx)
|
||||
txid := commitSweepTx.TxHash()
|
||||
block := ht.Miner.MineBlocksAndAssertNumTxes(1, 1)[0]
|
||||
ht.Miner.AssertTxInBlock(block, &txid)
|
||||
}
|
||||
|
||||
// We'll now mine enough blocks for the HTLC to expire. After this, Bob
|
||||
@@ -133,103 +102,62 @@ func testMultiHopLocalForceCloseOnChainHtlcTimeout(net *lntest.NetworkHarness,
|
||||
// commit sweep.
|
||||
numBlocks -= defaultCSV
|
||||
}
|
||||
_, err = net.Miner.Client.Generate(numBlocks)
|
||||
require.NoError(t.t, err)
|
||||
ht.MineBlocksAssertNodesSync(numBlocks)
|
||||
|
||||
// Bob's pending channel report should show that he has a single HTLC
|
||||
// that's now in stage one.
|
||||
err = waitForNumChannelPendingForceClose(
|
||||
bob, 1, func(c *lnrpcForceCloseChannel) error {
|
||||
if len(c.PendingHtlcs) != 1 {
|
||||
return fmt.Errorf("bob should have pending " +
|
||||
"htlc but doesn't")
|
||||
}
|
||||
|
||||
if c.PendingHtlcs[0].Stage != 1 {
|
||||
return fmt.Errorf("bob's htlc should have "+
|
||||
"advanced to the first stage: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
ht.AssertNumHTLCsAndStage(bob, bobChanPoint, 1, 1)
|
||||
|
||||
// We should also now find a transaction in the mempool, as Bob should
|
||||
// have broadcast his second layer timeout transaction.
|
||||
timeoutTx := assertSpendingTxInMempool(
|
||||
t, net.Miner.Client, minerMempoolTimeout, htlcOutpoint,
|
||||
)
|
||||
timeoutTx := ht.Miner.AssertOutpointInMempool(htlcOutpoint).TxHash()
|
||||
|
||||
// Next, we'll mine an additional block. This should serve to confirm
|
||||
// the second layer timeout transaction.
|
||||
block := mineBlocks(t, net, 1, 1)[0]
|
||||
assertTxInBlock(t, block, &timeoutTx)
|
||||
block := ht.Miner.MineBlocksAndAssertNumTxes(1, 1)[0]
|
||||
ht.Miner.AssertTxInBlock(block, &timeoutTx)
|
||||
|
||||
// With the second layer timeout transaction confirmed, Bob should have
|
||||
// canceled backwards the HTLC that carol sent.
|
||||
nodes = []*lntest.HarnessNode{alice}
|
||||
err = wait.NoError(func() error {
|
||||
return assertNumActiveHtlcs(nodes, 0)
|
||||
}, defaultTimeout)
|
||||
require.NoError(t.t, err)
|
||||
ht.AssertNumActiveHtlcs(alice, 0)
|
||||
|
||||
// Additionally, Bob should now show that HTLC as being advanced to the
|
||||
// second stage.
|
||||
err = waitForNumChannelPendingForceClose(
|
||||
bob, 1, func(c *lnrpcForceCloseChannel) error {
|
||||
if len(c.PendingHtlcs) != 1 {
|
||||
return fmt.Errorf("bob should have pending " +
|
||||
"htlc but doesn't")
|
||||
}
|
||||
|
||||
if c.PendingHtlcs[0].Stage != 2 {
|
||||
return fmt.Errorf("bob's htlc should have "+
|
||||
"advanced to the second stage: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
ht.AssertNumHTLCsAndStage(bob, bobChanPoint, 1, 2)
|
||||
|
||||
// Bob should now broadcast a transaction that sweeps certain inputs
|
||||
// depending on the commitment type. We'll need to mine some blocks
|
||||
// before the broadcast is possible.
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
resp, err := bob.PendingChannels(ctxt, &lnrpc.PendingChannelsRequest{})
|
||||
require.NoError(t.t, err)
|
||||
resp := bob.RPC.PendingChannels()
|
||||
|
||||
require.Len(t.t, resp.PendingForceClosingChannels, 1)
|
||||
require.Len(ht, resp.PendingForceClosingChannels, 1)
|
||||
forceCloseChan := resp.PendingForceClosingChannels[0]
|
||||
require.Len(t.t, forceCloseChan.PendingHtlcs, 1)
|
||||
require.Len(ht, forceCloseChan.PendingHtlcs, 1)
|
||||
pendingHtlc := forceCloseChan.PendingHtlcs[0]
|
||||
require.Positive(t.t, pendingHtlc.BlocksTilMaturity)
|
||||
require.Positive(ht, pendingHtlc.BlocksTilMaturity)
|
||||
numBlocks = uint32(pendingHtlc.BlocksTilMaturity)
|
||||
|
||||
_, err = net.Miner.Client.Generate(numBlocks)
|
||||
require.NoError(t.t, err)
|
||||
ht.MineBlocksAssertNodesSync(numBlocks)
|
||||
|
||||
// Now that the CSV/CLTV timelock has expired, the transaction should
|
||||
// either only sweep the HTLC timeout transaction, or sweep both the
|
||||
// HTLC timeout transaction and Bob's commit output depending on the
|
||||
// commitment type.
|
||||
htlcTimeoutOutpoint := wire.OutPoint{Hash: timeoutTx, Index: 0}
|
||||
expectedInputsSwept := []wire.OutPoint{htlcTimeoutOutpoint}
|
||||
sweepTx := ht.Miner.AssertOutpointInMempool(
|
||||
htlcTimeoutOutpoint,
|
||||
).TxHash()
|
||||
if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE {
|
||||
expectedInputsSwept = append(expectedInputsSwept, bobCommitOutpoint)
|
||||
ht.Miner.AssertOutpointInMempool(bobCommitOutpoint)
|
||||
}
|
||||
sweepTx := assertSpendingTxInMempool(
|
||||
t, net.Miner.Client, minerMempoolTimeout, expectedInputsSwept...,
|
||||
)
|
||||
block = mineBlocks(t, net, 1, 1)[0]
|
||||
assertTxInBlock(t, block, &sweepTx)
|
||||
|
||||
block = ht.Miner.MineBlocksAndAssertNumTxes(1, 1)[0]
|
||||
ht.Miner.AssertTxInBlock(block, &sweepTx)
|
||||
|
||||
// At this point, Bob should no longer show any channels as pending
|
||||
// close.
|
||||
err = waitForNumChannelPendingForceClose(bob, 0, nil)
|
||||
require.NoError(t.t, err)
|
||||
ht.AssertNumPendingForceClose(bob, 0)
|
||||
|
||||
// Coop close, no anchors.
|
||||
closeChannelAndAssertType(t, net, alice, aliceChanPoint, false, false)
|
||||
ht.CloseChannel(alice, aliceChanPoint)
|
||||
}
|
||||
@@ -39,12 +39,12 @@ func testMultiHopHtlcClaims(ht *lntemp.HarnessTest) {
|
||||
name: "receiver chain claim",
|
||||
test: testMultiHopReceiverChainClaim,
|
||||
},
|
||||
// {
|
||||
// // bob: outgoing our commit watch and see timeout
|
||||
// // carol: incoming their commit watch and see timeout
|
||||
// name: "local force close on-chain htlc timeout",
|
||||
// test: testMultiHopLocalForceCloseOnChainHtlcTimeout,
|
||||
// },
|
||||
{
|
||||
// bob: outgoing our commit watch and see timeout
|
||||
// carol: incoming their commit watch and see timeout
|
||||
name: "local force close on-chain htlc timeout",
|
||||
test: testMultiHopLocalForceCloseOnChainHtlcTimeout,
|
||||
},
|
||||
// {
|
||||
// // bob: outgoing their commit watch and see timeout
|
||||
// // carol: incoming our commit watch and see timeout
|
||||
|
||||
Reference in new issue
Block a user