X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Frouter.rs;h=56643c082eb5b54ace172b90f5faab58cda78afd;hb=2d6d5cc86f688fad01caca8ff420a10a6f01b726;hp=f1901d7c50188e7e2b4e8b49d11c4bb590b4b829;hpb=7002180261d495f15c12f9a341af1e7dbcac2990;p=rust-lightning diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index f1901d7c..56643c08 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::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 { @@ -654,6 +668,8 @@ const DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF: u8 = 2; // The median hop CLTV expiry delta currently seen in the network. const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40; +/// Estimated maximum number of hops that can be included in a payment path. May be inaccurate if +/// payment metadata, custom TLVs, or blinded paths are included in the payment. // During routing, we only consider paths shorter than our maximum length estimate. // In the TLV onion format, there is no fixed maximum length, but the `hop_payloads` // field is always 1300 bytes. As the `tlv_payload` for each hop may vary in length, we have to @@ -665,7 +681,7 @@ const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40; // (payment_secret and total_msat) = 93 bytes for the final hop. // Since the length of the potentially included `payment_metadata` is unknown to us, we round // down from (1300-93) / 61 = 19.78... to arrive at a conservative estimate of 19. -const MAX_PATH_LENGTH_ESTIMATE: u8 = 19; +pub const MAX_PATH_LENGTH_ESTIMATE: u8 = 19; /// Information used to route a payment. #[derive(Clone, Debug, Hash, PartialEq, Eq)] @@ -684,6 +700,10 @@ pub struct PaymentParameters { /// Defaults to [`DEFAULT_MAX_PATH_COUNT`]. pub max_path_count: u8, + /// The maximum number of [`Path::hops`] in any returned path. + /// Defaults to [`MAX_PATH_LENGTH_ESTIMATE`]. + pub max_path_length: u8, + /// Selects the maximum share of a channel's total capacity which will be sent over a channel, /// as a power of 1/2. A higher value prefers to send the payment using more MPP parts whereas /// a lower value prefers to send larger MPP parts, potentially saturating channels and @@ -730,6 +750,7 @@ impl Writeable for PaymentParameters { (8, *blinded_hints, optional_vec), (9, self.payee.final_cltv_expiry_delta(), option), (11, self.previously_failed_blinded_path_idxs, required_vec), + (13, self.max_path_length, required), }); Ok(()) } @@ -749,6 +770,7 @@ impl ReadableArgs for PaymentParameters { (8, blinded_route_hints, optional_vec), (9, final_cltv_expiry_delta, (default_value, default_final_cltv_expiry_delta)), (11, previously_failed_blinded_path_idxs, optional_vec), + (13, max_path_length, (default_value, MAX_PATH_LENGTH_ESTIMATE)), }); let blinded_route_hints = blinded_route_hints.unwrap_or(vec![]); let payee = if blinded_route_hints.len() != 0 { @@ -773,6 +795,7 @@ impl ReadableArgs for PaymentParameters { expiry_time, previously_failed_channels: previously_failed_channels.unwrap_or(Vec::new()), previously_failed_blinded_path_idxs: previously_failed_blinded_path_idxs.unwrap_or(Vec::new()), + max_path_length: _init_tlv_based_struct_field!(max_path_length, (default_value, unused)), }) } } @@ -789,6 +812,7 @@ impl PaymentParameters { expiry_time: None, max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, max_path_count: DEFAULT_MAX_PATH_COUNT, + max_path_length: MAX_PATH_LENGTH_ESTIMATE, max_channel_saturation_power_of_half: DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF, previously_failed_channels: Vec::new(), previously_failed_blinded_path_idxs: Vec::new(), @@ -828,6 +852,7 @@ impl PaymentParameters { expiry_time: None, max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, max_path_count: DEFAULT_MAX_PATH_COUNT, + max_path_length: MAX_PATH_LENGTH_ESTIMATE, max_channel_saturation_power_of_half: DEFAULT_MAX_CHANNEL_SATURATION_POW_HALF, previously_failed_channels: Vec::new(), previously_failed_blinded_path_idxs: Vec::new(), @@ -1144,11 +1169,11 @@ pub struct FirstHopCandidate<'a> { /// /// [`find_route`] validates this prior to constructing a [`CandidateRouteHop`]. /// - /// This is not exported to bindings users as lifetimes are not expressable in most languages. + /// This is not exported to bindings users as lifetimes are not expressible 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. + /// This is not exported to bindings users as lifetimes are not expressible in most languages. pub payer_node_id: &'a NodeId, } @@ -1158,7 +1183,7 @@ 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. + /// This is not exported to bindings users as lifetimes are not expressible 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. @@ -1170,11 +1195,11 @@ pub struct PublicHopCandidate<'a> { 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. + /// This is not exported to bindings users as lifetimes are not expressible 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. + /// This is not exported to bindings users as lifetimes are not expressible in most languages. pub target_node_id: &'a NodeId } @@ -1189,7 +1214,7 @@ 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. + /// This is not exported to bindings users as lifetimes are not expressible in most languages. pub hint: &'a (BlindedPayInfo, BlindedPath), /// Index of the hint in the original list of blinded hints. /// @@ -1211,7 +1236,7 @@ pub struct OneHopBlindedPathCandidate<'a> { /// /// Note that the [`BlindedPayInfo`] is ignored here. /// - /// This is not exported to bindings users as lifetimes are not expressable in most languages. + /// This is not exported to bindings users as lifetimes are not expressible in most languages. pub hint: &'a (BlindedPayInfo, BlindedPath), /// Index of the hint in the original list of blinded hints. /// @@ -1849,6 +1874,7 @@ pub(crate) fn get_route( where L::Target: Logger { let payment_params = &route_params.payment_params; + let max_path_length = core::cmp::min(payment_params.max_path_length, MAX_PATH_LENGTH_ESTIMATE); let final_value_msat = route_params.final_value_msat; // If we're routing to a blinded recipient, we won't have their node id. Therefore, keep the // unblinded payee id as an option. We also need a non-optional "payee id" for path construction, @@ -1874,6 +1900,9 @@ where L::Target: Logger { return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError}); } + let introduction_node_id_cache = payment_params.payee.blinded_route_hints().iter() + .map(|(_, path)| path.public_introduction_node_id(network_graph)) + .collect::>(); match &payment_params.payee { Payee::Clear { route_hints, node_id, .. } => { for route in route_hints.iter() { @@ -1885,17 +1914,19 @@ where L::Target: Logger { } }, Payee::Blinded { route_hints, .. } => { - if route_hints.iter().all(|(_, path)| path.public_introduction_node_id(network_graph) == Some(&our_node_id)) { + if introduction_node_id_cache.iter().all(|introduction_node_id| *introduction_node_id == Some(&our_node_id)) { return Err(LightningError{err: "Cannot generate a route to blinded paths if we are the introduction node to all of them".to_owned(), action: ErrorAction::IgnoreError}); } - for (_, blinded_path) in route_hints.iter() { + for ((_, blinded_path), introduction_node_id) in route_hints.iter().zip(introduction_node_id_cache.iter()) { if blinded_path.blinded_hops.len() == 0 { return Err(LightningError{err: "0-hop blinded path provided".to_owned(), action: ErrorAction::IgnoreError}); - } else if blinded_path.public_introduction_node_id(network_graph) == Some(&our_node_id) { + } else if *introduction_node_id == Some(&our_node_id) { log_info!(logger, "Got blinded path with ourselves as the introduction node, ignoring"); } else if blinded_path.blinded_hops.len() == 1 && - route_hints.iter().any( |(_, p)| p.blinded_hops.len() == 1 - && p.public_introduction_node_id(network_graph) != blinded_path.public_introduction_node_id(network_graph)) + route_hints + .iter().zip(introduction_node_id_cache.iter()) + .filter(|((_, p), _)| p.blinded_hops.len() == 1) + .any(|(_, p_introduction_node_id)| p_introduction_node_id != introduction_node_id) { return Err(LightningError{err: format!("1-hop blinded paths must all have matching introduction node ids"), action: ErrorAction::IgnoreError}); } @@ -1984,12 +2015,29 @@ where L::Target: Logger { 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. @@ -2139,8 +2187,9 @@ where L::Target: Logger { // Verify the liquidity offered by this channel complies to the minimal contribution. let contributes_sufficient_value = available_value_contribution_msat >= minimal_value_contribution_msat; // Do not consider candidate hops that would exceed the maximum path length. - let path_length_to_node = $next_hops_path_length + 1; - let exceeds_max_path_length = path_length_to_node > MAX_PATH_LENGTH_ESTIMATE; + let path_length_to_node = $next_hops_path_length + + if $candidate.blinded_hint_idx().is_some() { 0 } else { 1 }; + let exceeds_max_path_length = path_length_to_node > max_path_length; // Do not consider candidates that exceed the maximum total cltv expiry limit. // In order to already account for some of the privacy enhancing random CLTV @@ -2195,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 { @@ -2231,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; @@ -2540,7 +2577,7 @@ where L::Target: Logger { // Only add the hops in this route to our candidate set if either // we have a direct channel to the first hop or the first hop is // in the regular network graph. - let source_node_id = match hint.1.public_introduction_node_id(network_graph) { + let source_node_id = match introduction_node_id_cache[hint_idx] { Some(node_id) => node_id, None => match &hint.1.introduction_node { IntroductionNode::NodeId(pubkey) => { @@ -2594,9 +2631,8 @@ where L::Target: Logger { }; let path_min = candidate.htlc_minimum_msat().saturating_add( compute_fees_saturating(candidate.htlc_minimum_msat(), candidate.fees())); - add_entry!(&first_hop_candidate, blinded_path_fee, - path_contribution_msat, path_min, 0_u64, candidate.cltv_expiry_delta(), - candidate.blinded_path().map_or(1, |bp| bp.blinded_hops.len() as u8)); + add_entry!(&first_hop_candidate, blinded_path_fee, path_contribution_msat, path_min, + 0_u64, candidate.cltv_expiry_delta(), 0); } } } @@ -3286,7 +3322,8 @@ 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::ChannelId; + 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}; use crate::ln::channelmanager; @@ -3298,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; @@ -3313,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, @@ -3346,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(), } @@ -3356,7 +3394,7 @@ mod tests { fn simple_route_test() { let (secp_ctx, network_graph, _, _, logger) = build_graph(); let (_, our_id, _, nodes) = get_nodes(&secp_ctx); - let payment_params = PaymentParameters::from_node_id(nodes[2], 42); + let mut payment_params = PaymentParameters::from_node_id(nodes[2], 42); let scorer = ln_test_utils::TestScorer::new(); let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); @@ -3371,7 +3409,8 @@ mod tests { assert_eq!(err, "Cannot send a payment of 0 msat"); } else { panic!(); } - let route_params = RouteParameters::from_payment_params_and_value(payment_params, 100); + payment_params.max_path_length = 2; + let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 100); let route = get_route(&our_id, &route_params, &network_graph.read_only(), None, Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap(); assert_eq!(route.paths[0].hops.len(), 2); @@ -3389,6 +3428,10 @@ mod tests { assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42); assert_eq!(route.paths[0].hops[1].node_features.le_flags(), &id_to_feature_flags(3)); assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(4)); + + route_params.payment_params.max_path_length = 1; + get_route(&our_id, &route_params, &network_graph.read_only(), None, + Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap_err(); } #[test] @@ -3793,7 +3836,7 @@ mod tests { }); // If all the channels require some features we don't understand, route should fail - let route_params = RouteParameters::from_payment_params_and_value(payment_params, 100); + let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 100); if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &route_params, &network_graph.read_only(), None, Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes) { @@ -3803,6 +3846,7 @@ mod tests { // If we specify a channel to node7, that overrides our local channel view and that gets used let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)]; + route_params.payment_params.max_path_length = 2; let route = get_route(&our_id, &route_params, &network_graph.read_only(), Some(&our_chans.iter().collect::>()), Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap(); @@ -4049,8 +4093,9 @@ mod tests { } else { panic!(); } } - let payment_params = PaymentParameters::from_node_id(nodes[6], 42) + let mut payment_params = PaymentParameters::from_node_id(nodes[6], 42) .with_route_hints(last_hops_multi_private_channels(&nodes)).unwrap(); + payment_params.max_path_length = 5; let route_params = RouteParameters::from_payment_params_and_value(payment_params, 100); let route = get_route(&our_id, &route_params, &network_graph.read_only(), None, Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap(); @@ -4208,7 +4253,8 @@ mod tests { let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet); let random_seed_bytes = keys_manager.get_secure_random_bytes(); // Test through channels 2, 3, 0xff00, 0xff01. - // Test shows that multiple hop hints are considered. + // Test shows that multi-hop route hints are considered and factored correctly into the + // max path length. // Disabling channels 6 & 7 by flags=2 update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate { @@ -4236,7 +4282,8 @@ mod tests { excess_data: Vec::new() }); - let route_params = RouteParameters::from_payment_params_and_value(payment_params, 100); + let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 100); + route_params.payment_params.max_path_length = 4; let route = get_route(&our_id, &route_params, &network_graph.read_only(), None, Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap(); assert_eq!(route.paths[0].hops.len(), 4); @@ -4268,6 +4315,9 @@ mod tests { assert_eq!(route.paths[0].hops[3].cltv_expiry_delta, 42); assert_eq!(route.paths[0].hops[3].node_features.le_flags(), default_node_features().le_flags()); // We dont pass flags in from invoices yet assert_eq!(route.paths[0].hops[3].channel_features.le_flags(), &Vec::::new()); // We can't learn any flags from invoices, sadly + route_params.payment_params.max_path_length = 3; + get_route(&our_id, &route_params, &network_graph.read_only(), None, + Arc::clone(&logger), &scorer, &Default::default(), &random_seed_bytes).unwrap_err(); } #[test] @@ -4845,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); @@ -5461,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 @@ -5638,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 @@ -5811,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, @@ -6203,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); { @@ -8408,8 +8506,9 @@ pub(crate) mod bench_utils { use crate::chain::transaction::OutPoint; use crate::routing::scoring::ScoreUpdate; use crate::sign::KeysManager; - use crate::ln::ChannelId; - use crate::ln::channelmanager::{self, ChannelCounterparty}; + use crate::ln::channel_state::{ChannelCounterparty, ChannelShutdownState}; + use crate::ln::channelmanager; + use crate::ln::types::ChannelId; use crate::util::config::UserConfig; use crate::util::test_utils::TestLogger; @@ -8494,7 +8593,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(), }