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::blinded_path::message;
+use crate::blinded_path::payment::{ForwardTlvs, PaymentConstraints, PaymentRelay, ReceiveTlvs, self};
use crate::ln::{PaymentHash, PaymentPreimage};
-use crate::ln::channelmanager::{ChannelDetails, PaymentId, MIN_FINAL_CLTV_EXPIRY_DELTA, RecipientOnionFields};
+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 core::ops::Deref;
/// A [`Router`] implemented using [`find_route`].
+///
+/// # Privacy
+///
+/// Implements [`MessageRouter`] by delegating to [`DefaultMessageRouter`]. See those docs for
+/// privacy implications.
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>,
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,
) -> Result<Vec<BlindedPath>, ()> {
self.message_router.create_blinded_paths(recipient, peers, secp_ctx)
}
+
+ fn create_compact_blinded_paths<
+ T: secp256k1::Signing + secp256k1::Verification
+ > (
+ &self, recipient: PublicKey, peers: Vec<message::ForwardNode>, secp_ctx: &Secp256k1<T>,
+ ) -> Result<Vec<BlindedPath>, ()> {
+ self.message_router.create_compact_blinded_paths(recipient, peers, secp_ctx)
+ }
}
/// A trait defining behavior for routing a payment.
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());
+ 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.
// 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 {
} 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;
}
let features = if let Some(node_info) = $node.announcement_info.as_ref() {
- &node_info.features
+ &node_info.features()
} else {
&default_node_features
};
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();
}
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};
#[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;
use crate::sync::Arc;
fn get_channel_details(short_channel_id: Option<u64>, 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,
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(),
}
.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);
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
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
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,
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);
{
use crate::chain::transaction::OutPoint;
use crate::routing::scoring::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;
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(),
}