From e41375966988526b6b9da7ae186567111e56a598 Mon Sep 17 00:00:00 2001 From: ziggie Date: Tue, 30 Jul 2024 15:13:02 +0200 Subject: [PATCH 1/3] contractcourt: Always register anchors with sweeper. Even if no HTLCs are at stake we are going to register the anchor outputs with the sweeper subsystem with a default high deadline. We need to do this, because otherwise we are not able to bump the fee of the closing transaction manually. --- contractcourt/channel_arbitrator.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index d544e989c..8add61ce6 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -40,6 +40,12 @@ const ( // arbitratorBlockBufferSize is the size of the buffer we give to each // channel arbitrator. arbitratorBlockBufferSize = 20 + + // AnchorOutputValue is the output value for the anchor output of an + // anchor channel. + // See BOLT 03 for more details: + // https://github.com/lightning/bolts/blob/master/03-transactions.md + AnchorOutputValue = btcutil.Amount(330) ) // WitnessSubscription represents an intent to be notified once new witnesses @@ -1313,13 +1319,14 @@ func (c *ChannelArbitrator) sweepAnchors(anchors *lnwallet.AnchorResolutions, } // If we cannot find a deadline, it means there's no HTLCs at - // stake, which means we can relax our anchor sweeping as we - // don't have any time sensitive outputs to sweep. + // stake, which means we can relax our anchor sweeping + // conditions as we don't have any time sensitive outputs to + // sweep. However we need to register the anchor output with the + // sweeper so we are later able to bump the close fee. if deadline.IsNone() { log.Infof("ChannelArbitrator(%v): no HTLCs at stake, "+ - "skipped anchor CPFP", c.cfg.ChanPoint) - - return nil + "sweeping anchor with default deadline", + c.cfg.ChanPoint) } witnessType := input.CommitmentAnchor @@ -1357,10 +1364,13 @@ func (c *ChannelArbitrator) sweepAnchors(anchors *lnwallet.AnchorResolutions, // Calculate the budget based on the value under protection, // which is the sum of all HTLCs on this commitment subtracted // by their budgets. + // The anchor output in itself has a small output value of 330 + // sats so we also include it in the budget to pay for the + // cpfp transaction. budget := calculateBudget( value, c.cfg.Budget.AnchorCPFPRatio, c.cfg.Budget.AnchorCPFP, - ) + ) + AnchorOutputValue log.Infof("ChannelArbitrator(%v): offering anchor from %s "+ "commitment %v to sweeper with deadline=%v, budget=%v", From cac5b32d883697f218787972afe1a1508e1fc818 Mon Sep 17 00:00:00 2001 From: ziggie Date: Mon, 29 Jul 2024 16:53:41 +0200 Subject: [PATCH 2/3] itest: adapt itest for the new anchor behavior. Now we also register anchors when no HTLCs are at stake. --- itest/lnd_sweep_test.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/itest/lnd_sweep_test.go b/itest/lnd_sweep_test.go index b95bc0513..5c4e63f3e 100644 --- a/itest/lnd_sweep_test.go +++ b/itest/lnd_sweep_test.go @@ -73,9 +73,11 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) { htlcBudget := htlcValue.MulF64(contractcourt.DefaultBudgetRatio) // cpfpBudget is the budget used to sweep the CPFP anchor. + // In addition to the htlc amount to protect we also need to include + // the anchor amount itself for the budget. cpfpBudget := (htlcValue - htlcBudget).MulF64( contractcourt.DefaultBudgetRatio, - ) + ) + contractcourt.AnchorOutputValue // Create a preimage, that will be held by Carol. var preimage lntypes.Preimage @@ -488,9 +490,11 @@ func testSweepCPFPAnchorIncomingTimeout(ht *lntest.HarnessTest) { htlcBudget := htlcValue.MulF64(contractcourt.DefaultBudgetRatio) // cpfpBudget is the budget used to sweep the CPFP anchor. + // In addition to the htlc amount to protect we also need to include + // the anchor amount itself for the budget. cpfpBudget := (htlcValue - htlcBudget).MulF64( contractcourt.DefaultBudgetRatio, - ) + ) + contractcourt.AnchorOutputValue // Carol should have one incoming HTLC on channel Bob -> Carol. ht.AssertIncomingHTLCActive(carol, bcChanPoint, payHash[:]) @@ -1342,9 +1346,13 @@ func testSweepCommitOutputAndAnchor(ht *lntest.HarnessTest) { // PendingChannels RPC under the waiting close section. ht.AssertChannelWaitingClose(alice, chanPoint) - // We should see neither Alice or Bob has any pending sweeps as there - // are no time-sensitive HTLCs. - ht.AssertNumPendingSweeps(alice, 0) + // Alice should see 2 anchor sweeps for the local and remote commitment. + // Even without HTLCs at stake the anchors are registered with the + // sweeper subsytem. + ht.AssertNumPendingSweeps(alice, 2) + + // Bob did not force close the channel therefore he should have no + // pending sweeps. ht.AssertNumPendingSweeps(bob, 0) // Mine a block to confirm Alice's force closing tx. Once it's From da7b95d4a4960062462cb82eaebabd226de5e3ba Mon Sep 17 00:00:00 2001 From: ziggie Date: Mon, 29 Jul 2024 11:21:42 +0200 Subject: [PATCH 3/3] docs: add release-notes. --- docs/release-notes/release-notes-0.18.3.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes/release-notes-0.18.3.md b/docs/release-notes/release-notes-0.18.3.md index 6672a6e58..5184e9671 100644 --- a/docs/release-notes/release-notes-0.18.3.md +++ b/docs/release-notes/release-notes-0.18.3.md @@ -39,6 +39,10 @@ * The fee limit for payments [was made compatible](https://github.com/lightningnetwork/lnd/pull/8941) with inbound fees. + +* [Fixed](https://github.com/lightningnetwork/lnd/pull/8946) a case where +bumping an anchor channel closing was not possible when no HTLCs were on the +commitment when the channel was force closed. # New Features ## Functional Enhancements