X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Frouter.rs;h=207b0903e6183056824fcc97c1b116de3f1f3c4d;hb=d74c143afe2244c39c66adef0dd13e64bdcf6681;hp=81312980a5a1550137078ab69506c894c6d35c69;hpb=baa727ea3867a823ff6fb51abae1d7da534628cb;p=rust-lightning diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 81312980..207b0903 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -12,11 +12,14 @@ use bitcoin::secp256k1::{PublicKey, Secp256k1, self}; use crate::blinded_path::{BlindedHop, BlindedPath, Direction, IntroductionNode}; -use crate::blinded_path::payment::{ForwardNode, ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs}; -use crate::ln::types::PaymentHash; -use crate::ln::channelmanager::{ChannelDetails, PaymentId, MIN_FINAL_CLTV_EXPIRY_DELTA}; +use crate::blinded_path::message; +use crate::blinded_path::payment::{ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs, self}; +use crate::ln::{PaymentHash, PaymentPreimage}; +use crate::ln::channel_state::ChannelDetails; +use crate::ln::channelmanager::{PaymentId, MIN_FINAL_CLTV_EXPIRY_DELTA, RecipientOnionFields}; use crate::ln::features::{BlindedHopFeatures, Bolt11InvoiceFeatures, Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures}; use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT}; +use crate::ln::onion_utils; use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice}; use crate::onion_message::messenger::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath}; use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees}; @@ -121,7 +124,7 @@ impl> + Clone, L: Deref, ES: Deref, S: Deref, max_cltv_expiry: tlvs.payment_constraints.max_cltv_expiry + cltv_expiry_delta, htlc_minimum_msat: details.inbound_htlc_minimum_msat.unwrap_or(0), }; - Some(ForwardNode { + Some(payment::ForwardNode { tlvs: ForwardTlvs { short_channel_id, payment_relay, @@ -170,7 +173,7 @@ impl< G: Deref> + Clone, L: Deref, ES: Deref, S: Deref, fn create_blinded_paths< T: secp256k1::Signing + secp256k1::Verification > ( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { self.message_router.create_blinded_paths(recipient, peers, secp_ctx) } @@ -603,6 +606,17 @@ impl RouteParameters { pub fn from_payment_params_and_value(payment_params: PaymentParameters, final_value_msat: u64) -> Self { Self { payment_params, final_value_msat, max_total_routing_fee_msat: Some(final_value_msat / 100 + 50_000) } } + + /// Sets the maximum number of hops that can be included in a payment path, based on the provided + /// [`RecipientOnionFields`] and blinded paths. + pub fn set_max_path_length( + &mut self, recipient_onion: &RecipientOnionFields, is_keysend: bool, best_block_height: u32 + ) -> Result<(), ()> { + let keysend_preimage_opt = is_keysend.then(|| PaymentPreimage([42; 32])); + onion_utils::set_max_path_length( + self, recipient_onion, keysend_preimage_opt, best_block_height + ) + } } impl Writeable for RouteParameters { @@ -1996,17 +2010,34 @@ where L::Target: Logger { true } else if let Some(payee) = payee_node_id_opt { network_nodes.get(&payee).map_or(false, |node| node.announcement_info.as_ref().map_or(false, - |info| info.features.supports_basic_mpp())) + |info| info.features().supports_basic_mpp())) } else { false }; let max_total_routing_fee_msat = route_params.max_total_routing_fee_msat.unwrap_or(u64::max_value()); - log_trace!(logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph with a fee limit of {} msat", + let first_hop_count = first_hops.map(|hops| hops.len()).unwrap_or(0); + log_trace!(logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph of {} nodes and {} channels with a fee limit of {} msat", our_node_pubkey, LoggedPayeePubkey(payment_params.payee.node_id()), if allow_mpp { "with" } else { "without" }, - first_hops.map(|hops| hops.len()).unwrap_or(0), if first_hops.is_some() { "" } else { "not " }, + first_hop_count, if first_hops.is_some() { "" } else { "not " }, + network_graph.nodes().len(), network_graph.channels().len(), max_total_routing_fee_msat); + if first_hop_count < 10 { + if let Some(hops) = first_hops { + for hop in hops { + log_trace!( + logger, + " First hop through {}/{} can send between {}msat and {}msat (inclusive).", + hop.counterparty.node_id, + hop.get_outbound_payment_scid().unwrap_or(0), + hop.next_outbound_htlc_minimum_msat, + hop.next_outbound_htlc_limit_msat + ); + } + } + } + // Step (1). // Prepare the data we'll use for payee-to-payer search by // inserting first hops suggested by the caller as targets. @@ -2213,14 +2244,9 @@ where L::Target: Logger { // around again with a higher amount. if !contributes_sufficient_value { if should_log_candidate { - log_trace!(logger, "Ignoring {} due to insufficient value contribution.", LoggedCandidateHop(&$candidate)); - - if let Some(details) = first_hop_details { - log_trace!(logger, - "First hop candidate next_outbound_htlc_limit_msat: {}", - details.next_outbound_htlc_limit_msat, - ); - } + log_trace!(logger, "Ignoring {} due to insufficient value contribution (channel max {:?}).", + LoggedCandidateHop(&$candidate), + effective_capacity); } num_ignored_value_contribution += 1; } else if exceeds_max_path_length { @@ -2249,15 +2275,8 @@ where L::Target: Logger { } else if may_overpay_to_meet_path_minimum_msat { if should_log_candidate { log_trace!(logger, - "Ignoring {} to avoid overpaying to meet htlc_minimum_msat limit.", - LoggedCandidateHop(&$candidate)); - - if let Some(details) = first_hop_details { - log_trace!(logger, - "First hop candidate next_outbound_htlc_minimum_msat: {}", - details.next_outbound_htlc_minimum_msat, - ); - } + "Ignoring {} to avoid overpaying to meet htlc_minimum_msat limit ({}).", + LoggedCandidateHop(&$candidate), $candidate.htlc_minimum_msat()); } num_ignored_avoid_overpayment += 1; hit_minimum_limit = true; @@ -2481,7 +2500,7 @@ where L::Target: Logger { } let features = if let Some(node_info) = $node.announcement_info.as_ref() { - &node_info.features + &node_info.features() } else { &default_node_features }; @@ -2810,7 +2829,7 @@ where L::Target: Logger { if !features_set { if let Some(node) = network_nodes.get(&target) { if let Some(node_info) = node.announcement_info.as_ref() { - ordered_hops.last_mut().unwrap().1 = node_info.features.clone(); + ordered_hops.last_mut().unwrap().1 = node_info.features().clone(); } else { ordered_hops.last_mut().unwrap().1 = default_node_features.clone(); } @@ -3303,6 +3322,7 @@ mod tests { use crate::routing::test_utils::{add_channel, add_or_update_node, build_graph, build_line_graph, id_to_feature_flags, get_nodes, update_channel}; use crate::chain::transaction::OutPoint; use crate::sign::EntropySource; + use crate::ln::channel_state::{ChannelCounterparty, ChannelDetails, ChannelShutdownState}; use crate::ln::types::ChannelId; use crate::ln::features::{BlindedHopFeatures, ChannelFeatures, InitFeatures, NodeFeatures}; use crate::ln::msgs::{ErrorAction, LightningError, UnsignedChannelUpdate, MAX_VALUE_MSAT}; @@ -3315,8 +3335,9 @@ mod tests { #[cfg(c_bindings)] use crate::util::ser::Writer; + use bitcoin::amount::Amount; use bitcoin::hashes::Hash; - use bitcoin::network::constants::Network; + use bitcoin::network::Network; use bitcoin::blockdata::constants::ChainHash; use bitcoin::blockdata::script::Builder; use bitcoin::blockdata::opcodes; @@ -3330,10 +3351,10 @@ mod tests { use crate::sync::Arc; fn get_channel_details(short_channel_id: Option, node_id: PublicKey, - features: InitFeatures, outbound_capacity_msat: u64) -> channelmanager::ChannelDetails { - channelmanager::ChannelDetails { + features: InitFeatures, outbound_capacity_msat: u64) -> ChannelDetails { + ChannelDetails { channel_id: ChannelId::new_zero(), - counterparty: channelmanager::ChannelCounterparty { + counterparty: ChannelCounterparty { features, node_id, unspendable_punishment_reserve: 0, @@ -3363,7 +3384,7 @@ mod tests { inbound_htlc_maximum_msat: None, config: None, feerate_sat_per_1000_weight: None, - channel_shutdown_state: Some(channelmanager::ChannelShutdownState::NotShuttingDown), + channel_shutdown_state: Some(ChannelShutdownState::NotShuttingDown), pending_inbound_htlcs: Vec::new(), pending_outbound_htlcs: Vec::new(), } @@ -4874,10 +4895,10 @@ mod tests { .push_slice(&PublicKey::from_secret_key(&secp_ctx, &privkeys[0]).serialize()) .push_slice(&PublicKey::from_secret_key(&secp_ctx, &privkeys[2]).serialize()) .push_opcode(opcodes::all::OP_PUSHNUM_2) - .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh(); + .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_p2wsh(); *chain_monitor.utxo_ret.lock().unwrap() = - UtxoResult::Sync(Ok(TxOut { value: 15, script_pubkey: good_script.clone() })); + UtxoResult::Sync(Ok(TxOut { value: Amount::from_sat(15), script_pubkey: good_script.clone() })); gossip_sync.add_utxo_lookup(Some(chain_monitor)); add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 333); @@ -5490,6 +5511,18 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); + update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate { + chain_hash: ChainHash::using_genesis_block(Network::Testnet), + short_channel_id: 5, + timestamp: 2, + flags: 3, // disable direction 1 + cltv_expiry_delta: 0, + htlc_minimum_msat: 0, + htlc_maximum_msat: 200_000, + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); // Path via {node7, node2, node4} is channels {12, 13, 6, 11}. // Add 100 sats to the capacities of {12, 13}, because these channels @@ -5667,6 +5700,18 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); + update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate { + chain_hash: ChainHash::using_genesis_block(Network::Testnet), + short_channel_id: 5, + timestamp: 2, + flags: 3, // disable direction 1 + cltv_expiry_delta: 0, + htlc_minimum_msat: 0, + htlc_maximum_msat: 200_000, + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); // Path via {node7, node2, node4} is channels {12, 13, 6, 11}. // Add 100 sats to the capacities of {12, 13}, because these channels @@ -5840,6 +5885,18 @@ mod tests { fee_proportional_millionths: 0, excess_data: Vec::new() }); + update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate { + chain_hash: ChainHash::using_genesis_block(Network::Testnet), + short_channel_id: 5, + timestamp: 2, + flags: 3, // Disable direction 1 + cltv_expiry_delta: 0, + htlc_minimum_msat: 0, + htlc_maximum_msat: 100_000, + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); // Path via {node7, node2, node4} is channels {12, 13, 6, 11}. // All channels should be 100 sats capacity. But for the fee experiment, @@ -6232,92 +6289,104 @@ mod tests { let payment_params = PaymentParameters::from_node_id(nodes[6], 42); add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6); - update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate { - chain_hash: ChainHash::using_genesis_block(Network::Testnet), - short_channel_id: 6, - timestamp: 1, - flags: 0, - cltv_expiry_delta: (6 << 4) | 0, - htlc_minimum_msat: 0, - htlc_maximum_msat: MAX_VALUE_MSAT, - fee_base_msat: 0, - fee_proportional_millionths: 0, - excess_data: Vec::new() - }); + for (key, flags) in [(&our_privkey, 0), (&privkeys[1], 3)] { + update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate { + chain_hash: ChainHash::using_genesis_block(Network::Testnet), + short_channel_id: 6, + timestamp: 1, + flags, + cltv_expiry_delta: (6 << 4) | 0, + htlc_minimum_msat: 0, + htlc_maximum_msat: MAX_VALUE_MSAT, + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); + } add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[1], NodeFeatures::from_le_bytes(id_to_feature_flags(1)), 0); add_channel(&gossip_sync, &secp_ctx, &privkeys[1], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5); - update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate { - chain_hash: ChainHash::using_genesis_block(Network::Testnet), - short_channel_id: 5, - timestamp: 1, - flags: 0, - cltv_expiry_delta: (5 << 4) | 0, - htlc_minimum_msat: 0, - htlc_maximum_msat: MAX_VALUE_MSAT, - fee_base_msat: 100, - fee_proportional_millionths: 0, - excess_data: Vec::new() - }); + for (key, flags) in [(&privkeys[1], 0), (&privkeys[4], 3)] { + update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate { + chain_hash: ChainHash::using_genesis_block(Network::Testnet), + short_channel_id: 5, + timestamp: 1, + flags, + cltv_expiry_delta: (5 << 4) | 0, + htlc_minimum_msat: 0, + htlc_maximum_msat: MAX_VALUE_MSAT, + fee_base_msat: 100, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); + } add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[4], NodeFeatures::from_le_bytes(id_to_feature_flags(4)), 0); add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(4)), 4); - update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { - chain_hash: ChainHash::using_genesis_block(Network::Testnet), - short_channel_id: 4, - timestamp: 1, - flags: 0, - cltv_expiry_delta: (4 << 4) | 0, - htlc_minimum_msat: 0, - htlc_maximum_msat: MAX_VALUE_MSAT, - fee_base_msat: 0, - fee_proportional_millionths: 0, - excess_data: Vec::new() - }); + for (key, flags) in [(&privkeys[4], 0), (&privkeys[3], 3)] { + update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate { + chain_hash: ChainHash::using_genesis_block(Network::Testnet), + short_channel_id: 4, + timestamp: 1, + flags, + cltv_expiry_delta: (4 << 4) | 0, + htlc_minimum_msat: 0, + htlc_maximum_msat: MAX_VALUE_MSAT, + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); + } add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[3], NodeFeatures::from_le_bytes(id_to_feature_flags(3)), 0); add_channel(&gossip_sync, &secp_ctx, &privkeys[3], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 3); - update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate { - chain_hash: ChainHash::using_genesis_block(Network::Testnet), - short_channel_id: 3, - timestamp: 1, - flags: 0, - cltv_expiry_delta: (3 << 4) | 0, - htlc_minimum_msat: 0, - htlc_maximum_msat: MAX_VALUE_MSAT, - fee_base_msat: 0, - fee_proportional_millionths: 0, - excess_data: Vec::new() - }); + for (key, flags) in [(&privkeys[3], 0), (&privkeys[2], 3)] { + update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate { + chain_hash: ChainHash::using_genesis_block(Network::Testnet), + short_channel_id: 3, + timestamp: 1, + flags, + cltv_expiry_delta: (3 << 4) | 0, + htlc_minimum_msat: 0, + htlc_maximum_msat: MAX_VALUE_MSAT, + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); + } add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[2], NodeFeatures::from_le_bytes(id_to_feature_flags(2)), 0); add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(2)), 2); - update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { - chain_hash: ChainHash::using_genesis_block(Network::Testnet), - short_channel_id: 2, - timestamp: 1, - flags: 0, - cltv_expiry_delta: (2 << 4) | 0, - htlc_minimum_msat: 0, - htlc_maximum_msat: MAX_VALUE_MSAT, - fee_base_msat: 0, - fee_proportional_millionths: 0, - excess_data: Vec::new() - }); + for (key, flags) in [(&privkeys[2], 0), (&privkeys[4], 3)] { + update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate { + chain_hash: ChainHash::using_genesis_block(Network::Testnet), + short_channel_id: 2, + timestamp: 1, + flags, + cltv_expiry_delta: (2 << 4) | 0, + htlc_minimum_msat: 0, + htlc_maximum_msat: MAX_VALUE_MSAT, + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); + } add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[6], ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1); - update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate { - chain_hash: ChainHash::using_genesis_block(Network::Testnet), - short_channel_id: 1, - timestamp: 1, - flags: 0, - cltv_expiry_delta: (1 << 4) | 0, - htlc_minimum_msat: 100, - htlc_maximum_msat: MAX_VALUE_MSAT, - fee_base_msat: 0, - fee_proportional_millionths: 0, - excess_data: Vec::new() - }); + for (key, flags) in [(&privkeys[4], 0), (&privkeys[6], 3)] { + update_channel(&gossip_sync, &secp_ctx, key, UnsignedChannelUpdate { + chain_hash: ChainHash::using_genesis_block(Network::Testnet), + short_channel_id: 1, + timestamp: 1, + flags, + cltv_expiry_delta: (1 << 4) | 0, + htlc_minimum_msat: 100, + htlc_maximum_msat: MAX_VALUE_MSAT, + fee_base_msat: 0, + fee_proportional_millionths: 0, + excess_data: Vec::new() + }); + } add_or_update_node(&gossip_sync, &secp_ctx, &privkeys[6], NodeFeatures::from_le_bytes(id_to_feature_flags(6)), 0); { @@ -7046,11 +7115,11 @@ mod tests { #[test] #[cfg(feature = "std")] fn generate_routes() { - use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; + use crate::routing::scoring::ProbabilisticScoringFeeParameters; let logger = ln_test_utils::TestLogger::new(); - let graph = match super::bench_utils::read_network_graph(&logger) { - Ok(f) => f, + let (graph, mut scorer) = match super::bench_utils::read_graph_scorer(&logger) { + Ok(res) => res, Err(e) => { eprintln!("{}", e); return; @@ -7058,7 +7127,6 @@ mod tests { }; let params = ProbabilisticScoringFeeParameters::default(); - let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger); let features = super::Bolt11InvoiceFeatures::empty(); super::bench_utils::generate_test_routes(&graph, &mut scorer, ¶ms, features, random_init_seed(), 0, 2); @@ -7067,11 +7135,11 @@ mod tests { #[test] #[cfg(feature = "std")] fn generate_routes_mpp() { - use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; + use crate::routing::scoring::ProbabilisticScoringFeeParameters; let logger = ln_test_utils::TestLogger::new(); - let graph = match super::bench_utils::read_network_graph(&logger) { - Ok(f) => f, + let (graph, mut scorer) = match super::bench_utils::read_graph_scorer(&logger) { + Ok(res) => res, Err(e) => { eprintln!("{}", e); return; @@ -7079,7 +7147,6 @@ mod tests { }; let params = ProbabilisticScoringFeeParameters::default(); - let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger); let features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default()); super::bench_utils::generate_test_routes(&graph, &mut scorer, ¶ms, features, random_init_seed(), 0, 2); @@ -7088,11 +7155,11 @@ mod tests { #[test] #[cfg(feature = "std")] fn generate_large_mpp_routes() { - use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; + use crate::routing::scoring::ProbabilisticScoringFeeParameters; let logger = ln_test_utils::TestLogger::new(); - let graph = match super::bench_utils::read_network_graph(&logger) { - Ok(f) => f, + let (graph, mut scorer) = match super::bench_utils::read_graph_scorer(&logger) { + Ok(res) => res, Err(e) => { eprintln!("{}", e); return; @@ -7100,7 +7167,6 @@ mod tests { }; let params = ProbabilisticScoringFeeParameters::default(); - let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger); let features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default()); super::bench_utils::generate_test_routes(&graph, &mut scorer, ¶ms, features, random_init_seed(), 1_000_000, 2); @@ -8429,55 +8495,71 @@ mod tests { pub(crate) mod bench_utils { use super::*; use std::fs::File; - use std::time::Duration; use bitcoin::hashes::Hash; use bitcoin::secp256k1::SecretKey; use crate::chain::transaction::OutPoint; - use crate::routing::scoring::ScoreUpdate; + use crate::routing::scoring::{ProbabilisticScorer, ScoreUpdate}; use crate::sign::KeysManager; + use crate::ln::channel_state::{ChannelCounterparty, ChannelShutdownState}; + use crate::ln::channelmanager; use crate::ln::types::ChannelId; - use crate::ln::channelmanager::{self, ChannelCounterparty}; use crate::util::config::UserConfig; use crate::util::test_utils::TestLogger; + use crate::sync::Arc; /// Tries to open a network graph file, or panics with a URL to fetch it. - pub(crate) fn get_route_file() -> Result { - let res = File::open("net_graph-2023-01-18.bin") // By default we're run in RL/lightning - .or_else(|_| File::open("lightning/net_graph-2023-01-18.bin")) // We may be run manually in RL/ - .or_else(|_| { // Fall back to guessing based on the binary location - // path is likely something like .../rust-lightning/target/debug/deps/lightning-... - let mut path = std::env::current_exe().unwrap(); - path.pop(); // lightning-... - path.pop(); // deps - path.pop(); // debug - path.pop(); // target - path.push("lightning"); - path.push("net_graph-2023-01-18.bin"); - File::open(path) - }) - .or_else(|_| { // Fall back to guessing based on the binary location for a subcrate - // path is likely something like .../rust-lightning/bench/target/debug/deps/bench.. - let mut path = std::env::current_exe().unwrap(); - path.pop(); // bench... - path.pop(); // deps - path.pop(); // debug - path.pop(); // target - path.pop(); // bench - path.push("lightning"); - path.push("net_graph-2023-01-18.bin"); - File::open(path) - }) - .map_err(|_| "Please fetch https://bitcoin.ninja/ldk-net_graph-v0.0.113-2023-01-18.bin and place it at lightning/net_graph-2023-01-18.bin"); + pub(crate) fn get_graph_scorer_file() -> Result<(std::fs::File, std::fs::File), &'static str> { + let load_file = |fname, err_str| { + File::open(fname) // By default we're run in RL/lightning + .or_else(|_| File::open(&format!("lightning/{}", fname))) // We may be run manually in RL/ + .or_else(|_| { // Fall back to guessing based on the binary location + // path is likely something like .../rust-lightning/target/debug/deps/lightning-... + let mut path = std::env::current_exe().unwrap(); + path.pop(); // lightning-... + path.pop(); // deps + path.pop(); // debug + path.pop(); // target + path.push("lightning"); + path.push(fname); + File::open(path) + }) + .or_else(|_| { // Fall back to guessing based on the binary location for a subcrate + // path is likely something like .../rust-lightning/bench/target/debug/deps/bench.. + let mut path = std::env::current_exe().unwrap(); + path.pop(); // bench... + path.pop(); // deps + path.pop(); // debug + path.pop(); // target + path.pop(); // bench + path.push("lightning"); + path.push(fname); + File::open(path) + }) + .map_err(|_| err_str) + }; + let graph_res = load_file( + "net_graph-2023-12-10.bin", + "Please fetch https://bitcoin.ninja/ldk-net_graph-v0.0.118-2023-12-10.bin and place it at lightning/net_graph-2023-12-10.bin" + ); + let scorer_res = load_file( + "scorer-2023-12-10.bin", + "Please fetch https://bitcoin.ninja/ldk-scorer-v0.0.118-2023-12-10.bin and place it at lightning/scorer-2023-12-10.bin" + ); #[cfg(require_route_graph_test)] - return Ok(res.unwrap()); + return Ok((graph_res.unwrap(), scorer_res.unwrap())); #[cfg(not(require_route_graph_test))] - return res; + return Ok((graph_res?, scorer_res?)); } - pub(crate) fn read_network_graph(logger: &TestLogger) -> Result, &'static str> { - get_route_file().map(|mut f| NetworkGraph::read(&mut f, logger).unwrap()) + pub(crate) fn read_graph_scorer(logger: &TestLogger) + -> Result<(Arc>, ProbabilisticScorer>, &TestLogger>), &'static str> { + let (mut graph_file, mut scorer_file) = get_graph_scorer_file()?; + let graph = Arc::new(NetworkGraph::read(&mut graph_file, logger).unwrap()); + let scorer_args = (Default::default(), Arc::clone(&graph), logger); + let scorer = ProbabilisticScorer::read(&mut scorer_file, scorer_args).unwrap(); + Ok((graph, scorer)) } pub(crate) fn payer_pubkey() -> PublicKey { @@ -8523,7 +8605,7 @@ pub(crate) mod bench_utils { inbound_htlc_maximum_msat: None, config: None, feerate_sat_per_1000_weight: None, - channel_shutdown_state: Some(channelmanager::ChannelShutdownState::NotShuttingDown), + channel_shutdown_state: Some(ChannelShutdownState::NotShuttingDown), pending_inbound_htlcs: Vec::new(), pending_outbound_htlcs: Vec::new(), } @@ -8539,9 +8621,7 @@ pub(crate) mod bench_utils { let nodes = graph.read_only().nodes().clone(); let mut route_endpoints = Vec::new(); - // Fetch 1.5x more routes than we need as after we do some scorer updates we may end up - // with some routes we picked being un-routable. - for _ in 0..route_count * 3 / 2 { + for _ in 0..route_count { loop { seed = seed.overflowing_mul(6364136223846793005).0.overflowing_add(1).0; let src = PublicKey::from_slice(nodes.unordered_keys() @@ -8559,54 +8639,12 @@ pub(crate) mod bench_utils { get_route(&payer, &route_params, &graph.read_only(), Some(&[&first_hop]), &TestLogger::new(), scorer, score_params, &random_seed_bytes).is_ok(); if path_exists { - // ...and seed the scorer with success and failure data... - seed = seed.overflowing_mul(6364136223846793005).0.overflowing_add(1).0; - let mut score_amt = seed % 1_000_000_000; - loop { - // Generate fail/success paths for a wider range of potential amounts with - // MPP enabled to give us a chance to apply penalties for more potential - // routes. - let mpp_features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default()); - let params = PaymentParameters::from_node_id(dst, 42) - .with_bolt11_features(mpp_features).unwrap(); - let route_params = RouteParameters::from_payment_params_and_value( - params.clone(), score_amt); - let route_res = get_route(&payer, &route_params, &graph.read_only(), - Some(&[&first_hop]), &TestLogger::new(), scorer, score_params, - &random_seed_bytes); - if let Ok(route) = route_res { - for path in route.paths { - if seed & 0x80 == 0 { - scorer.payment_path_successful(&path, Duration::ZERO); - } else { - let short_channel_id = path.hops[path.hops.len() / 2].short_channel_id; - scorer.payment_path_failed(&path, short_channel_id, Duration::ZERO); - } - seed = seed.overflowing_mul(6364136223846793005).0.overflowing_add(1).0; - } - break; - } - // If we couldn't find a path with a higher amount, reduce and try again. - score_amt /= 100; - } - route_endpoints.push((first_hop, params, amt_msat)); break; } } } - // Because we've changed channel scores, it's possible we'll take different routes to the - // selected destinations, possibly causing us to fail because, eg, the newly-selected path - // requires a too-high CLTV delta. - route_endpoints.retain(|(first_hop, params, amt_msat)| { - let route_params = RouteParameters::from_payment_params_and_value( - params.clone(), *amt_msat); - get_route(&payer, &route_params, &graph.read_only(), Some(&[first_hop]), - &TestLogger::new(), scorer, score_params, &random_seed_bytes).is_ok() - }); - route_endpoints.truncate(route_count); - assert_eq!(route_endpoints.len(), route_count); route_endpoints } } @@ -8619,7 +8657,7 @@ pub mod benches { use crate::ln::channelmanager; use crate::ln::features::Bolt11InvoiceFeatures; use crate::routing::gossip::NetworkGraph; - use crate::routing::scoring::{FixedPenaltyScorer, ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters}; + use crate::routing::scoring::{FixedPenaltyScorer, ProbabilisticScoringFeeParameters}; use crate::util::config::UserConfig; use crate::util::logger::{Logger, Record}; use crate::util::test_utils::TestLogger; @@ -8633,7 +8671,7 @@ pub mod benches { pub fn generate_routes_with_zero_penalty_scorer(bench: &mut Criterion) { let logger = TestLogger::new(); - let network_graph = bench_utils::read_network_graph(&logger).unwrap(); + let (network_graph, _) = bench_utils::read_graph_scorer(&logger).unwrap(); let scorer = FixedPenaltyScorer::with_penalty(0); generate_routes(bench, &network_graph, scorer, &Default::default(), Bolt11InvoiceFeatures::empty(), 0, "generate_routes_with_zero_penalty_scorer"); @@ -8641,7 +8679,7 @@ pub mod benches { pub fn generate_mpp_routes_with_zero_penalty_scorer(bench: &mut Criterion) { let logger = TestLogger::new(); - let network_graph = bench_utils::read_network_graph(&logger).unwrap(); + let (network_graph, _) = bench_utils::read_graph_scorer(&logger).unwrap(); let scorer = FixedPenaltyScorer::with_penalty(0); generate_routes(bench, &network_graph, scorer, &Default::default(), channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0, @@ -8650,18 +8688,16 @@ pub mod benches { pub fn generate_routes_with_probabilistic_scorer(bench: &mut Criterion) { let logger = TestLogger::new(); - let network_graph = bench_utils::read_network_graph(&logger).unwrap(); + let (network_graph, scorer) = bench_utils::read_graph_scorer(&logger).unwrap(); let params = ProbabilisticScoringFeeParameters::default(); - let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger); generate_routes(bench, &network_graph, scorer, ¶ms, Bolt11InvoiceFeatures::empty(), 0, "generate_routes_with_probabilistic_scorer"); } pub fn generate_mpp_routes_with_probabilistic_scorer(bench: &mut Criterion) { let logger = TestLogger::new(); - let network_graph = bench_utils::read_network_graph(&logger).unwrap(); + let (network_graph, scorer) = bench_utils::read_graph_scorer(&logger).unwrap(); let params = ProbabilisticScoringFeeParameters::default(); - let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger); generate_routes(bench, &network_graph, scorer, ¶ms, channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0, "generate_mpp_routes_with_probabilistic_scorer"); @@ -8669,9 +8705,8 @@ pub mod benches { pub fn generate_large_mpp_routes_with_probabilistic_scorer(bench: &mut Criterion) { let logger = TestLogger::new(); - let network_graph = bench_utils::read_network_graph(&logger).unwrap(); + let (network_graph, scorer) = bench_utils::read_graph_scorer(&logger).unwrap(); let params = ProbabilisticScoringFeeParameters::default(); - let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger); generate_routes(bench, &network_graph, scorer, ¶ms, channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 100_000_000, "generate_large_mpp_routes_with_probabilistic_scorer"); @@ -8679,11 +8714,9 @@ pub mod benches { pub fn generate_routes_with_nonlinear_probabilistic_scorer(bench: &mut Criterion) { let logger = TestLogger::new(); - let network_graph = bench_utils::read_network_graph(&logger).unwrap(); + let (network_graph, scorer) = bench_utils::read_graph_scorer(&logger).unwrap(); let mut params = ProbabilisticScoringFeeParameters::default(); params.linear_success_probability = false; - let scorer = ProbabilisticScorer::new( - ProbabilisticScoringDecayParameters::default(), &network_graph, &logger); generate_routes(bench, &network_graph, scorer, ¶ms, channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0, "generate_routes_with_nonlinear_probabilistic_scorer"); @@ -8691,11 +8724,9 @@ pub mod benches { pub fn generate_mpp_routes_with_nonlinear_probabilistic_scorer(bench: &mut Criterion) { let logger = TestLogger::new(); - let network_graph = bench_utils::read_network_graph(&logger).unwrap(); + let (network_graph, scorer) = bench_utils::read_graph_scorer(&logger).unwrap(); let mut params = ProbabilisticScoringFeeParameters::default(); params.linear_success_probability = false; - let scorer = ProbabilisticScorer::new( - ProbabilisticScoringDecayParameters::default(), &network_graph, &logger); generate_routes(bench, &network_graph, scorer, ¶ms, channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0, "generate_mpp_routes_with_nonlinear_probabilistic_scorer"); @@ -8703,11 +8734,9 @@ pub mod benches { pub fn generate_large_mpp_routes_with_nonlinear_probabilistic_scorer(bench: &mut Criterion) { let logger = TestLogger::new(); - let network_graph = bench_utils::read_network_graph(&logger).unwrap(); + let (network_graph, scorer) = bench_utils::read_graph_scorer(&logger).unwrap(); let mut params = ProbabilisticScoringFeeParameters::default(); params.linear_success_probability = false; - let scorer = ProbabilisticScorer::new( - ProbabilisticScoringDecayParameters::default(), &network_graph, &logger); generate_routes(bench, &network_graph, scorer, ¶ms, channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 100_000_000, "generate_large_mpp_routes_with_nonlinear_probabilistic_scorer"); @@ -8718,14 +8747,23 @@ pub mod benches { score_params: &S::ScoreParams, features: Bolt11InvoiceFeatures, starting_amount: u64, bench_name: &'static str, ) { - let payer = bench_utils::payer_pubkey(); - let keys_manager = KeysManager::new(&[0u8; 32], 42, 42); - let random_seed_bytes = keys_manager.get_secure_random_bytes(); - // First, get 100 (source, destination) pairs for which route-getting actually succeeds... let route_endpoints = bench_utils::generate_test_routes(graph, &mut scorer, score_params, features, 0xdeadbeef, starting_amount, 50); // ...then benchmark finding paths between the nodes we learned. + do_route_bench(bench, graph, scorer, score_params, bench_name, route_endpoints); + } + + #[inline(never)] + fn do_route_bench( + bench: &mut Criterion, graph: &NetworkGraph<&TestLogger>, scorer: S, + score_params: &S::ScoreParams, bench_name: &'static str, + route_endpoints: Vec<(ChannelDetails, PaymentParameters, u64)>, + ) { + let payer = bench_utils::payer_pubkey(); + let keys_manager = KeysManager::new(&[0u8; 32], 42, 42); + let random_seed_bytes = keys_manager.get_secure_random_bytes(); + let mut idx = 0; bench.bench_function(bench_name, |b| b.iter(|| { let (first_hop, params, amt) = &route_endpoints[idx % route_endpoints.len()];