Add tx fee information to `Balance::ClaimableOnChannelClose`
authorMatt Corallo <git@bluematt.me>
Fri, 29 Sep 2023 17:54:24 +0000 (17:54 +0000)
committerMatt Corallo <git@bluematt.me>
Tue, 14 Nov 2023 22:01:09 +0000 (22:01 +0000)
`Balance::ClaimableOnChannelClose` excludes the commitment
transaction fee, which makes it hard to use for current balance
calculation. Here we add it, setting the value to zero for inbound
channels (i.e. ones for which we don't pay the fee).

lightning/src/chain/channelmonitor.rs
lightning/src/ln/channel.rs
lightning/src/ln/monitor_tests.rs

index 3845328cda43f7a1c29e0cf3529b6d13b41bec7d..7f9d40b502c35c1d776c159ef8e95fa2c5054315 100644 (file)
@@ -578,6 +578,13 @@ pub enum Balance {
                /// The amount available to claim, in satoshis, excluding the on-chain fees which will be
                /// required to do so.
                amount_satoshis: u64,
+               /// The transaction fee we pay for the closing commitment transaction. This amount is not
+               /// included in the [`Balance::ClaimableOnChannelClose::amount_satoshis`] value.
+               ///
+               /// Note that if this channel is inbound (and thus our counterparty pays the commitment
+               /// transaction fee) this value will be zero. For [`ChannelMonitor`]s created prior to LDK
+               /// 0.0.117, the channel is always treated as outbound (and thus this value is never zero).
+               transaction_fee_satoshis: u64,
        },
        /// The channel has been closed, and the given balance is ours but awaiting confirmations until
        /// we consider it spendable.
@@ -861,6 +868,10 @@ pub(crate) struct ChannelMonitorImpl<Signer: WriteableEcdsaChannelSigner> {
        // of block connection between ChannelMonitors and the ChannelManager.
        funding_spend_seen: bool,
 
+       /// True if the commitment transaction fee is paid by us.
+       /// Added in 0.0.117.
+       holder_pays_commitment_tx_fee: Option<bool>,
+
        /// Set to `Some` of the confirmed transaction spending the funding input of the channel after
        /// reaching `ANTI_REORG_DELAY` confirmations.
        funding_spend_confirmed: Option<Txid>,
@@ -1086,6 +1097,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signe
                        (13, self.spendable_txids_confirmed, required_vec),
                        (15, self.counterparty_fulfilled_htlcs, required),
                        (17, self.initial_counterparty_commitment_info, option),
+                       (19, self.holder_pays_commitment_tx_fee, option),
                });
 
                Ok(())
@@ -1138,7 +1150,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
 
        pub(crate) fn new(secp_ctx: Secp256k1<secp256k1::All>, keys: Signer, shutdown_script: Option<Script>,
                          on_counterparty_tx_csv: u16, destination_script: &Script, funding_info: (OutPoint, Script),
-                         channel_parameters: &ChannelTransactionParameters,
+                         channel_parameters: &ChannelTransactionParameters, holder_pays_commitment_tx_fee: bool,
                          funding_redeemscript: Script, channel_value_satoshis: u64,
                          commitment_transaction_number_obscure_factor: u64,
                          initial_holder_commitment_tx: HolderCommitmentTransaction,
@@ -1228,6 +1240,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
 
                        onchain_tx_handler,
 
+                       holder_pays_commitment_tx_fee: Some(holder_pays_commitment_tx_fee),
                        lockdown_from_offchain: false,
                        holder_tx_signed: false,
                        funding_spend_seen: false,
@@ -2059,8 +2072,11 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                        }
                } else {
                        let mut claimable_inbound_htlc_value_sat = 0;
+                       let mut nondust_htlc_count = 0;
                        for (htlc, _, source) in us.current_holder_commitment_tx.htlc_outputs.iter() {
-                               if htlc.transaction_output_index.is_none() { continue; }
+                               if htlc.transaction_output_index.is_some() {
+                                       nondust_htlc_count += 1;
+                               } else { continue; }
                                if htlc.offered {
                                        let outbound_payment = match source {
                                                None => {
@@ -2090,6 +2106,11 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitor<Signer> {
                        }
                        res.push(Balance::ClaimableOnChannelClose {
                                amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat + claimable_inbound_htlc_value_sat,
+                               transaction_fee_satoshis: if us.holder_pays_commitment_tx_fee.unwrap_or(true) {
+                                       chan_utils::commit_tx_fee_sat(
+                                               us.current_holder_commitment_tx.feerate_per_kw, nondust_htlc_count,
+                                               us.onchain_tx_handler.channel_type_features())
+                               } else { 0 },
                        });
                }
 
@@ -4421,6 +4442,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                let mut spendable_txids_confirmed = Some(Vec::new());
                let mut counterparty_fulfilled_htlcs = Some(HashMap::new());
                let mut initial_counterparty_commitment_info = None;
+               let mut holder_pays_commitment_tx_fee = None;
                read_tlv_fields!(reader, {
                        (1, funding_spend_confirmed, option),
                        (3, htlcs_resolved_on_chain, optional_vec),
@@ -4431,6 +4453,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                        (13, spendable_txids_confirmed, optional_vec),
                        (15, counterparty_fulfilled_htlcs, option),
                        (17, initial_counterparty_commitment_info, option),
+                       (19, holder_pays_commitment_tx_fee, option),
                });
 
                // Monitors for anchor outputs channels opened in v0.0.116 suffered from a bug in which the
@@ -4489,6 +4512,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
 
                        lockdown_from_offchain,
                        holder_tx_signed,
+                       holder_pays_commitment_tx_fee,
                        funding_spend_seen: funding_spend_seen.unwrap(),
                        funding_spend_confirmed,
                        confirmed_commitment_tx_counterparty_output,
@@ -4720,7 +4744,7 @@ mod tests {
                let monitor = ChannelMonitor::new(Secp256k1::new(), keys,
                        Some(ShutdownScript::new_p2wpkh_from_pubkey(shutdown_pubkey).into_inner()), 0, &Script::new(),
                        (OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, Script::new()),
-                       &channel_parameters, Script::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()),
+                       &channel_parameters, true, Script::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()),
                        best_block, dummy_key);
 
                let mut htlcs = preimages_slice_to_htlcs!(preimages[0..10]);
index ff78c2636f3ce15e43c484d27bad6bcf2519c087..26b343a186e9cd48e027fe4afbc0ada05e6d37cb 100644 (file)
@@ -2711,7 +2711,7 @@ impl<SP: Deref> Channel<SP> where
                let channel_monitor = ChannelMonitor::new(self.context.secp_ctx.clone(), monitor_signer,
                                                          shutdown_script, self.context.get_holder_selected_contest_delay(),
                                                          &self.context.destination_script, (funding_txo, funding_txo_script),
-                                                         &self.context.channel_transaction_parameters,
+                                                         &self.context.channel_transaction_parameters, self.context.is_outbound(),
                                                          funding_redeemscript.clone(), self.context.channel_value_satoshis,
                                                          obscure_factor,
                                                          holder_commitment_tx, best_block, self.context.counterparty_node_id);
@@ -6875,7 +6875,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
                let channel_monitor = ChannelMonitor::new(self.context.secp_ctx.clone(), monitor_signer,
                                                          shutdown_script, self.context.get_holder_selected_contest_delay(),
                                                          &self.context.destination_script, (funding_txo, funding_txo_script.clone()),
-                                                         &self.context.channel_transaction_parameters,
+                                                         &self.context.channel_transaction_parameters, self.context.is_outbound(),
                                                          funding_redeemscript.clone(), self.context.channel_value_satoshis,
                                                          obscure_factor,
                                                          holder_commitment_tx, best_block, self.context.counterparty_node_id);
index e432b8bc0121b6afd9b3f5c7e77f5e73b3fc97dc..7dfc7fba8e95014a99e129c652ed74aa6e67df1f 100644 (file)
@@ -184,10 +184,11 @@ fn do_chanmon_claim_value_coop_close(anchors: bool) {
        let commitment_tx_fee = chan_feerate * chan_utils::commitment_tx_base_weight(&channel_type_features) / 1000;
        let anchor_outputs_value = if anchors { channel::ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 };
        assert_eq!(vec![Balance::ClaimableOnChannelClose {
-                       amount_satoshis: 1_000_000 - 1_000 - commitment_tx_fee - anchor_outputs_value
+                       amount_satoshis: 1_000_000 - 1_000 - commitment_fee - anchor_outputs_value,
+                       transaction_fee_satoshis: commitment_fee,
                }],
                nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
-       assert_eq!(vec![Balance::ClaimableOnChannelClose { amount_satoshis: 1_000, }],
+       assert_eq!(vec![Balance::ClaimableOnChannelClose { amount_satoshis: 1_000, transaction_fee_satoshis: 0 }],
                nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
 
        nodes[0].node.close_channel(&chan_id, &nodes[1].node.get_our_node_id()).unwrap();
@@ -385,10 +386,12 @@ fn do_test_claim_value_force_close(anchors: bool, prev_commitment_tx: bool) {
        let anchor_outputs_value = if anchors { 2 * channel::ANCHOR_OUTPUT_VALUE_SATOSHI } else { 0 };
        assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
                        amount_satoshis: 1_000_000 - 3_000 - 4_000 - 1_000 - 3 - commitment_tx_fee - anchor_outputs_value,
+                       transaction_fee_satoshis: commitment_tx_fee,
                }, sent_htlc_balance.clone(), sent_htlc_timeout_balance.clone()]),
                sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
        assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
                        amount_satoshis: 1_000,
+                       transaction_fee_satoshis: 0,
                }, received_htlc_balance.clone(), received_htlc_timeout_balance.clone()]),
                sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
 
@@ -435,6 +438,7 @@ fn do_test_claim_value_force_close(anchors: bool, prev_commitment_tx: bool) {
                                3 - // The dust HTLC value in satoshis
                                commitment_tx_fee - // The commitment transaction fee with two HTLC outputs
                                anchor_outputs_value, // The anchor outputs value in satoshis
+                       transaction_fee_satoshis: commitment_tx_fee,
                }, sent_htlc_timeout_balance.clone()];
        if !prev_commitment_tx {
                a_expected_balances.push(sent_htlc_balance.clone());
@@ -443,6 +447,7 @@ fn do_test_claim_value_force_close(anchors: bool, prev_commitment_tx: bool) {
                sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
        assert_eq!(vec![Balance::ClaimableOnChannelClose {
                        amount_satoshis: 1_000 + 3_000 + 4_000,
+                       transaction_fee_satoshis: 0,
                }],
                nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
 
@@ -920,15 +925,17 @@ fn test_no_preimage_inbound_htlc_balances() {
        // Both A and B will have an HTLC that's claimable on timeout and one that's claimable if they
        // receive the preimage. These will remain the same through the channel closure and until the
        // HTLC output is spent.
-
+       let commitment_tx_fee = chan_feerate *
+               (chan_utils::commitment_tx_base_weight(&channel_type_features) + 2 * chan_utils::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000;
        assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
-                       amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
-                               (chan_utils::commitment_tx_base_weight(&channel_type_features) + 2 * chan_utils::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
+                       amount_satoshis: 1_000_000 - 500_000 - 10_000 - commitment_tx_fee,
+                       transaction_fee_satoshis: commitment_tx_fee,
                }, a_received_htlc_balance.clone(), a_sent_htlc_balance.clone()]),
                sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
 
        assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
                        amount_satoshis: 500_000 - 20_000,
+                       transaction_fee_satoshis: 0,
                }, b_received_htlc_balance.clone(), b_sent_htlc_balance.clone()]),
                sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
 
@@ -1207,6 +1214,7 @@ fn do_test_revoked_counterparty_commitment_balances(anchors: bool, confirm_htlc_
        // lists the two on-chain timeout-able HTLCs as claimable balances.
        assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
                        amount_satoshis: 100_000 - 5_000 - 4_000 - 3 - 2_000 + 3_000,
+                       transaction_fee_satoshis: 0,
                }, Balance::MaybeTimeoutClaimableHTLC {
                        amount_satoshis: 2_000,
                        claimable_height: missing_htlc_cltv_timeout,
@@ -1760,6 +1768,7 @@ fn do_test_revoked_counterparty_aggregated_claims(anchors: bool) {
 
        assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
                        amount_satoshis: 100_000 - 4_000 - 3_000,
+                       transaction_fee_satoshis: 0,
                }, Balance::MaybeTimeoutClaimableHTLC {
                        amount_satoshis: 4_000,
                        claimable_height: htlc_cltv_timeout,