///
/// Scoring is in terms of fees willing to be paid in order to avoid routing through a channel.
pub trait Score {
- /// Returns the fee in msats willing to be paid to avoid routing through the given channel
- /// in the direction from `source` to `target`.
- fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId) -> u64;
+ /// Returns the fee in msats willing to be paid to avoid routing `send_amt_msat` through the
+ /// given channel in the direction from `source` to `target`.
+ ///
+ /// The channel's capacity (less any other MPP parts which are also being considered for use in
+ /// the same payment) is given by `channel_capacity_msat`. It may be guessed from various
+ /// sources or assumed from no data at all.
+ ///
+ /// For hints provided in the invoice, we assume the channel has sufficient capacity to accept
+ /// the invoice's full amount, and provide a `channel_capacity_msat` of `None`. In all other
+ /// cases it is set to `Some`, even if we're guessing at the channel value.
+ ///
+ /// Your code should be overflow-safe through a `channel_capacity_msat` of 21 million BTC.
+ fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, channel_capacity_msat: Option<u64>, source: &NodeId, target: &NodeId) -> u64;
/// Handles updating channel penalties after failing to route through a channel.
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64);
}
impl<S: Score, T: DerefMut<Target=S>> Score for T {
- fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId) -> u64 {
- self.deref().channel_penalty_msat(short_channel_id, source, target)
+ fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, channel_capacity_msat: Option<u64>, source: &NodeId, target: &NodeId) -> u64 {
+ self.deref().channel_penalty_msat(short_channel_id, send_amt_msat, channel_capacity_msat, source, target)
}
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
}
}
- let path_penalty_msat = $next_hops_path_penalty_msat
- .checked_add(scorer.channel_penalty_msat($chan_id.clone(), &$src_node_id, &$dest_node_id))
- .unwrap_or_else(|| u64::max_value());
+ let path_penalty_msat = $next_hops_path_penalty_msat.checked_add(
+ scorer.channel_penalty_msat($chan_id.clone(), amount_to_transfer_over_msat, Some(*available_liquidity_msat),
+ &$src_node_id, &$dest_node_id)).unwrap_or_else(|| u64::max_value());
let new_graph_node = RouteGraphNode {
node_id: $src_node_id,
lowest_fee_to_peer_through_node: total_fee_msat,
let src_node_id = NodeId::from_pubkey(&hop.src_node_id);
let dest_node_id = NodeId::from_pubkey(&prev_hop_id);
aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
- .checked_add(scorer.channel_penalty_msat(hop.short_channel_id, &src_node_id, &dest_node_id))
+ .checked_add(scorer.channel_penalty_msat(hop.short_channel_id, final_value_msat, None, &src_node_id, &dest_node_id))
.unwrap_or_else(|| u64::max_value());
// We assume that the recipient only included route hints for routes which had
}
impl routing::Score for BadChannelScorer {
- fn channel_penalty_msat(&self, short_channel_id: u64, _source: &NodeId, _target: &NodeId) -> u64 {
+ fn channel_penalty_msat(&self, short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId) -> u64 {
if short_channel_id == self.short_channel_id { u64::max_value() } else { 0 }
}
}
impl routing::Score for BadNodeScorer {
- fn channel_penalty_msat(&self, _short_channel_id: u64, _source: &NodeId, target: &NodeId) -> u64 {
+ fn channel_penalty_msat(&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, target: &NodeId) -> u64 {
if *target == self.node_id { u64::max_value() } else { 0 }
}
impl<T: Time> routing::Score for ScorerUsingTime<T> {
fn channel_penalty_msat(
- &self, short_channel_id: u64, _source: &NodeId, _target: &NodeId
+ &self, short_channel_id: u64, _send_amt_msat: u64, _chan_capacity_msat: Option<u64>, _source: &NodeId, _target: &NodeId
) -> u64 {
let failure_penalty_msat = self.channel_failures
.get(&short_channel_id)
});
let source = source_node_id();
let target = target_node_id();
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
SinceEpoch::advance(Duration::from_secs(1));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
}
#[test]
});
let source = source_node_id();
let target = target_node_id();
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
scorer.payment_path_failed(&[], 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_064);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_064);
scorer.payment_path_failed(&[], 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_128);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_128);
scorer.payment_path_failed(&[], 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_192);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_192);
}
#[test]
});
let source = source_node_id();
let target = target_node_id();
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
scorer.payment_path_failed(&[], 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
SinceEpoch::advance(Duration::from_secs(9));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
SinceEpoch::advance(Duration::from_secs(1));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_256);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
SinceEpoch::advance(Duration::from_secs(10 * 8));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_001);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_001);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
}
#[test]
});
let source = source_node_id();
let target = target_node_id();
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
scorer.payment_path_failed(&[], 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_256);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
scorer.payment_path_failed(&[], 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_768);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_768);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_384);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_384);
}
#[test]
let target = target_node_id();
scorer.payment_path_failed(&[], 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_256);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
scorer.payment_path_failed(&[], 43);
- assert_eq!(scorer.channel_penalty_msat(43, &source, &target), 1_512);
+ assert_eq!(scorer.channel_penalty_msat(43, 1, Some(1), &source, &target), 1_512);
let mut serialized_scorer = Vec::new();
scorer.write(&mut serialized_scorer).unwrap();
let deserialized_scorer = <Scorer>::read(&mut io::Cursor::new(&serialized_scorer)).unwrap();
- assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target), 1_256);
- assert_eq!(deserialized_scorer.channel_penalty_msat(43, &source, &target), 1_512);
+ assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
+ assert_eq!(deserialized_scorer.channel_penalty_msat(43, 1, Some(1), &source, &target), 1_512);
}
#[test]
let target = target_node_id();
scorer.payment_path_failed(&[], 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
+ assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
let mut serialized_scorer = Vec::new();
scorer.write(&mut serialized_scorer).unwrap();
SinceEpoch::advance(Duration::from_secs(10));
let deserialized_scorer = <Scorer>::read(&mut io::Cursor::new(&serialized_scorer)).unwrap();
- assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target), 1_256);
+ assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target), 1_128);
+ assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_128);
}
}