Skip to content

Commit 1695c74

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 7ffbe4a commit 1695c74

File tree

3 files changed

+67
-24
lines changed

3 files changed

+67
-24
lines changed

lightning/src/ln/channel.rs

+41-6
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,25 @@ impl<SP: Deref> Channel<SP> where
15501550
}
15511551
}
15521552
}
1553+
1554+
/// Get the available balances, see [`AvailableBalances`]'s fields for more info.
1555+
/// Doesn't bother handling the
1556+
/// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
1557+
/// corner case properly.
1558+
pub fn get_available_balances<F: Deref>(
1559+
&self, fee_estimator: &LowerBoundedFeeEstimator<F>,
1560+
) -> AvailableBalances
1561+
where
1562+
F::Target: FeeEstimator,
1563+
{
1564+
match &self.phase {
1565+
ChannelPhase::Undefined => unreachable!(),
1566+
ChannelPhase::Funded(chan) => chan.get_available_balances(fee_estimator),
1567+
ChannelPhase::UnfundedOutboundV1(chan) => chan.context.get_available_balances_for_scope(&chan.funding, fee_estimator),
1568+
ChannelPhase::UnfundedInboundV1(chan) => chan.context.get_available_balances_for_scope(&chan.funding, fee_estimator),
1569+
ChannelPhase::UnfundedV2(chan) => chan.context.get_available_balances_for_scope(&chan.funding, fee_estimator),
1570+
}
1571+
}
15531572
}
15541573

15551574
impl<SP: Deref> From<OutboundV1Channel<SP>> for Channel<SP>
@@ -4123,11 +4142,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
41234142
outbound_details
41244143
}
41254144

4126-
/// Get the available balances, see [`AvailableBalances`]'s fields for more info.
4127-
/// Doesn't bother handling the
4128-
/// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
4129-
/// corner case properly.
4130-
pub fn get_available_balances<F: Deref>(
4145+
fn get_available_balances_for_scope<F: Deref>(
41314146
&self, funding: &FundingScope, fee_estimator: &LowerBoundedFeeEstimator<F>,
41324147
) -> AvailableBalances
41334148
where
@@ -8707,7 +8722,7 @@ impl<SP: Deref> FundedChannel<SP> where
87078722
return Err(ChannelError::Ignore("Cannot send 0-msat HTLC".to_owned()));
87088723
}
87098724

8710-
let available_balances = self.context.get_available_balances(&self.funding, fee_estimator);
8725+
let available_balances = self.get_available_balances(fee_estimator);
87118726
if amount_msat < available_balances.next_outbound_htlc_minimum_msat {
87128727
return Err(ChannelError::Ignore(format!("Cannot send less than our next-HTLC minimum - {} msat",
87138728
available_balances.next_outbound_htlc_minimum_msat)));
@@ -8779,6 +8794,26 @@ impl<SP: Deref> FundedChannel<SP> where
87798794
Ok(Some(res))
87808795
}
87818796

8797+
pub(super) fn get_available_balances<F: Deref>(
8798+
&self, fee_estimator: &LowerBoundedFeeEstimator<F>,
8799+
) -> AvailableBalances
8800+
where
8801+
F::Target: FeeEstimator,
8802+
{
8803+
core::iter::once(&self.funding)
8804+
.chain(self.pending_funding.iter())
8805+
.map(|funding| self.context.get_available_balances_for_scope(funding, fee_estimator))
8806+
.reduce(|acc, e| {
8807+
AvailableBalances {
8808+
inbound_capacity_msat: acc.inbound_capacity_msat.min(e.inbound_capacity_msat),
8809+
outbound_capacity_msat: acc.outbound_capacity_msat.min(e.outbound_capacity_msat),
8810+
next_outbound_htlc_limit_msat: acc.next_outbound_htlc_limit_msat.min(e.next_outbound_htlc_limit_msat),
8811+
next_outbound_htlc_minimum_msat: acc.next_outbound_htlc_minimum_msat.max(e.next_outbound_htlc_minimum_msat),
8812+
}
8813+
})
8814+
.expect("At least one FundingScope is always provided")
8815+
}
8816+
87828817
fn build_commitment_no_status_check<L: Deref>(&mut self, logger: &L) -> ChannelMonitorUpdate where L::Target: Logger {
87838818
log_trace!(logger, "Updating HTLC state for a newly-sent commitment_signed...");
87848819
// We can upgrade the status of some HTLCs that are waiting on a commitment, even if we

lightning/src/ln/channel_state.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use bitcoin::secp256k1::PublicKey;
1515

1616
use crate::chain::chaininterface::{FeeEstimator, LowerBoundedFeeEstimator};
1717
use crate::chain::transaction::OutPoint;
18-
use crate::ln::channel::{ChannelContext, FundingScope};
18+
use crate::ln::channel::Channel;
1919
use crate::ln::types::ChannelId;
2020
use crate::sign::SignerProvider;
2121
use crate::types::features::{ChannelTypeFeatures, InitFeatures};
@@ -475,15 +475,17 @@ impl ChannelDetails {
475475
self.short_channel_id.or(self.outbound_scid_alias)
476476
}
477477

478-
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>,
478+
pub(super) fn from_channel<SP: Deref, F: Deref>(
479+
channel: &Channel<SP>, best_block_height: u32, latest_features: InitFeatures,
480+
fee_estimator: &LowerBoundedFeeEstimator<F>,
481481
) -> Self
482482
where
483483
SP::Target: SignerProvider,
484484
F::Target: FeeEstimator,
485485
{
486-
let balance = context.get_available_balances(funding, fee_estimator);
486+
let context = channel.context();
487+
let funding = channel.funding();
488+
let balance = channel.get_available_balances(fee_estimator);
487489
let (to_remote_reserve_satoshis, to_self_reserve_satoshis) =
488490
funding.get_holder_counterparty_selected_channel_reserve_satoshis();
489491
#[allow(deprecated)] // TODO: Remove once balance_msat is removed.

lightning/src/ln/channelmanager.rs

+19-13
Original file line numberDiff line numberDiff line change
@@ -3726,7 +3726,7 @@ where
37263726
Ok(temporary_channel_id)
37273727
}
37283728

3729-
fn list_funded_channels_with_filter<Fn: FnMut(&(&ChannelId, &FundedChannel<SP>)) -> bool + Copy>(&self, f: Fn) -> Vec<ChannelDetails> {
3729+
fn list_funded_channels_with_filter<Fn: FnMut(&(&ChannelId, &Channel<SP>)) -> bool + Copy>(&self, f: Fn) -> Vec<ChannelDetails> {
37303730
// Allocate our best estimate of the number of channels we have in the `res`
37313731
// Vec. Sadly the `short_to_chan_info` map doesn't cover channels without
37323732
// a scid or a scid alias. Therefore reallocations may still occur, but is
@@ -3741,11 +3741,13 @@ where
37413741
let peer_state = &mut *peer_state_lock;
37423742
res.extend(peer_state.channel_by_id.iter()
37433743
// Only `Channels` in the `Channel::Funded` phase can be considered funded.
3744-
.filter_map(|(chan_id, chan)| chan.as_funded().map(|chan| (chan_id, chan)))
3744+
.filter(|(_, chan)| chan.is_funded())
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(
3748+
channel, best_block_height, peer_state.latest_features.clone(),
3749+
&self.fee_estimator,
3750+
)
37493751
})
37503752
);
37513753
}
@@ -3768,9 +3770,11 @@ where
37683770
for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
37693771
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
37703772
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);
3773+
for (_, channel) in peer_state.channel_by_id.iter() {
3774+
let details = ChannelDetails::from_channel(
3775+
channel, best_block_height, peer_state.latest_features.clone(),
3776+
&self.fee_estimator,
3777+
);
37743778
res.push(details);
37753779
}
37763780
}
@@ -3788,7 +3792,7 @@ where
37883792
// Note we use is_live here instead of usable which leads to somewhat confused
37893793
// internal/external nomenclature, but that's ok cause that's probably what the user
37903794
// really wanted anyway.
3791-
self.list_funded_channels_with_filter(|&(_, ref channel)| channel.context.is_live())
3795+
self.list_funded_channels_with_filter(|&(_, ref channel)| channel.context().is_live())
37923796
}
37933797

37943798
/// Gets the list of channels we have with a given counterparty, in random order.
@@ -3800,13 +3804,15 @@ where
38003804
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
38013805
let peer_state = &mut *peer_state_lock;
38023806
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)
3807+
let channel_to_details = |channel| {
3808+
ChannelDetails::from_channel(
3809+
channel, best_block_height, features.clone(), &self.fee_estimator,
3810+
)
38053811
};
38063812
return peer_state.channel_by_id
38073813
.iter()
3808-
.map(|(_, chan)| (chan.context(), chan.funding()))
3809-
.map(context_to_details)
3814+
.map(|(_, chan)| (chan))
3815+
.map(channel_to_details)
38103816
.collect();
38113817
}
38123818
vec![]
@@ -6087,7 +6093,7 @@ where
60876093
let maybe_optimal_channel = peer_state.channel_by_id.values_mut()
60886094
.filter_map(Channel::as_funded_mut)
60896095
.filter_map(|chan| {
6090-
let balances = chan.context.get_available_balances(&chan.funding, &self.fee_estimator);
6096+
let balances = chan.get_available_balances(&self.fee_estimator);
60916097
if outgoing_amt_msat <= balances.next_outbound_htlc_limit_msat &&
60926098
outgoing_amt_msat >= balances.next_outbound_htlc_minimum_msat &&
60936099
chan.context.is_usable() {

0 commit comments

Comments
 (0)