From 83229a8446e876d5effed72182fac6bf55ccb244 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 14 Nov 2023 22:19:19 +0000 Subject: [PATCH] (XXX: more commit message) Include routed msat balances in `Balance::ClaimableOnChannelClose` If we're gonna push users towards using `Balance` to determine their current balances, we really need to provide more information, including msat balances. Here we add rounded-out msat balances to the pre-close balance information --- lightning/src/chain/channelmonitor.rs | 81 ++++++++++++++++++++++----- lightning/src/ln/monitor_tests.rs | 44 ++++++++++++++- 2 files changed, 109 insertions(+), 16 deletions(-) diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 7f9d40b5..a80540df 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -585,6 +585,32 @@ pub enum Balance { /// 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 amount of millisatoshis which has been burned to fees from HTLCs which are outbound + /// from us and are related to a payment which was sent by us. This is the sum of the + /// millisatoshis part of all HTLCs which are otherwise represented by + /// [`Balance::MaybeTimeoutClaimableHTLC`] with their + /// [`Balance::MaybeTimeoutClaimableHTLC::outbound_payment`] flag set, as well as any dust + /// HTLCs which would otherwise be represented the same. + outbound_payment_htlc_rounded_msat: u64, + /// The amount of millisatoshis which has been burned to fees from HTLCs which are outbound + /// from us and are related to a forwarded HTLC. This is the sum of the millisatoshis part + /// of all HTLCs which are otherwise represented by [`Balance::MaybeTimeoutClaimableHTLC`] + /// with their [`Balance::MaybeTimeoutClaimableHTLC::outbound_payment`] flag *not* set, as + /// well as any dust HTLCs which would otherwise be represented the same. + outbound_forwarded_htlc_rounded_msat: u64, + /// The amount of millisatoshis which has been burned to fees from HTLCs which are inbound + /// to us and for which we know the preimage. This is the sum of the millisatoshis part of + /// all HTLCs which would be represented by [`Balance::ContentiousClaimable`] on channel + /// close, but who's current value is included in + /// [`Balance::ClaimableOnChannelClose::amount_satoshis`], as well as any dust HTLCs which + /// would otherwise be represented the same. + inbound_claiming_htlc_rounded_msat: u64, + /// The amount of millisatoshis which has been burned to fees from HTLCs which are inbound + /// to us and for which we do not know the preimage. This is the sum of the millisatoshis + /// part of all HTLCs which would be represented by [`Balance::MaybePreimageClaimableHTLC`] + /// on channel close, as well as any dust HTLCs which would otherwise be represented the + /// same. + inbound_htlc_rounded_msat: u64, }, /// The channel has been closed, and the given balance is ours but awaiting confirmations until /// we consider it spendable. @@ -2073,10 +2099,17 @@ impl ChannelMonitor { } else { let mut claimable_inbound_htlc_value_sat = 0; let mut nondust_htlc_count = 0; + let mut outbound_payment_htlc_rounded_msat = 0; + let mut outbound_forwarded_htlc_rounded_msat = 0; + let mut inbound_claiming_htlc_rounded_msat = 0; + let mut inbound_htlc_rounded_msat = 0; for (htlc, _, source) in us.current_holder_commitment_tx.htlc_outputs.iter() { if htlc.transaction_output_index.is_some() { nondust_htlc_count += 1; - } else { continue; } + } + let rounded_value_msat = if htlc.transaction_output_index.is_none() { + htlc.amount_msat + } else { htlc.amount_msat % 1000 }; if htlc.offered { let outbound_payment = match source { None => { @@ -2086,22 +2119,36 @@ impl ChannelMonitor { Some(HTLCSource::PreviousHopData(_)) => false, Some(HTLCSource::OutboundRoute { .. }) => true, }; - res.push(Balance::MaybeTimeoutClaimableHTLC { - amount_satoshis: htlc.amount_msat / 1000, - claimable_height: htlc.cltv_expiry, - payment_hash: htlc.payment_hash, - outbound_payment, - }); + if outbound_payment { + outbound_payment_htlc_rounded_msat += rounded_value_msat; + } else { + outbound_forwarded_htlc_rounded_msat += rounded_value_msat; + } + if htlc.transaction_output_index.is_some() { + res.push(Balance::MaybeTimeoutClaimableHTLC { + amount_satoshis: htlc.amount_msat / 1000, + claimable_height: htlc.cltv_expiry, + payment_hash: htlc.payment_hash, + outbound_payment, + }); + } } else if us.payment_preimages.get(&htlc.payment_hash).is_some() { - claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000; + inbound_claiming_htlc_rounded_msat += rounded_value_msat; + if htlc.transaction_output_index.is_some() { + claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000; + claimable_inbound_htlc_value_msat += htlc.amount_msat; + } } else { - // As long as the HTLC is still in our latest commitment state, treat - // it as potentially claimable, even if it has long-since expired. - res.push(Balance::MaybePreimageClaimableHTLC { - amount_satoshis: htlc.amount_msat / 1000, - expiry_height: htlc.cltv_expiry, - payment_hash: htlc.payment_hash, - }); + inbound_htlc_rounded_msat += rounded_value_msat; + if htlc.transaction_output_index.is_some() { + // As long as the HTLC is still in our latest commitment state, treat + // it as potentially claimable, even if it has long-since expired. + res.push(Balance::MaybePreimageClaimableHTLC { + amount_satoshis: htlc.amount_msat / 1000, + expiry_height: htlc.cltv_expiry, + payment_hash: htlc.payment_hash, + }); + } } } res.push(Balance::ClaimableOnChannelClose { @@ -2111,6 +2158,10 @@ impl ChannelMonitor { us.current_holder_commitment_tx.feerate_per_kw, nondust_htlc_count, us.onchain_tx_handler.channel_type_features()) } else { 0 }, + outbound_payment_htlc_rounded_msat, + outbound_forwarded_htlc_rounded_msat, + inbound_claiming_htlc_rounded_msat, + inbound_htlc_rounded_msat, }); } diff --git a/lightning/src/ln/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs index aa8b39cb..53504260 100644 --- a/lightning/src/ln/monitor_tests.rs +++ b/lightning/src/ln/monitor_tests.rs @@ -186,9 +186,19 @@ fn do_chanmon_claim_value_coop_close(anchors: bool) { assert_eq!(vec![Balance::ClaimableOnChannelClose { amount_satoshis: 1_000_000 - 1_000 - commitment_tx_fee - anchor_outputs_value, transaction_fee_satoshis: commitment_tx_fee, + outbound_payment_htlc_rounded_msat: 0, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 0, }], nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()); - assert_eq!(vec![Balance::ClaimableOnChannelClose { amount_satoshis: 1_000, transaction_fee_satoshis: 0 }], + assert_eq!(vec![Balance::ClaimableOnChannelClose { + amount_satoshis: 1_000, transaction_fee_satoshis: 0, + outbound_payment_htlc_rounded_msat: 0, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 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(); @@ -387,11 +397,19 @@ fn do_test_claim_value_force_close(anchors: bool, prev_commitment_tx: bool) { 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, + outbound_payment_htlc_rounded_msat: 3000, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 0, }, 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, + outbound_payment_htlc_rounded_msat: 0, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 3000, }, 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())); @@ -439,6 +457,10 @@ fn do_test_claim_value_force_close(anchors: bool, prev_commitment_tx: bool) { 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, + outbound_payment_htlc_rounded_msat: 3000, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 0, }, sent_htlc_timeout_balance.clone()]; if !prev_commitment_tx { a_expected_balances.push(sent_htlc_balance.clone()); @@ -448,6 +470,10 @@ fn do_test_claim_value_force_close(anchors: bool, prev_commitment_tx: bool) { assert_eq!(vec![Balance::ClaimableOnChannelClose { amount_satoshis: 1_000 + 3_000 + 4_000, transaction_fee_satoshis: 0, + outbound_payment_htlc_rounded_msat: 0, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 3000, + inbound_htlc_rounded_msat: 0, }], nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()); @@ -930,12 +956,20 @@ fn test_no_preimage_inbound_htlc_balances() { assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose { amount_satoshis: 1_000_000 - 500_000 - 10_000 - commitment_tx_fee, transaction_fee_satoshis: commitment_tx_fee, + outbound_payment_htlc_rounded_msat: 0, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 0, }, 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, + outbound_payment_htlc_rounded_msat: 0, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 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())); @@ -1215,6 +1249,10 @@ fn do_test_revoked_counterparty_commitment_balances(anchors: bool, confirm_htlc_ assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose { amount_satoshis: 100_000 - 5_000 - 4_000 - 3 - 2_000 + 3_000, transaction_fee_satoshis: 0, + outbound_payment_htlc_rounded_msat: 3000, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 0, }, Balance::MaybeTimeoutClaimableHTLC { amount_satoshis: 2_000, claimable_height: missing_htlc_cltv_timeout, @@ -1769,6 +1807,10 @@ 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, + outbound_payment_htlc_rounded_msat: 0, + outbound_forwarded_htlc_rounded_msat: 0, + inbound_claiming_htlc_rounded_msat: 0, + inbound_htlc_rounded_msat: 0, }, Balance::MaybeTimeoutClaimableHTLC { amount_satoshis: 4_000, claimable_height: htlc_cltv_timeout, -- 2.30.2