//! The router finds paths within a [`NetworkGraph`] for a payment.
use bitcoin::secp256k1::{PublicKey, Secp256k1, self};
-use bitcoin::hashes::Hash;
-use bitcoin::hashes::sha256::Hash as Sha256;
use crate::blinded_path::{BlindedHop, BlindedPath};
use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs};
use crate::ln::features::{BlindedHopFeatures, Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
-use crate::onion_message::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
+use crate::onion_message::messenger::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath};
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
use crate::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp};
use crate::sign::EntropySource;
use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer};
use crate::util::logger::{Level, Logger};
-use crate::util::chacha20::ChaCha20;
+use crate::crypto::chacha20::ChaCha20;
use crate::io;
use crate::prelude::*;
-use crate::sync::Mutex;
use alloc::collections::BinaryHeap;
use core::{cmp, fmt};
use core::ops::Deref;
/// A [`Router`] implemented using [`find_route`].
-pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> where
+pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> where
L::Target: Logger,
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
+ ES::Target: EntropySource,
{
network_graph: G,
logger: L,
- random_seed_bytes: Mutex<[u8; 32]>,
+ entropy_source: ES,
scorer: S,
score_params: SP,
- message_router: DefaultMessageRouter<G, L>,
+ message_router: DefaultMessageRouter<G, L, ES>,
}
-impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> DefaultRouter<G, L, S, SP, Sc> where
+impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref + Clone, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> DefaultRouter<G, L, ES, S, SP, Sc> where
L::Target: Logger,
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
+ ES::Target: EntropySource,
{
/// Creates a new router.
- pub fn new(network_graph: G, logger: L, random_seed_bytes: [u8; 32], scorer: S, score_params: SP) -> Self {
- let random_seed_bytes = Mutex::new(random_seed_bytes);
- let message_router = DefaultMessageRouter::new(network_graph.clone());
- Self { network_graph, logger, random_seed_bytes, scorer, score_params, message_router }
+ pub fn new(network_graph: G, logger: L, entropy_source: ES, scorer: S, score_params: SP) -> Self {
+ let message_router = DefaultMessageRouter::new(network_graph.clone(), entropy_source.clone());
+ Self { network_graph, logger, entropy_source, scorer, score_params, message_router }
}
}
-impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> Router for DefaultRouter<G, L, S, SP, Sc> where
+impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> Router for DefaultRouter<G, L, ES, S, SP, Sc> where
L::Target: Logger,
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
+ ES::Target: EntropySource,
{
fn find_route(
&self,
first_hops: Option<&[&ChannelDetails]>,
inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError> {
- let random_seed_bytes = {
- let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
- *locked_random_seed_bytes = Sha256::hash(&*locked_random_seed_bytes).to_byte_array();
- *locked_random_seed_bytes
- };
+ let random_seed_bytes = self.entropy_source.get_secure_random_bytes();
find_route(
payer, params, &self.network_graph, first_hops, &*self.logger,
&ScorerAccountingForInFlightHtlcs::new(self.scorer.read_lock(), &inflight_htlcs),
}
fn create_blinded_payment_paths<
- ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
- >(
+ T: secp256k1::Signing + secp256k1::Verification
+ > (
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
- amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
+ amount_msats: u64, secp_ctx: &Secp256k1<T>
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
// Limit the number of blinded paths that are computed.
const MAX_PAYMENT_PATHS: usize = 3;
None => return None,
};
let payment_relay: PaymentRelay = match details.counterparty.forwarding_info {
- Some(forwarding_info) => forwarding_info.into(),
+ Some(forwarding_info) => match forwarding_info.try_into() {
+ Ok(payment_relay) => payment_relay,
+ Err(()) => return None,
+ },
None => return None,
};
- // Avoid exposing esoteric CLTV expiry deltas
- let cltv_expiry_delta = match payment_relay.cltv_expiry_delta {
- 0..=40 => 40u32,
- 41..=80 => 80u32,
- 81..=144 => 144u32,
- 145..=216 => 216u32,
- _ => return None,
- };
-
+ let cltv_expiry_delta = payment_relay.cltv_expiry_delta as u32;
let payment_constraints = PaymentConstraints {
max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta,
htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0),
})
.map(|forward_node| {
BlindedPath::new_for_payment(
- &[forward_node], recipient, tlvs.clone(), u64::MAX, entropy_source, secp_ctx
+ &[forward_node], recipient, tlvs.clone(), u64::MAX, &*self.entropy_source, secp_ctx
)
})
.take(MAX_PAYMENT_PATHS)
Ok(paths) if !paths.is_empty() => Ok(paths),
_ => {
if network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient)) {
- BlindedPath::one_hop_for_payment(recipient, tlvs, entropy_source, secp_ctx)
+ BlindedPath::one_hop_for_payment(recipient, tlvs, &*self.entropy_source, secp_ctx)
.map(|path| vec![path])
} else {
Err(())
}
}
-impl< G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> MessageRouter for DefaultRouter<G, L, S, SP, Sc> where
+impl< G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref, ES: Deref, S: Deref, SP: Sized, Sc: ScoreLookUp<ScoreParams = SP>> MessageRouter for DefaultRouter<G, L, ES, S, SP, Sc> where
L::Target: Logger,
S::Target: for <'a> LockableScore<'a, ScoreLookUp = Sc>,
+ ES::Target: EntropySource,
{
fn find_path(
&self, sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
}
fn create_blinded_paths<
- ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
- >(
- &self, recipient: PublicKey, peers: Vec<PublicKey>, entropy_source: &ES,
- secp_ctx: &Secp256k1<T>
+ T: secp256k1::Signing + secp256k1::Verification
+ > (
+ &self, recipient: PublicKey, peers: Vec<PublicKey>, secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedPath>, ()> {
- self.message_router.create_blinded_paths(recipient, peers, entropy_source, secp_ctx)
+ self.message_router.create_blinded_paths(recipient, peers, secp_ctx)
}
}
/// are assumed to be with the `recipient`'s peers. The payment secret and any constraints are
/// given in `tlvs`.
fn create_blinded_payment_paths<
- ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification
- >(
+ T: secp256k1::Signing + secp256k1::Verification
+ > (
&self, recipient: PublicKey, first_hops: Vec<ChannelDetails>, tlvs: ReceiveTlvs,
- amount_msats: u64, entropy_source: &ES, secp_ctx: &Secp256k1<T>
+ amount_msats: u64, secp_ctx: &Secp256k1<T>
) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()>;
}
/// has been funded and is able to pay), and accessor methods may panic otherwise.
///
/// [`find_route`] validates this prior to constructing a [`CandidateRouteHop`].
+ ///
+ /// This is not exported to bindings users as lifetimes are not expressable in most languages.
pub details: &'a ChannelDetails,
/// The node id of the payer, which is also the source side of this candidate route hop.
+ ///
+ /// This is not exported to bindings users as lifetimes are not expressable in most languages.
pub payer_node_id: &'a NodeId,
}
pub struct PublicHopCandidate<'a> {
/// Information about the channel, including potentially its capacity and
/// direction-specific information.
+ ///
+ /// This is not exported to bindings users as lifetimes are not expressable in most languages.
pub info: DirectedChannelInfo<'a>,
/// The short channel ID of the channel, i.e. the identifier by which we refer to this
/// channel.
#[derive(Clone, Debug)]
pub struct PrivateHopCandidate<'a> {
/// Information about the private hop communicated via BOLT 11.
+ ///
+ /// This is not exported to bindings users as lifetimes are not expressable in most languages.
pub hint: &'a RouteHintHop,
/// Node id of the next hop in BOLT 11 route hint.
+ ///
+ /// This is not exported to bindings users as lifetimes are not expressable in most languages.
pub target_node_id: &'a NodeId
}
pub struct BlindedPathCandidate<'a> {
/// Information about the blinded path including the fee, HTLC amount limits, and
/// cryptographic material required to build an HTLC through the given path.
+ ///
+ /// This is not exported to bindings users as lifetimes are not expressable in most languages.
pub hint: &'a (BlindedPayInfo, BlindedPath),
/// Index of the hint in the original list of blinded hints.
///
/// cryptographic material required to build an HTLC terminating with the given path.
///
/// Note that the [`BlindedPayInfo`] is ignored here.
+ ///
+ /// This is not exported to bindings users as lifetimes are not expressable in most languages.
pub hint: &'a (BlindedPayInfo, BlindedPath),
/// Index of the hint in the original list of blinded hints.
///
_ => None,
}
}
+ fn blinded_hint_idx(&self) -> Option<usize> {
+ match self {
+ Self::Blinded(BlindedPathCandidate { hint_idx, .. }) |
+ Self::OneHopBlinded(OneHopBlindedPathCandidate { hint_idx, .. }) => {
+ Some(*hint_idx)
+ },
+ _ => None,
+ }
+ }
/// Returns the source node id of current hop.
///
/// Source node id refers to the node forwarding the HTLC through this hop.
(amount_to_transfer_over_msat < $next_hops_path_htlc_minimum_msat &&
recommended_value_msat >= $next_hops_path_htlc_minimum_msat));
- let payment_failed_on_this_channel = scid_opt.map_or(false,
- |scid| payment_params.previously_failed_channels.contains(&scid));
+ let payment_failed_on_this_channel = match scid_opt {
+ Some(scid) => payment_params.previously_failed_channels.contains(&scid),
+ None => match $candidate.blinded_hint_idx() {
+ Some(idx) => {
+ payment_params.previously_failed_blinded_path_idxs.contains(&(idx as u64))
+ },
+ None => false,
+ },
+ };
let (should_log_candidate, first_hop_details) = match $candidate {
CandidateRouteHop::FirstHop(hop) => (true, Some(hop.details)),
}
}
- // Means we succesfully traversed from the payer to the payee, now
+ // Means we successfully traversed from the payer to the payee, now
// save this path for the payment route. Also, update the liquidity
// remaining on the used hops, so that we take them into account
// while looking for more paths.
use crate::offers::invoice::BlindedPayInfo;
use crate::util::config::UserConfig;
use crate::util::test_utils as ln_test_utils;
- use crate::util::chacha20::ChaCha20;
+ use crate::crypto::chacha20::ChaCha20;
use crate::util::ser::{Readable, Writeable};
#[cfg(c_bindings)]
use crate::util::ser::Writer;
(route.paths[1].hops[1].short_channel_id == 4 && route.paths[0].hops[1].short_channel_id == 13));
}
- #[cfg(not(feature = "no-std"))]
+ #[cfg(feature = "std")]
pub(super) fn random_init_seed() -> u64 {
// Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.
use core::hash::{BuildHasher, Hasher};
}
#[test]
- #[cfg(not(feature = "no-std"))]
+ #[cfg(feature = "std")]
fn generate_routes() {
use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
}
#[test]
- #[cfg(not(feature = "no-std"))]
+ #[cfg(feature = "std")]
fn generate_routes_mpp() {
use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
}
#[test]
- #[cfg(not(feature = "no-std"))]
+ #[cfg(feature = "std")]
fn generate_large_mpp_routes() {
use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
fn do_min_htlc_overpay_violates_max_htlc(blinded_payee: bool) {
// Test that if overpaying to meet a later hop's min_htlc and causes us to violate an earlier
// hop's max_htlc, we don't consider that candidate hop valid. Previously we would add this hop
- // to `targets` and build an invalid path with it, and subsquently hit a debug panic asserting
+ // to `targets` and build an invalid path with it, and subsequently hit a debug panic asserting
// that the used liquidity for a hop was less than its available liquidity limit.
let secp_ctx = Secp256k1::new();
let logger = Arc::new(ln_test_utils::TestLogger::new());
}
}
-#[cfg(all(any(test, ldk_bench), not(feature = "no-std")))]
+#[cfg(all(any(test, ldk_bench), feature = "std"))]
pub(crate) mod bench_utils {
use super::*;
use std::fs::File;
}
break;
}
- // If we couldn't find a path with a higer amount, reduce and try again.
+ // If we couldn't find a path with a higher amount, reduce and try again.
score_amt /= 100;
}