Skip to content

Commit 7dbb0fb

Browse files
committed
Add ChannelContext::get_next_{local, remote}_commitment_stats
In upcoming commits, these methods will serve as proxies to `SpecTxBuilder::get_next_commitment_stats` in all validation of channel updates in `ChannelContext`. Eventually, these methods will completely replace `get_pending_htlc_stats`, and `get_next_{local, remote}_commit_tx_fee_msat`. When predicting the HTLCs on next commitment, we take the conservative approach and only assume that a HTLC will not be in the next commitment when it is guaranteed that it won't be. While we previously took into account HTLCs in the holding cell when validating channel updates, we now choose to ignore them; our counterparty certainly does not know about them, and we will re-validate their addition to the channel upon freeing the holding cell.
1 parent 64966e1 commit 7dbb0fb

File tree

1 file changed

+157
-1
lines changed

1 file changed

+157
-1
lines changed

lightning/src/ln/channel.rs

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ use crate::ln::script::{self, ShutdownScript};
7070
use crate::ln::types::ChannelId;
7171
use crate::routing::gossip::NodeId;
7272
use crate::sign::ecdsa::EcdsaChannelSigner;
73-
use crate::sign::tx_builder::{SpecTxBuilder, TxBuilder};
73+
use crate::sign::tx_builder::{HTLCAmountDirection, NextCommitmentStats, SpecTxBuilder, TxBuilder};
7474
use crate::sign::{ChannelSigner, EntropySource, NodeSigner, Recipient, SignerProvider};
7575
use crate::types::features::{ChannelTypeFeatures, InitFeatures};
7676
use crate::types::payment::{PaymentHash, PaymentPreimage};
@@ -4104,6 +4104,162 @@ where
41044104
);
41054105
}
41064106

4107+
#[allow(dead_code)]
4108+
fn pending_inbound_htlcs_value_msat(&self) -> u64 {
4109+
self.pending_inbound_htlcs.iter().map(|htlc| htlc.amount_msat).sum()
4110+
}
4111+
4112+
/// Returns a best-effort guess of the set of HTLCs that will be present
4113+
/// on the next local or remote commitment. We cannot be certain as the
4114+
/// actual set of HTLCs present on the next commitment depends on the
4115+
/// ordering of commitment_signed and revoke_and_ack messages.
4116+
///
4117+
/// We take the conservative approach and only assume that a HTLC will
4118+
/// not be in the next commitment when it is guaranteed that it won't be.
4119+
///
4120+
/// While we previously took into account HTLCs in the holding cell when
4121+
/// validating channel updates, we now choose to ignore them; our
4122+
/// counterparty certainly does not know about them, and we will re-validate
4123+
/// their addition to the channel upon freeing the holding cell, failing
4124+
/// them backwards if necessary.
4125+
#[allow(dead_code)]
4126+
#[rustfmt::skip]
4127+
fn get_next_commitment_htlcs(
4128+
&self, local: bool, htlc_candidate: Option<HTLCAmountDirection>,
4129+
) -> Vec<HTLCAmountDirection> {
4130+
let mut commitment_htlcs = Vec::with_capacity(
4131+
1 + self.pending_inbound_htlcs.len()
4132+
+ self.pending_outbound_htlcs.len()
4133+
+ self.holding_cell_htlc_updates.len(),
4134+
);
4135+
// `LocalRemoved` HTLCs will certainly not be present on any future remote
4136+
// commitments, but they could be in a future local commitment as the remote has
4137+
// not yet acknowledged the removal.
4138+
let pending_inbound_htlcs = self
4139+
.pending_inbound_htlcs
4140+
.iter()
4141+
.filter(|InboundHTLCOutput { state, .. }| match (state, local) {
4142+
(InboundHTLCState::RemoteAnnounced(..), _) => true,
4143+
(InboundHTLCState::AwaitingRemoteRevokeToAnnounce(..), _) => true,
4144+
(InboundHTLCState::AwaitingAnnouncedRemoteRevoke(..), _) => true,
4145+
(InboundHTLCState::Committed, _) => true,
4146+
(InboundHTLCState::LocalRemoved(..), true) => true,
4147+
(InboundHTLCState::LocalRemoved(..), false) => false,
4148+
})
4149+
.map(|&InboundHTLCOutput { amount_msat, .. }| HTLCAmountDirection { outbound: false, amount_msat });
4150+
// While `LocalAnnounced` HTLCs will for sure be present on the next remote commitment, it is
4151+
// also possible for `LocalAnnounced` HTLCs to be present on the next local commitment if the
4152+
// remote acks the update before producing the next local commitment.
4153+
//
4154+
// `RemoteRemoved` HTLCs can still be present on the next remote commitment if
4155+
// local produces a commitment before acknowledging the update. These HTLCs
4156+
// will for sure not be present on the next local commitment.
4157+
let pending_outbound_htlcs = self
4158+
.pending_outbound_htlcs
4159+
.iter()
4160+
.filter(|OutboundHTLCOutput { state, .. }| match (state, local) {
4161+
(OutboundHTLCState::LocalAnnounced(..), _) => true,
4162+
(OutboundHTLCState::Committed, _) => true,
4163+
(OutboundHTLCState::RemoteRemoved(..), true) => false,
4164+
(OutboundHTLCState::RemoteRemoved(..), false) => true,
4165+
(OutboundHTLCState::AwaitingRemoteRevokeToRemove(..), _) => false,
4166+
(OutboundHTLCState::AwaitingRemovedRemoteRevoke(..), _) => false,
4167+
})
4168+
.map(|&OutboundHTLCOutput { amount_msat, .. }| HTLCAmountDirection { outbound: true, amount_msat });
4169+
4170+
// We do not include holding cell HTLCs, we will validate them upon freeing the holding cell...
4171+
//let holding_cell_htlcs = self.holding_cell_htlc_updates.iter().filter_map(|htlc| if let HTLCUpdateAwaitingACK::AddHTLC { amount_msat, ..} = htlc { Some(HTLCAmountDirection { outbound: true, amount_msat: *amount_msat }) } else { None });
4172+
4173+
commitment_htlcs.extend(
4174+
htlc_candidate.into_iter().chain(pending_inbound_htlcs).chain(pending_outbound_htlcs),
4175+
);
4176+
commitment_htlcs
4177+
}
4178+
4179+
/// This returns the value of `value_to_self_msat` after accounting for all the
4180+
/// successful inbound and outbound HTLCs that won't be present on the next
4181+
/// commitment.
4182+
///
4183+
/// To determine which HTLC claims to account for, we take the cases where a HTLC
4184+
/// will *not* be present on the next commitment from `next_commitment_htlcs`, and
4185+
/// check if their outcome is successful. If it is, we add the value of this claimed
4186+
/// HTLC to the balance of the claimer.
4187+
#[allow(dead_code)]
4188+
#[rustfmt::skip]
4189+
fn get_next_commitment_value_to_self_msat(&self, local: bool, funding: &FundingScope) -> u64 {
4190+
let inbound_claimed_htlc_msat: u64 =
4191+
self.pending_inbound_htlcs
4192+
.iter()
4193+
.filter(|InboundHTLCOutput { state, .. }| match (state, local) {
4194+
(InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_, _)), true) => false,
4195+
(InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_, _)), false) => true,
4196+
_ => false,
4197+
})
4198+
.map(|InboundHTLCOutput { amount_msat, .. }| amount_msat)
4199+
.sum();
4200+
let outbound_claimed_htlc_msat: u64 =
4201+
self.pending_outbound_htlcs
4202+
.iter()
4203+
.filter(|OutboundHTLCOutput { state, .. }| match (state, local) {
4204+
(OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_, _)), true) => true,
4205+
(OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_, _)), false) => false,
4206+
(OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_, _)), _) => true,
4207+
(OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_, _)), _) => true,
4208+
_ => false,
4209+
})
4210+
.map(|OutboundHTLCOutput { amount_msat, .. }| amount_msat)
4211+
.sum();
4212+
4213+
funding
4214+
.value_to_self_msat
4215+
.saturating_sub(outbound_claimed_htlc_msat)
4216+
.saturating_add(inbound_claimed_htlc_msat)
4217+
}
4218+
4219+
#[allow(dead_code)]
4220+
fn get_next_local_commitment_stats(
4221+
&self, funding: &FundingScope, candidate_htlc: Option<HTLCAmountDirection>,
4222+
addl_nondust_htlc_count: usize, feerate_per_kw: u32,
4223+
dust_exposure_limiting_feerate: Option<u32>,
4224+
) -> NextCommitmentStats {
4225+
let next_commitment_htlcs = self.get_next_commitment_htlcs(true, candidate_htlc);
4226+
let next_value_to_self_msat = self.get_next_commitment_value_to_self_msat(true, funding);
4227+
SpecTxBuilder {}.get_next_commitment_stats(
4228+
true,
4229+
funding.is_outbound(),
4230+
funding.get_value_satoshis(),
4231+
next_value_to_self_msat,
4232+
next_commitment_htlcs,
4233+
addl_nondust_htlc_count,
4234+
feerate_per_kw,
4235+
dust_exposure_limiting_feerate,
4236+
self.holder_dust_limit_satoshis,
4237+
funding.get_channel_type(),
4238+
)
4239+
}
4240+
4241+
#[allow(dead_code)]
4242+
fn get_next_remote_commitment_stats(
4243+
&self, funding: &FundingScope, candidate_htlc: Option<HTLCAmountDirection>,
4244+
addl_nondust_htlc_count: usize, feerate_per_kw: u32,
4245+
dust_exposure_limiting_feerate: Option<u32>,
4246+
) -> NextCommitmentStats {
4247+
let next_commitment_htlcs = self.get_next_commitment_htlcs(false, candidate_htlc);
4248+
let next_value_to_self_msat = self.get_next_commitment_value_to_self_msat(false, funding);
4249+
SpecTxBuilder {}.get_next_commitment_stats(
4250+
false,
4251+
funding.is_outbound(),
4252+
funding.get_value_satoshis(),
4253+
next_value_to_self_msat,
4254+
next_commitment_htlcs,
4255+
addl_nondust_htlc_count,
4256+
feerate_per_kw,
4257+
dust_exposure_limiting_feerate,
4258+
self.counterparty_dust_limit_satoshis,
4259+
funding.get_channel_type(),
4260+
)
4261+
}
4262+
41074263
#[rustfmt::skip]
41084264
fn validate_update_add_htlc<F: Deref>(
41094265
&self, funding: &FundingScope, msg: &msgs::UpdateAddHTLC,

0 commit comments

Comments
 (0)