@@ -74,7 +74,7 @@ use crate::ln::script::{self, ShutdownScript};
7474use crate::ln::types::ChannelId;
7575use crate::routing::gossip::NodeId;
7676use crate::sign::ecdsa::EcdsaChannelSigner;
77- use crate::sign::tx_builder::{SpecTxBuilder, TxBuilder};
77+ use crate::sign::tx_builder::{HTLCAmountDirection, NextCommitmentStats, SpecTxBuilder, TxBuilder};
7878use crate::sign::{ChannelSigner, EntropySource, NodeSigner, Recipient, SignerProvider};
7979use crate::types::features::{ChannelTypeFeatures, InitFeatures};
8080use crate::types::payment::{PaymentHash, PaymentPreimage};
@@ -4100,6 +4100,167 @@ where
41004100 );
41014101 }
41024102
4103+ /// Returns a best-effort guess of the set of HTLCs that will be present
4104+ /// on the next local or remote commitment. We cannot be certain as the
4105+ /// actual set of HTLCs present on the next commitment depends on the
4106+ /// ordering of commitment_signed and revoke_and_ack messages.
4107+ ///
4108+ /// We take the conservative approach and only assume that a HTLC will
4109+ /// not be in the next commitment when it is guaranteed that it won't be.
4110+ #[allow(dead_code)]
4111+ #[rustfmt::skip]
4112+ fn get_next_commitment_htlcs(
4113+ &self, local: bool, htlc_candidate: Option<HTLCAmountDirection>, include_counterparty_unknown_htlcs: bool,
4114+ ) -> Vec<HTLCAmountDirection> {
4115+ let mut commitment_htlcs = Vec::with_capacity(
4116+ 1 + self.pending_inbound_htlcs.len()
4117+ + self.pending_outbound_htlcs.len()
4118+ + self.holding_cell_htlc_updates.len(),
4119+ );
4120+ // `LocalRemoved` HTLCs will certainly not be present on any future remote
4121+ // commitments, but they could be in a future local commitment as the remote has
4122+ // not yet acknowledged the removal.
4123+ let pending_inbound_htlcs = self
4124+ .pending_inbound_htlcs
4125+ .iter()
4126+ .filter(|InboundHTLCOutput { state, .. }| match (state, local) {
4127+ (InboundHTLCState::RemoteAnnounced(..), _) => true,
4128+ (InboundHTLCState::AwaitingRemoteRevokeToAnnounce(..), _) => true,
4129+ (InboundHTLCState::AwaitingAnnouncedRemoteRevoke(..), _) => true,
4130+ (InboundHTLCState::Committed, _) => true,
4131+ (InboundHTLCState::LocalRemoved(..), true) => true,
4132+ (InboundHTLCState::LocalRemoved(..), false) => false,
4133+ })
4134+ .map(|&InboundHTLCOutput { amount_msat, .. }| HTLCAmountDirection { outbound: false, amount_msat });
4135+ // `RemoteRemoved` HTLCs can still be present on the next remote commitment if
4136+ // local produces a commitment before acknowledging the update. These HTLCs
4137+ // will for sure not be present on the next local commitment.
4138+ let pending_outbound_htlcs = self
4139+ .pending_outbound_htlcs
4140+ .iter()
4141+ .filter(|OutboundHTLCOutput { state, .. }| match (state, local) {
4142+ (OutboundHTLCState::LocalAnnounced(..), _) => include_counterparty_unknown_htlcs,
4143+ (OutboundHTLCState::Committed, _) => true,
4144+ (OutboundHTLCState::RemoteRemoved(..), true) => false,
4145+ (OutboundHTLCState::RemoteRemoved(..), false) => true,
4146+ (OutboundHTLCState::AwaitingRemoteRevokeToRemove(..), _) => false,
4147+ (OutboundHTLCState::AwaitingRemovedRemoteRevoke(..), _) => false,
4148+ })
4149+ .map(|&OutboundHTLCOutput { amount_msat, .. }| HTLCAmountDirection { outbound: true, amount_msat });
4150+
4151+ let holding_cell_htlcs = self.holding_cell_htlc_updates.iter().filter_map(|htlc| {
4152+ if let &HTLCUpdateAwaitingACK::AddHTLC { amount_msat, .. } = htlc {
4153+ Some(HTLCAmountDirection { outbound: true, amount_msat })
4154+ } else {
4155+ None
4156+ }
4157+ });
4158+
4159+ if include_counterparty_unknown_htlcs {
4160+ commitment_htlcs.extend(
4161+ htlc_candidate.into_iter().chain(pending_inbound_htlcs).chain(pending_outbound_htlcs).chain(holding_cell_htlcs)
4162+ );
4163+ } else {
4164+ commitment_htlcs.extend(
4165+ htlc_candidate.into_iter().chain(pending_inbound_htlcs).chain(pending_outbound_htlcs)
4166+ );
4167+ }
4168+
4169+ commitment_htlcs
4170+ }
4171+
4172+ /// This returns the value of `value_to_self_msat` after accounting for all the
4173+ /// successful inbound and outbound HTLCs that won't be present on the next
4174+ /// commitment.
4175+ ///
4176+ /// To determine which HTLC claims to account for, we take the cases where a HTLC
4177+ /// will *not* be present on the next commitment from `next_commitment_htlcs`, and
4178+ /// check if their outcome is successful. If it is, we add the value of this claimed
4179+ /// HTLC to the balance of the claimer.
4180+ #[allow(dead_code)]
4181+ #[rustfmt::skip]
4182+ fn get_next_commitment_value_to_self_msat(&self, local: bool, funding: &FundingScope) -> u64 {
4183+ let inbound_claimed_htlc_msat: u64 =
4184+ self.pending_inbound_htlcs
4185+ .iter()
4186+ .filter(|InboundHTLCOutput { state, .. }| match (state, local) {
4187+ (InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_, _)), true) => false,
4188+ (InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_, _)), false) => true,
4189+ _ => false,
4190+ })
4191+ .map(|InboundHTLCOutput { amount_msat, .. }| amount_msat)
4192+ .sum();
4193+ let outbound_claimed_htlc_msat: u64 =
4194+ self.pending_outbound_htlcs
4195+ .iter()
4196+ .filter(|OutboundHTLCOutput { state, .. }| match (state, local) {
4197+ (OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_, _)), true) => true,
4198+ (OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_, _)), false) => false,
4199+ (OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_, _)), _) => true,
4200+ (OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_, _)), _) => true,
4201+ _ => false,
4202+ })
4203+ .map(|OutboundHTLCOutput { amount_msat, .. }| amount_msat)
4204+ .sum();
4205+
4206+ funding
4207+ .value_to_self_msat
4208+ .saturating_sub(outbound_claimed_htlc_msat)
4209+ .saturating_add(inbound_claimed_htlc_msat)
4210+ }
4211+
4212+ #[allow(dead_code)]
4213+ fn get_next_local_commitment_stats(
4214+ &self, funding: &FundingScope, htlc_candidate: Option<HTLCAmountDirection>,
4215+ include_counterparty_unknown_htlcs: bool, addl_nondust_htlc_count: usize,
4216+ feerate_per_kw: u32, dust_exposure_limiting_feerate: Option<u32>,
4217+ ) -> NextCommitmentStats {
4218+ let next_commitment_htlcs = self.get_next_commitment_htlcs(
4219+ true,
4220+ htlc_candidate,
4221+ include_counterparty_unknown_htlcs,
4222+ );
4223+ let next_value_to_self_msat = self.get_next_commitment_value_to_self_msat(true, funding);
4224+ SpecTxBuilder {}.get_next_commitment_stats(
4225+ true,
4226+ funding.is_outbound(),
4227+ funding.get_value_satoshis(),
4228+ next_value_to_self_msat,
4229+ &next_commitment_htlcs,
4230+ addl_nondust_htlc_count,
4231+ feerate_per_kw,
4232+ dust_exposure_limiting_feerate,
4233+ self.holder_dust_limit_satoshis,
4234+ funding.get_channel_type(),
4235+ )
4236+ }
4237+
4238+ #[allow(dead_code)]
4239+ fn get_next_remote_commitment_stats(
4240+ &self, funding: &FundingScope, htlc_candidate: Option<HTLCAmountDirection>,
4241+ include_counterparty_unknown_htlcs: bool, addl_nondust_htlc_count: usize,
4242+ feerate_per_kw: u32, dust_exposure_limiting_feerate: Option<u32>,
4243+ ) -> NextCommitmentStats {
4244+ let next_commitment_htlcs = self.get_next_commitment_htlcs(
4245+ false,
4246+ htlc_candidate,
4247+ include_counterparty_unknown_htlcs,
4248+ );
4249+ let next_value_to_self_msat = self.get_next_commitment_value_to_self_msat(false, funding);
4250+ SpecTxBuilder {}.get_next_commitment_stats(
4251+ false,
4252+ funding.is_outbound(),
4253+ funding.get_value_satoshis(),
4254+ next_value_to_self_msat,
4255+ &next_commitment_htlcs,
4256+ addl_nondust_htlc_count,
4257+ feerate_per_kw,
4258+ dust_exposure_limiting_feerate,
4259+ self.counterparty_dust_limit_satoshis,
4260+ funding.get_channel_type(),
4261+ )
4262+ }
4263+
41034264 #[rustfmt::skip]
41044265 fn validate_update_add_htlc<F: Deref>(
41054266 &self, funding: &FundingScope, msg: &msgs::UpdateAddHTLC,
0 commit comments