Skip to content

Commit

Permalink
itest+lntest: fix channel force close test
Browse files Browse the repository at this point in the history
  • Loading branch information
yyforyongyu committed Jul 10, 2024
1 parent 779b11d commit f79fea9
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 64 deletions.
112 changes: 55 additions & 57 deletions itest/lnd_channel_force_close_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,46 +259,47 @@ func channelForceClosureTest(ht *lntest.HarnessTest,

// The following restart is intended to ensure that outputs from the
// force close commitment transaction have been persisted once the
// transaction has been confirmed, but before the outputs are spendable
// (the "kindergarten" bucket.)
// transaction has been confirmed, but before the outputs are
// spendable.
ht.RestartNode(alice)

// Carol should offer her commit and anchor outputs to the sweeper.
sweepTxns := ht.AssertNumPendingSweeps(carol, 2)

// Find Carol's anchor sweep.
// Identify Carol's pending sweeps.
var carolAnchor, carolCommit = sweepTxns[0], sweepTxns[1]
if carolAnchor.AmountSat != uint32(anchorSize) {
carolAnchor, carolCommit = carolCommit, carolAnchor
}

// Mine a block to trigger Carol's sweeper to make decisions on the
// anchor sweeping.
ht.MineEmptyBlocks(1)

// Carol's sweep tx should be in the mempool already, as her output is
// not timelocked.
// not timelocked. This sweep tx should spend her to_local output as
// the anchor output is not economical to spend.
carolTx := ht.GetNumTxsFromMempool(1)[0]

// Carol's sweeping tx should have 2-input-1-output shape.
require.Len(ht, carolTx.TxIn, 2)
// Carol's sweeping tx should have 1-input-1-output shape.
require.Len(ht, carolTx.TxIn, 1)
require.Len(ht, carolTx.TxOut, 1)

// Calculate the total fee Carol paid.
totalFeeCarol := ht.CalculateTxFee(carolTx)

// If we have anchors, add an anchor resolution for carol.
op := fmt.Sprintf("%v:%v", carolAnchor.Outpoint.TxidStr,
carolAnchor.Outpoint.OutputIndex)
carolReports[op] = &lnrpc.Resolution{
ResolutionType: lnrpc.ResolutionType_ANCHOR,
Outcome: lnrpc.ResolutionOutcome_CLAIMED,
SweepTxid: carolTx.TxHash().String(),
AmountSat: anchorSize,
Outpoint: carolAnchor.Outpoint,
}

op = fmt.Sprintf("%v:%v", carolCommit.Outpoint.TxidStr,
// Carol's anchor report won't be created since it's uneconomical to
// sweep. We still keep the follow code snippet in case we want to
// bring it back.
//
// // If we have anchors, add an anchor resolution for carol.
// op := fmt.Sprintf("%v:%v", carolAnchor.Outpoint.TxidStr,
// carolAnchor.Outpoint.OutputIndex)
// carolReports[op] = &lnrpc.Resolution{
// ResolutionType: lnrpc.ResolutionType_ANCHOR,
// Outcome: lnrpc.ResolutionOutcome_CLAIMED,
// SweepTxid: carolTx.TxHash().String(),
// AmountSat: anchorSize,
// Outpoint: carolAnchor.Outpoint,
// }

op := fmt.Sprintf("%v:%v", carolCommit.Outpoint.TxidStr,
carolCommit.Outpoint.OutputIndex)
carolReports[op] = &lnrpc.Resolution{
ResolutionType: lnrpc.ResolutionType_COMMIT,
Expand Down Expand Up @@ -345,12 +346,12 @@ func channelForceClosureTest(ht *lntest.HarnessTest,
aliceBalance = forceClose.Channel.LocalBalance

// At this point, the nursery should show that the commitment
// output has 2 block left before its CSV delay expires. In
// output has 3 block left before its CSV delay expires. In
// total, we have mined exactly defaultCSV blocks, so the htlc
// outputs should also reflect that this many blocks have
// passed.
err = checkCommitmentMaturity(
forceClose, commCsvMaturityHeight, 2,
forceClose, commCsvMaturityHeight, 3,
)
if err != nil {
return err
Expand All @@ -369,9 +370,9 @@ func channelForceClosureTest(ht *lntest.HarnessTest,
}, defaultTimeout)
require.NoError(ht, err, "timeout while checking force closed channel")

// Generate an additional block, which should cause the CSV delayed
// output from the commitment txn to expire.
ht.MineEmptyBlocks(1)
// Generate two blocks, which should cause the CSV delayed output from
// the commitment txn to expire.
ht.MineEmptyBlocks(2)

// At this point, the CSV will expire in the next block, meaning that
// the output should be offered to the sweeper.
Expand All @@ -388,6 +389,7 @@ func channelForceClosureTest(ht *lntest.HarnessTest,
// Mine one block and the sweeping transaction should now be broadcast.
// So we fetch the node's mempool to ensure it has been properly
// broadcast.
// ht.MineBlocksAndAssertNumTxes(1, 1)
ht.MineEmptyBlocks(1)
sweepingTXID := ht.AssertNumTxsInMempool(1)[0]

Expand All @@ -410,16 +412,20 @@ func channelForceClosureTest(ht *lntest.HarnessTest,
AmountSat: uint64(aliceBalance),
}

// Add alice's anchor to our expected set of reports.
op = fmt.Sprintf("%v:%v", aliceAnchor.Outpoint.TxidStr,
aliceAnchor.Outpoint.OutputIndex)
aliceReports[op] = &lnrpc.Resolution{
ResolutionType: lnrpc.ResolutionType_ANCHOR,
Outcome: lnrpc.ResolutionOutcome_CLAIMED,
SweepTxid: sweepingTXID.String(),
Outpoint: aliceAnchor.Outpoint,
AmountSat: uint64(anchorSize),
}
// Alice's anchor report won't be created since it's uneconomical to
// sweep. We still keep the follow code snippet in case we want to
// bring it back.
//
// // Add alice's anchor to our expected set of reports.
// op = fmt.Sprintf("%v:%v", aliceAnchor.Outpoint.TxidStr,
// aliceAnchor.Outpoint.OutputIndex)
// aliceReports[op] = &lnrpc.Resolution{
// ResolutionType: lnrpc.ResolutionType_ANCHOR,
// Outcome: lnrpc.ResolutionOutcome_CLAIMED,
// SweepTxid: sweepingTXID.String(),
// Outpoint: aliceAnchor.Outpoint,
// AmountSat: uint64(anchorSize),
// }

// Check that we can find the commitment sweep in our set of known
// sweeps, using the simple transaction id ListSweeps output.
Expand Down Expand Up @@ -539,20 +545,19 @@ func channelForceClosureTest(ht *lntest.HarnessTest,

// Since Alice had numInvoices (6) htlcs extended to Carol before force
// closing, we expect Alice to broadcast an htlc timeout txn for each
// one.
ht.AssertNumPendingSweeps(alice, numInvoices)
// one. We also expect Alice to still have her anchor since it's not
// swept.
ht.AssertNumPendingSweeps(alice, numInvoices+1)

// Wait for them all to show up in the mempool
//
// NOTE: after restart, all the htlc timeout txns will be offered to
// the sweeper with `Immediate` set to true, so they won't be
// aggregated.
htlcTxIDs := ht.AssertNumTxsInMempool(numInvoices)
htlcTxIDs := ht.AssertNumTxsInMempool(1)

// Retrieve each htlc timeout txn from the mempool, and ensure it is
// well-formed. This entails verifying that each only spends from
// output, and that output is from the commitment txn.
numInputs := 2
// well-formed. The sweeping tx should spend all the htlc outputs.
//
// NOTE: We also add 1 output as the outgoing HTLC is swept using twice
// its value as its budget, so a wallet utxo is used.
numInputs := 6 + 1

// Construct a map of the already confirmed htlc timeout outpoints,
// that will count the number of times each is spent by the sweep txn.
Expand Down Expand Up @@ -653,7 +658,7 @@ func channelForceClosureTest(ht *lntest.HarnessTest,

// Generate a block that mines the htlc timeout txns. Doing so now
// activates the 2nd-stage CSV delayed outputs.
ht.MineBlocksAndAssertNumTxes(1, numInvoices)
ht.MineBlocksAndAssertNumTxes(1, 1)

// Alice is restarted here to ensure that she promptly moved the crib
// outputs to the kindergarten bucket after the htlc timeout txns were
Expand Down Expand Up @@ -688,10 +693,9 @@ func channelForceClosureTest(ht *lntest.HarnessTest,
}, defaultTimeout)
require.NoError(ht, err, "timeout while checking force closed channel")

// Generate a block that causes Alice to sweep the htlc outputs in the
// kindergarten bucket.
// Generate a block that causes Alice to sweep the htlc outputs.
ht.MineEmptyBlocks(1)
ht.AssertNumPendingSweeps(alice, numInvoices)
ht.AssertNumPendingSweeps(alice, numInvoices+1)

// Mine a block to trigger the sweep.
ht.MineEmptyBlocks(1)
Expand Down Expand Up @@ -847,12 +851,6 @@ func channelForceClosureTest(ht *lntest.HarnessTest,
carolExpectedBalance := btcutil.Amount(carolStartingBalance) +
pushAmt - totalFeeCarol

// In addition, if this is an anchor-enabled channel, further add the
// anchor size.
if lntest.CommitTypeHasAnchors(channelType) {
carolExpectedBalance += btcutil.Amount(anchorSize)
}

require.Equal(ht, carolExpectedBalance,
btcutil.Amount(carolBalResp.ConfirmedBalance),
"carol's balance is incorrect")
Expand Down
9 changes: 3 additions & 6 deletions lntest/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -1625,9 +1625,9 @@ func (h *HarnessTest) CleanupForceClose(hn *node.HarnessNode) {
h.AssertNumPendingForceClose(hn, 1)

// Mine enough blocks for the node to sweep its funds from the force
// closed channel. The commit sweep resolver is able to offer the input
// to the sweeper at defaulCSV-1, and broadcast the sweep tx once one
// more block is mined.
// closed channel. The commit sweep resolver is offers the input to the
// sweeper when it's force closed, and broadcast the sweep tx at
// defaulCSV-1.
//
// NOTE: we might empty blocks here as we don't know the exact number
// of blocks to mine. This may end up mining more blocks than needed.
Expand All @@ -1636,9 +1636,6 @@ func (h *HarnessTest) CleanupForceClose(hn *node.HarnessNode) {
// Assert there is one pending sweep.
h.AssertNumPendingSweeps(hn, 1)

// Mine a block to trigger the sweep.
h.MineEmptyBlocks(1)

// The node should now sweep the funds, clean up by mining the sweeping
// tx.
h.MineBlocksAndAssertNumTxes(1, 1)
Expand Down
3 changes: 2 additions & 1 deletion lntest/harness_miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ func (h *HarnessTest) mineTillForceCloseResolved(hn *node.HarnessNode) {
return nil
}, DefaultTimeout)

require.NoErrorf(h, err, "assert force close resolved timeout")
require.NoErrorf(h, err, "%s: assert force close resolved timeout",
hn.Name())
}

// AssertTxInMempool asserts a given transaction can be found in the mempool.
Expand Down

0 comments on commit f79fea9

Please sign in to comment.