Skip to content

Commit 2745076

Browse files
committed
Effective channel capacity for router and scoring
A channel's capacity may be inferred or learned and is used to make routing decisions, including as a parameter to channel scoring. Define an EffectiveCapacity for this purpose. Score::channel_penalty_msat takes the effective capacity (less in-flight HTLCs for the same payment), and never None. Thus, for hops given in an invoice, the effective capacity is now considered (near) infinite if over a private channel or based on learned information if over a public channel. If a Score implementations needs the effective capacity when updating a channel's score, i.e. in payment_path_failed or payment_path_successful, it can access the channel's EffectiveCapacity via the NetworkGraph by first looking up the channel and then specifying which direction is desired using ChannelInfo::as_directed.
1 parent 457e48e commit 2745076

File tree

4 files changed

+377
-230
lines changed

4 files changed

+377
-230
lines changed

lightning-invoice/src/payment.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
//! # }
9191
//! # impl Score for FakeScorer {
9292
//! # fn channel_penalty_msat(
93-
//! # &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
93+
//! # &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: u64, _source: &NodeId, _target: &NodeId
9494
//! # ) -> u64 { 0 }
9595
//! # fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
9696
//! # fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
@@ -1327,7 +1327,7 @@ mod tests {
13271327

13281328
impl Score for TestScorer {
13291329
fn channel_penalty_msat(
1330-
&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
1330+
&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: u64, _source: &NodeId, _target: &NodeId
13311331
) -> u64 { 0 }
13321332

13331333
fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {

lightning/src/routing/network_graph.rs

Lines changed: 151 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -593,9 +593,8 @@ where
593593
}
594594

595595
#[derive(Clone, Debug, PartialEq)]
596-
/// Details about one direction of a channel. Received
597-
/// within a channel update.
598-
pub struct DirectionalChannelInfo {
596+
/// Details about one direction of a channel as received within a [`ChannelUpdate`].
597+
pub struct ChannelUpdateInfo {
599598
/// When the last update to the channel direction was issued.
600599
/// Value is opaque, as set in the announcement.
601600
pub last_update: u32,
@@ -616,14 +615,14 @@ pub struct DirectionalChannelInfo {
616615
pub last_update_message: Option<ChannelUpdate>,
617616
}
618617

619-
impl fmt::Display for DirectionalChannelInfo {
618+
impl fmt::Display for ChannelUpdateInfo {
620619
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
621620
write!(f, "last_update {}, enabled {}, cltv_expiry_delta {}, htlc_minimum_msat {}, fees {:?}", self.last_update, self.enabled, self.cltv_expiry_delta, self.htlc_minimum_msat, self.fees)?;
622621
Ok(())
623622
}
624623
}
625624

626-
impl_writeable_tlv_based!(DirectionalChannelInfo, {
625+
impl_writeable_tlv_based!(ChannelUpdateInfo, {
627626
(0, last_update, required),
628627
(2, enabled, required),
629628
(4, cltv_expiry_delta, required),
@@ -642,11 +641,11 @@ pub struct ChannelInfo {
642641
/// Source node of the first direction of a channel
643642
pub node_one: NodeId,
644643
/// Details about the first direction of a channel
645-
pub one_to_two: Option<DirectionalChannelInfo>,
644+
pub one_to_two: Option<ChannelUpdateInfo>,
646645
/// Source node of the second direction of a channel
647646
pub node_two: NodeId,
648647
/// Details about the second direction of a channel
649-
pub two_to_one: Option<DirectionalChannelInfo>,
648+
pub two_to_one: Option<ChannelUpdateInfo>,
650649
/// The channel capacity as seen on-chain, if chain lookup is available.
651650
pub capacity_sats: Option<u64>,
652651
/// An initial announcement of the channel
@@ -660,6 +659,23 @@ pub struct ChannelInfo {
660659
announcement_received_time: u64,
661660
}
662661

662+
impl ChannelInfo {
663+
/// Returns a [`DirectedChannelInfo`] for the channel directed to the given `target` from a
664+
/// returned `source`, or `None` if `target` is not one of the channel's counterparties.
665+
pub fn as_directed_to(&self, target: &NodeId) -> Option<(DirectedChannelInfo, &NodeId)> {
666+
let (direction, source) = {
667+
if target == &self.node_one {
668+
(self.two_to_one.as_ref(), &self.node_two)
669+
} else if target == &self.node_two {
670+
(self.one_to_two.as_ref(), &self.node_one)
671+
} else {
672+
return None;
673+
}
674+
};
675+
Some((DirectedChannelInfo { channel: self, direction }, source))
676+
}
677+
}
678+
663679
impl fmt::Display for ChannelInfo {
664680
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
665681
write!(f, "features: {}, node_one: {}, one_to_two: {:?}, node_two: {}, two_to_one: {:?}",
@@ -679,6 +695,132 @@ impl_writeable_tlv_based!(ChannelInfo, {
679695
(12, announcement_message, required),
680696
});
681697

698+
/// A wrapper around [`ChannelInfo`] representing information about the channel as directed from a
699+
/// source node to a target node.
700+
#[derive(Clone)]
701+
pub struct DirectedChannelInfo<'a> {
702+
channel: &'a ChannelInfo,
703+
direction: Option<&'a ChannelUpdateInfo>,
704+
}
705+
706+
impl<'a> DirectedChannelInfo<'a> {
707+
/// Returns information for the channel.
708+
pub fn channel(&self) -> &'a ChannelInfo { self.channel }
709+
710+
/// Returns information for the direction.
711+
pub fn direction(&self) -> Option<&'a ChannelUpdateInfo> { self.direction }
712+
713+
/// Returns the [`EffectiveCapacity`] of the channel in the direction.
714+
///
715+
/// This is either the total capacity from the funding transaction, if known, or the
716+
/// `htlc_maximum_msat` for the direction as advertised by the gossip network, if known,
717+
/// whichever is smaller.
718+
pub fn effective_capacity(&self) -> EffectiveCapacity {
719+
let capacity_msat = self.channel.capacity_sats.map(|capacity_sats| capacity_sats * 1000);
720+
self.direction
721+
.and_then(|direction| direction.htlc_maximum_msat)
722+
.map(|max_htlc_msat| {
723+
let capacity_msat = capacity_msat.unwrap_or(u64::max_value());
724+
if max_htlc_msat < capacity_msat {
725+
EffectiveCapacity::MaximumHTLC { amount_msat: max_htlc_msat }
726+
} else {
727+
EffectiveCapacity::Total { capacity_msat }
728+
}
729+
})
730+
.or_else(|| capacity_msat.map(|capacity_msat|
731+
EffectiveCapacity::Total { capacity_msat }))
732+
.unwrap_or(EffectiveCapacity::Unknown)
733+
}
734+
735+
/// Returns `Some` if [`ChannelUpdateInfo`] is available in the direction.
736+
pub(super) fn with_update(self) -> Option<DirectedChannelInfoWithUpdate<'a>> {
737+
match self.direction {
738+
Some(_) => Some(DirectedChannelInfoWithUpdate { inner: self }),
739+
None => None,
740+
}
741+
}
742+
}
743+
744+
impl<'a> fmt::Debug for DirectedChannelInfo<'a> {
745+
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
746+
f.debug_struct("DirectedChannelInfo")
747+
.field("channel", &self.channel)
748+
.finish()
749+
}
750+
}
751+
752+
/// A [`DirectedChannelInfo`] with [`ChannelUpdateInfo`] available in its the direction.
753+
#[derive(Clone)]
754+
pub(super) struct DirectedChannelInfoWithUpdate<'a> {
755+
inner: DirectedChannelInfo<'a>,
756+
}
757+
758+
impl<'a> DirectedChannelInfoWithUpdate<'a> {
759+
/// Returns information for the channel.
760+
#[inline]
761+
pub(super) fn channel(&self) -> &'a ChannelInfo { &self.inner.channel }
762+
763+
/// Returns information for the direction.
764+
#[inline]
765+
pub(super) fn direction(&self) -> &'a ChannelUpdateInfo { self.inner.direction.unwrap() }
766+
767+
/// Returns the [`EffectiveCapacity`] of the channel in the direction.
768+
#[inline]
769+
pub(super) fn effective_capacity(&self) -> EffectiveCapacity { self.inner.effective_capacity() }
770+
}
771+
772+
impl<'a> fmt::Debug for DirectedChannelInfoWithUpdate<'a> {
773+
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
774+
self.inner.fmt(f)
775+
}
776+
}
777+
778+
/// The effective capacity of a channel for routing purposes.
779+
///
780+
/// While this may be smaller than the actual channel capacity, amounts greater than
781+
/// [`Self::as_msat`] should not be routed through the channel.
782+
pub enum EffectiveCapacity {
783+
/// The available liquidity in the channel known from being a channel counterparty, and thus a
784+
/// direct hop.
785+
ExactLiquidity {
786+
/// Either the inbound or outbound liquidity depending on the direction, denominated in
787+
/// millisatoshi.
788+
liquidity_msat: u64,
789+
},
790+
/// The maximum HTLC amount in one direction as advertised on the gossip network.
791+
MaximumHTLC {
792+
/// The maximum HTLC amount denominated in millisatoshi.
793+
amount_msat: u64,
794+
},
795+
/// The total capacity of the channel as determined by the funding transaction.
796+
Total {
797+
/// The funding amount denominated in millisatoshi.
798+
capacity_msat: u64,
799+
},
800+
/// A capacity sufficient to route any payment, typically used for private channels provided by
801+
/// an invoice.
802+
Infinite,
803+
/// A capacity that is unknown possibly because either the chain state is unavailable to know
804+
/// the total capacity or the `htlc_maximum_msat` was not advertised on the gossip network.
805+
Unknown,
806+
}
807+
808+
/// The presumed channel capacity denominated in millisatoshi for [`EffectiveCapacity::Unknown`] to
809+
/// use when making routing decisions.
810+
pub const UNKNOWN_CHANNEL_CAPACITY_MSAT: u64 = 250_000 * 1000;
811+
812+
impl EffectiveCapacity {
813+
/// Returns the effective capacity denominated in millisatoshi.
814+
pub fn as_msat(&self) -> u64 {
815+
match self {
816+
EffectiveCapacity::ExactLiquidity { liquidity_msat } => *liquidity_msat,
817+
EffectiveCapacity::MaximumHTLC { amount_msat } => *amount_msat,
818+
EffectiveCapacity::Total { capacity_msat } => *capacity_msat,
819+
EffectiveCapacity::Infinite => u64::max_value(),
820+
EffectiveCapacity::Unknown => UNKNOWN_CHANNEL_CAPACITY_MSAT,
821+
}
822+
}
823+
}
682824

683825
/// Fees for routing via a given channel or a node
684826
#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
@@ -1227,7 +1369,7 @@ impl NetworkGraph {
12271369
let last_update_message = if msg.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY
12281370
{ full_msg.cloned() } else { None };
12291371

1230-
let updated_channel_dir_info = DirectionalChannelInfo {
1372+
let updated_channel_update_info = ChannelUpdateInfo {
12311373
enabled: chan_enabled,
12321374
last_update: msg.timestamp,
12331375
cltv_expiry_delta: msg.cltv_expiry_delta,
@@ -1239,7 +1381,7 @@ impl NetworkGraph {
12391381
},
12401382
last_update_message
12411383
};
1242-
$target = Some(updated_channel_dir_info);
1384+
$target = Some(updated_channel_update_info);
12431385
}
12441386
}
12451387

0 commit comments

Comments
 (0)