Skip to content

Commit 3c2df0c

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 35016a1 commit 3c2df0c

File tree

4 files changed

+357
-188
lines changed

4 files changed

+357
-188
lines changed

lightning-invoice/src/payment.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
//! # }
8383
//! # impl Score for FakeScorer {
8484
//! # fn channel_penalty_msat(
85-
//! # &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
85+
//! # &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: u64, _source: &NodeId, _target: &NodeId
8686
//! # ) -> u64 { 0 }
8787
//! # fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
8888
//! # fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
@@ -1295,7 +1295,7 @@ mod tests {
12951295

12961296
impl Score for TestScorer {
12971297
fn channel_penalty_msat(
1298-
&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
1298+
&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: u64, _source: &NodeId, _target: &NodeId
12991299
) -> u64 { 0 }
13001300

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

lightning/src/routing/network_graph.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,48 @@ pub struct ChannelInfo {
649649
announcement_received_time: u64,
650650
}
651651

652+
impl ChannelInfo {
653+
/// Returns a [`DirectedChannelInfo`] for the channel from `source` to `target`.
654+
///
655+
/// # Panics
656+
///
657+
/// Panics if `source` and `target` are not the channel's counterparties.
658+
pub fn as_directed(&self, source: &NodeId, target: &NodeId) -> DirectedChannelInfo {
659+
let (direction, source, target) = {
660+
if source == &self.node_one && target == &self.node_two {
661+
(self.one_to_two.as_ref(), &self.node_one, &self.node_two)
662+
} else if source == &self.node_two && target == &self.node_one {
663+
(self.two_to_one.as_ref(), &self.node_two, &self.node_one)
664+
} else if source != &self.node_one && source != &self.node_two {
665+
panic!("Unknown source node: {:?}", source)
666+
} else if target != &self.node_one && target != &self.node_two {
667+
panic!("Unknown target node: {:?}", target)
668+
} else {
669+
unreachable!()
670+
}
671+
};
672+
DirectedChannelInfo { channel: self, direction, source, target }
673+
}
674+
675+
/// Returns a [`DirectedChannelInfo`] for the channel directed to the given `target`.
676+
///
677+
/// # Panics
678+
///
679+
/// Panics if `target` is not one of the channel's counterparties.
680+
pub fn directed_to(&self, target: &NodeId) -> DirectedChannelInfo {
681+
let (direction, source, target) = {
682+
if target == &self.node_one {
683+
(self.two_to_one.as_ref(), &self.node_two, &self.node_one)
684+
} else if target == &self.node_two {
685+
(self.one_to_two.as_ref(), &self.node_one, &self.node_two)
686+
} else {
687+
panic!("Unknown target node: {:?}", target)
688+
}
689+
};
690+
DirectedChannelInfo { channel: self, direction, source, target }
691+
}
692+
}
693+
652694
impl fmt::Display for ChannelInfo {
653695
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
654696
write!(f, "features: {}, node_one: {}, one_to_two: {:?}, node_two: {}, two_to_one: {:?}",
@@ -668,6 +710,105 @@ impl_writeable_tlv_based!(ChannelInfo, {
668710
(12, announcement_message, required),
669711
});
670712

713+
/// A wrapper around [`ChannelInfo`] representing information about the channel as directed from a
714+
/// source node to a target node.
715+
pub struct DirectedChannelInfo<'a: 'b, 'b> {
716+
channel: &'a ChannelInfo,
717+
direction: Option<&'b DirectionalChannelInfo>,
718+
source: &'b NodeId,
719+
target: &'b NodeId,
720+
}
721+
722+
impl<'a: 'b, 'b> DirectedChannelInfo<'a, 'b> {
723+
/// Returns the node id for the source.
724+
pub fn source(&self) -> &'b NodeId { self.source }
725+
726+
/// Returns the node id for the target.
727+
pub fn target(&self) -> &'b NodeId { self.target }
728+
729+
/// Consumes the [`DirectedChannelInfo`], returning the wrapped parts.
730+
pub fn into_parts(self) -> (&'a ChannelInfo, Option<&'b DirectionalChannelInfo>) {
731+
(self.channel, self.direction)
732+
}
733+
734+
/// Returns the [`EffectiveCapacity`] of the channel in a specific direction.
735+
///
736+
/// This is either the total capacity from the funding transaction, if known, or the
737+
/// `htlc_maximum_msat` for the direction as advertised by the gossip network, if known,
738+
/// whichever is smaller.
739+
pub fn effective_capacity(&self) -> EffectiveCapacity {
740+
Self::effective_capacity_from_parts(self.channel, self.direction)
741+
}
742+
743+
/// Returns the [`EffectiveCapacity`] of the channel in the given direction.
744+
///
745+
/// See [`Self::effective_capacity`] for details.
746+
pub fn effective_capacity_from_parts(
747+
channel: &ChannelInfo, direction: Option<&DirectionalChannelInfo>
748+
) -> EffectiveCapacity {
749+
let capacity_msat = channel.capacity_sats.map(|capacity_sats| capacity_sats * 1000);
750+
direction
751+
.and_then(|direction| direction.htlc_maximum_msat)
752+
.map(|max_htlc_msat| {
753+
let capacity_msat = capacity_msat.unwrap_or(u64::max_value());
754+
if max_htlc_msat < capacity_msat {
755+
EffectiveCapacity::MaximumHTLC { amount_msat: max_htlc_msat }
756+
} else {
757+
EffectiveCapacity::Total { capacity_msat }
758+
}
759+
})
760+
.or_else(|| capacity_msat.map(|capacity_msat|
761+
EffectiveCapacity::Total { capacity_msat }))
762+
.unwrap_or(EffectiveCapacity::Unknown)
763+
}
764+
}
765+
766+
/// The effective capacity of a channel for routing purposes.
767+
///
768+
/// While this may be smaller than the actual channel capacity, amounts greater than
769+
/// [`Self::as_msat`] should not be routed through the channel.
770+
pub enum EffectiveCapacity {
771+
/// The available liquidity in the channel known from being a channel counterparty, and thus a
772+
/// direct hop.
773+
ExactLiquidity {
774+
/// Either the inbound or outbound liquidity depending on the direction, denominated in
775+
/// millisatoshi.
776+
liquidity_msast: u64,
777+
},
778+
/// The maximum HTLC amount in one direction as advertised on the gossip network.
779+
MaximumHTLC {
780+
/// The maximum HTLC amount denominated in millisatoshi.
781+
amount_msat: u64,
782+
},
783+
/// The total capacity of the channel as determined by the funding transaction.
784+
Total {
785+
/// The funding amount denominated in millisatoshi.
786+
capacity_msat: u64,
787+
},
788+
/// A capacity sufficient to route any payment, typically used for private channels provided by
789+
/// an invoice, though may not be the case for zero-amount invoices.
790+
Infinite,
791+
/// A capacity that is unknown possibly because either the chain state is unavailable to know
792+
/// the total capacity or the `htlc_maximum_msat` was not advertised on the gossip network.
793+
Unknown,
794+
}
795+
796+
/// The presumed channel capacity denominated in millisatoshi for [`EffectiveCapacity::Unknown`] to
797+
/// use when making routing decisions.
798+
pub const UNKNOWN_CHANNEL_CAPACITY_MSAT: u64 = 250_000 * 1000;
799+
800+
impl EffectiveCapacity {
801+
/// Returns the effective capacity denominated in millisatoshi.
802+
pub fn as_msat(&self) -> u64 {
803+
match self {
804+
EffectiveCapacity::ExactLiquidity { liquidity_msast } => *liquidity_msast,
805+
EffectiveCapacity::MaximumHTLC { amount_msat } => *amount_msat,
806+
EffectiveCapacity::Total { capacity_msat } => *capacity_msat,
807+
EffectiveCapacity::Infinite => u64::max_value(),
808+
EffectiveCapacity::Unknown => UNKNOWN_CHANNEL_CAPACITY_MSAT,
809+
}
810+
}
811+
}
671812

672813
/// Fees for routing via a given channel or a node
673814
#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]

0 commit comments

Comments
 (0)