]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Add tx fee information to `Balance::ClaimableOnChannelClose`
authorMatt Corallo <git@bluematt.me>
Fri, 29 Sep 2023 17:54:24 +0000 (17:54 +0000)
committerDuncan Dean <git@dunxen.dev>
Tue, 13 Aug 2024 11:26:57 +0000 (13:26 +0200)
`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 fb2f11458539a699d794078684c2c8725ac481db..e12873d2f799c377222fb1cc752939cd8e292beb 100644 (file)
@@ -622,6 +622,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.124, 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.
@@ -912,6 +919,10 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
        // 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.124.
+       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>,
@@ -1160,6 +1171,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
                        (17, self.initial_counterparty_commitment_info, option),
                        (19, self.channel_id, required),
                        (21, self.balances_empty_height, option),
+                       (23, self.holder_pays_commitment_tx_fee, option),
                });
 
                Ok(())
@@ -1261,7 +1273,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
 
        pub(crate) fn new(secp_ctx: Secp256k1<secp256k1::All>, keys: Signer, shutdown_script: Option<ScriptBuf>,
                          on_counterparty_tx_csv: u16, destination_script: &Script, funding_info: (OutPoint, ScriptBuf),
-                         channel_parameters: &ChannelTransactionParameters,
+                         channel_parameters: &ChannelTransactionParameters, holder_pays_commitment_tx_fee: bool,
                          funding_redeemscript: ScriptBuf, channel_value_satoshis: u64,
                          commitment_transaction_number_obscure_factor: u64,
                          initial_holder_commitment_tx: HolderCommitmentTransaction,
@@ -1353,6 +1365,7 @@ impl<Signer: EcdsaChannelSigner> 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,
@@ -2306,8 +2319,11 @@ impl<Signer: EcdsaChannelSigner> 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 => {
@@ -2337,6 +2353,11 @@ impl<Signer: EcdsaChannelSigner> 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 },
                        });
                }
 
@@ -4743,6 +4764,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                let mut initial_counterparty_commitment_info = None;
                let mut balances_empty_height = None;
                let mut channel_id = 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),
@@ -4755,6 +4777,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
                        (17, initial_counterparty_commitment_info, option),
                        (19, channel_id, option),
                        (21, balances_empty_height, option),
+                       (23, holder_pays_commitment_tx_fee, option),
                });
 
                // `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. If we have both
@@ -4824,6 +4847,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,
@@ -5063,7 +5087,7 @@ mod tests {
                let monitor = ChannelMonitor::new(Secp256k1::new(), keys,
                        Some(ShutdownScript::new_p2wpkh_from_pubkey(shutdown_pubkey).into_inner()), 0, &ScriptBuf::new(),
                        (OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, ScriptBuf::new()),
-                       &channel_parameters, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()),
+                       &channel_parameters, true, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()),
                        best_block, dummy_key, channel_id);
 
                let mut htlcs = preimages_slice_to_htlcs!(preimages[0..10]);
@@ -5311,7 +5335,7 @@ mod tests {
                let monitor = ChannelMonitor::new(Secp256k1::new(), keys,
                        Some(ShutdownScript::new_p2wpkh_from_pubkey(shutdown_pubkey).into_inner()), 0, &ScriptBuf::new(),
                        (OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, ScriptBuf::new()),
-                       &channel_parameters, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()),
+                       &channel_parameters, true, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()),
                        best_block, dummy_key, channel_id);
 
                let chan_id = monitor.inner.lock().unwrap().channel_id();
index 7a03fe7853608e4b2b04c832b6cdd5f95753506a..daa7b63dfb49469dd9c235d4c7b4b690421424ad 100644 (file)
@@ -7861,7 +7861,7 @@ impl<SP: Deref> OutboundV1Channel<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),
-                                                         &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, self.context.channel_id());
@@ -8173,7 +8173,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, self.context.channel_id());
index 486c0bdedcb3356de4354e8187cb50a3ae27ac63..757ca41e1b902f2abdd8c4017122bf84f72dcc43 100644 (file)
@@ -240,10 +240,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_tx_fee - anchor_outputs_value,
+                       transaction_fee_satoshis: commitment_tx_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();
@@ -441,10 +442,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()));
 
@@ -491,6 +494,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());
@@ -499,6 +503,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());
 
@@ -977,15 +982,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()));
 
@@ -1264,6 +1271,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,
@@ -1817,6 +1825,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,