itest: refactor testMultiHopLocalForceCloseOnChainHtlcTimeout

This commit is contained in:
yyforyongyu committed 2022-10-27 01:11:32 +08:00
1 parent d51f98efc6
commit 6b5f4f407c
4 files changed
+66 -132

No files matched your search

+1 -1
View File
@@ -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
+9 -3
View File
@@ -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)
}
+6 -6
View File
@@ -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