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