Skip to content

Commit b0f3390

Browse files
committed
Consider funding scopes in get_available_balances
A FundedChannel may have more than one pending FundingScope during splicing, one for the splice attempt and one or more for any RBF attempts. When calling get_available_balances, consider all funding scopes and take the minimum by next_outbound_htlc_limit_msat. This is used both informationally and to determine which channel to use to forward an HTLC. The choice of next_outbound_htlc_limit_msat is somewhat arbitrary but matches the field used when determining which channel used to forward an HTLC. Any field should do since each field should be adjusted by the same amount relative to another FundingScope given the nature of the fields (i.e., inbound/outbound capacity, min/max HTLC limit). Using the minimum was chosen since an order for an HTLC to be sent over the channel, it must be possible for each funding scope -- both the confirmed one and any pending scopes, one of which may eventually confirm.
1 parent dceead3 commit b0f3390

File tree

3 files changed

+65
-14
lines changed

3 files changed

+65
-14
lines changed

lightning/src/ln/channel.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,16 @@ impl<SP: Deref> Channel<SP> where
12481248
}
12491249
}
12501250

1251+
pub fn pending_funding(&self) -> &[FundingScope] {
1252+
match &self.phase {
1253+
ChannelPhase::Undefined => unreachable!(),
1254+
ChannelPhase::Funded(chan) => &chan.pending_funding,
1255+
ChannelPhase::UnfundedOutboundV1(_) => &[],
1256+
ChannelPhase::UnfundedInboundV1(_) => &[],
1257+
ChannelPhase::UnfundedV2(_) => &[],
1258+
}
1259+
}
1260+
12511261
pub fn unfunded_context_mut(&mut self) -> Option<&mut UnfundedChannelContext> {
12521262
match &mut self.phase {
12531263
ChannelPhase::Undefined => unreachable!(),
@@ -4128,6 +4138,27 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
41284138
/// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
41294139
/// corner case properly.
41304140
pub fn get_available_balances<F: Deref>(
4141+
&self, funding: &FundingScope, pending_funding: &[FundingScope],
4142+
fee_estimator: &LowerBoundedFeeEstimator<F>,
4143+
) -> AvailableBalances
4144+
where
4145+
F::Target: FeeEstimator,
4146+
{
4147+
core::iter::once(funding)
4148+
.chain(pending_funding.iter())
4149+
.map(|funding| self.get_available_balances_for_scope(funding, fee_estimator))
4150+
.reduce(|acc, e| {
4151+
AvailableBalances {
4152+
inbound_capacity_msat: acc.inbound_capacity_msat.min(e.inbound_capacity_msat),
4153+
outbound_capacity_msat: acc.outbound_capacity_msat.min(e.outbound_capacity_msat),
4154+
next_outbound_htlc_limit_msat: acc.next_outbound_htlc_limit_msat.min(e.next_outbound_htlc_limit_msat),
4155+
next_outbound_htlc_minimum_msat: acc.next_outbound_htlc_minimum_msat.max(e.next_outbound_htlc_minimum_msat),
4156+
}
4157+
})
4158+
.expect("At least one FundingScope is always provided")
4159+
}
4160+
4161+
fn get_available_balances_for_scope<F: Deref>(
41314162
&self, funding: &FundingScope, fee_estimator: &LowerBoundedFeeEstimator<F>,
41324163
) -> AvailableBalances
41334164
where
@@ -4918,7 +4949,7 @@ pub(super) struct DualFundingChannelContext {
49184949
// Counterparty designates channel data owned by the another channel participant entity.
49194950
pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
49204951
pub funding: FundingScope,
4921-
pending_funding: Vec<FundingScope>,
4952+
pub(super) pending_funding: Vec<FundingScope>,
49224953
pub context: ChannelContext<SP>,
49234954
pub interactive_tx_signing_session: Option<InteractiveTxSigningSession>,
49244955
holder_commitment_point: HolderCommitmentPoint,
@@ -8711,7 +8742,7 @@ impl<SP: Deref> FundedChannel<SP> where
87118742
return Err(ChannelError::Ignore("Cannot send 0-msat HTLC".to_owned()));
87128743
}
87138744

8714-
let available_balances = self.context.get_available_balances(&self.funding, fee_estimator);
8745+
let available_balances = self.get_available_balances(fee_estimator);
87158746
if amount_msat < available_balances.next_outbound_htlc_minimum_msat {
87168747
return Err(ChannelError::Ignore(format!("Cannot send less than our next-HTLC minimum - {} msat",
87178748
available_balances.next_outbound_htlc_minimum_msat)));
@@ -8783,6 +8814,15 @@ impl<SP: Deref> FundedChannel<SP> where
87838814
Ok(Some(res))
87848815
}
87858816

8817+
pub fn get_available_balances<F: Deref>(
8818+
&self, fee_estimator: &LowerBoundedFeeEstimator<F>,
8819+
) -> AvailableBalances
8820+
where
8821+
F::Target: FeeEstimator,
8822+
{
8823+
self.context.get_available_balances(&self.funding, &self.pending_funding, fee_estimator)
8824+
}
8825+
87868826
fn build_commitment_no_status_check<L: Deref>(&mut self, logger: &L) -> ChannelMonitorUpdate where L::Target: Logger {
87878827
log_trace!(logger, "Updating HTLC state for a newly-sent commitment_signed...");
87888828
// We can upgrade the status of some HTLCs that are waiting on a commitment, even if we

lightning/src/ln/channel_state.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -476,14 +476,15 @@ impl ChannelDetails {
476476
}
477477

478478
pub(super) fn from_channel_context<SP: Deref, F: Deref>(
479-
context: &ChannelContext<SP>, funding: &FundingScope, best_block_height: u32,
480-
latest_features: InitFeatures, fee_estimator: &LowerBoundedFeeEstimator<F>,
479+
context: &ChannelContext<SP>, funding: &FundingScope, pending_funding: &[FundingScope],
480+
best_block_height: u32, latest_features: InitFeatures,
481+
fee_estimator: &LowerBoundedFeeEstimator<F>,
481482
) -> Self
482483
where
483484
SP::Target: SignerProvider,
484485
F::Target: FeeEstimator,
485486
{
486-
let balance = context.get_available_balances(funding, fee_estimator);
487+
let balance = context.get_available_balances(funding, pending_funding, fee_estimator);
487488
let (to_remote_reserve_satoshis, to_self_reserve_satoshis) =
488489
funding.get_holder_counterparty_selected_channel_reserve_satoshis();
489490
#[allow(deprecated)] // TODO: Remove once balance_msat is removed.

lightning/src/ln/channelmanager.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3744,8 +3744,11 @@ where
37443744
.filter_map(|(chan_id, chan)| chan.as_funded().map(|chan| (chan_id, chan)))
37453745
.filter(f)
37463746
.map(|(_channel_id, channel)| {
3747-
ChannelDetails::from_channel_context(&channel.context, &channel.funding, best_block_height,
3748-
peer_state.latest_features.clone(), &self.fee_estimator)
3747+
ChannelDetails::from_channel_context(
3748+
&channel.context, &channel.funding, &channel.pending_funding,
3749+
best_block_height, peer_state.latest_features.clone(),
3750+
&self.fee_estimator,
3751+
)
37493752
})
37503753
);
37513754
}
@@ -3768,9 +3771,13 @@ where
37683771
for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
37693772
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
37703773
let peer_state = &mut *peer_state_lock;
3771-
for (context, funding) in peer_state.channel_by_id.iter().map(|(_, chan)| (chan.context(), chan.funding())) {
3772-
let details = ChannelDetails::from_channel_context(context, funding, best_block_height,
3773-
peer_state.latest_features.clone(), &self.fee_estimator);
3774+
for (context, funding, pending_funding) in peer_state.channel_by_id.iter()
3775+
.map(|(_, chan)| (chan.context(), chan.funding(), chan.pending_funding()))
3776+
{
3777+
let details = ChannelDetails::from_channel_context(
3778+
context, funding, pending_funding, best_block_height,
3779+
peer_state.latest_features.clone(), &self.fee_estimator,
3780+
);
37743781
res.push(details);
37753782
}
37763783
}
@@ -3800,12 +3807,15 @@ where
38003807
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
38013808
let peer_state = &mut *peer_state_lock;
38023809
let features = &peer_state.latest_features;
3803-
let context_to_details = |(context, funding)| {
3804-
ChannelDetails::from_channel_context(context, funding, best_block_height, features.clone(), &self.fee_estimator)
3810+
let context_to_details = |(context, funding, pending_funding)| {
3811+
ChannelDetails::from_channel_context(
3812+
context, funding, pending_funding, best_block_height, features.clone(),
3813+
&self.fee_estimator,
3814+
)
38053815
};
38063816
return peer_state.channel_by_id
38073817
.iter()
3808-
.map(|(_, chan)| (chan.context(), chan.funding()))
3818+
.map(|(_, chan)| (chan.context(), chan.funding(), chan.pending_funding()))
38093819
.map(context_to_details)
38103820
.collect();
38113821
}
@@ -6087,7 +6097,7 @@ where
60876097
let maybe_optimal_channel = peer_state.channel_by_id.values_mut()
60886098
.filter_map(Channel::as_funded_mut)
60896099
.filter_map(|chan| {
6090-
let balances = chan.context.get_available_balances(&chan.funding, &self.fee_estimator);
6100+
let balances = chan.get_available_balances(&self.fee_estimator);
60916101
if outgoing_amt_msat <= balances.next_outbound_htlc_limit_msat &&
60926102
outgoing_amt_msat >= balances.next_outbound_htlc_minimum_msat &&
60936103
chan.context.is_usable() {

0 commit comments

Comments
 (0)