mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-15 15:39:09 +01:00
Merge pull request #2258 from valentinewallace/2023-04-blinded-pathfinding-groundwork-2
Prefactor `PaymentParameters` for blinded recipients
This commit is contained in:
commit
0ecb4b093a
13 changed files with 314 additions and 173 deletions
|
@ -300,7 +300,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
|
|||
let final_cltv_expiry_delta = slice_to_be32(get_slice!(4));
|
||||
let route_params = RouteParameters {
|
||||
payment_params: PaymentParameters::from_node_id(*target, final_cltv_expiry_delta)
|
||||
.with_route_hints(last_hops.clone()),
|
||||
.with_route_hints(last_hops.clone()).unwrap(),
|
||||
final_value_msat,
|
||||
};
|
||||
let _ = find_route(&our_pubkey, &route_params, &net_graph,
|
||||
|
|
|
@ -152,9 +152,9 @@ fn pay_invoice_using_amount<P: Deref>(
|
|||
let mut payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(),
|
||||
invoice.min_final_cltv_expiry_delta() as u32)
|
||||
.with_expiry_time(expiry_time_from_unix_epoch(invoice).as_secs())
|
||||
.with_route_hints(invoice.route_hints());
|
||||
.with_route_hints(invoice.route_hints()).unwrap();
|
||||
if let Some(features) = invoice.features() {
|
||||
payment_params = payment_params.with_features(features.clone());
|
||||
payment_params = payment_params.with_bolt11_features(features.clone()).unwrap();
|
||||
}
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
|
|
|
@ -838,8 +838,8 @@ mod test {
|
|||
|
||||
let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(),
|
||||
invoice.min_final_cltv_expiry_delta() as u32)
|
||||
.with_features(invoice.features().unwrap().clone())
|
||||
.with_route_hints(invoice.route_hints());
|
||||
.with_bolt11_features(invoice.features().unwrap().clone()).unwrap()
|
||||
.with_route_hints(invoice.route_hints()).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: invoice.amount_milli_satoshis().unwrap(),
|
||||
|
@ -1294,8 +1294,8 @@ mod test {
|
|||
|
||||
let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key(),
|
||||
invoice.min_final_cltv_expiry_delta() as u32)
|
||||
.with_features(invoice.features().unwrap().clone())
|
||||
.with_route_hints(invoice.route_hints());
|
||||
.with_bolt11_features(invoice.features().unwrap().clone()).unwrap()
|
||||
.with_route_hints(invoice.route_hints()).unwrap();
|
||||
let params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: invoice.amount_milli_satoshis().unwrap(),
|
||||
|
|
|
@ -45,7 +45,7 @@ use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, No
|
|||
#[cfg(any(feature = "_test_utils", test))]
|
||||
use crate::ln::features::InvoiceFeatures;
|
||||
use crate::routing::gossip::NetworkGraph;
|
||||
use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, Router};
|
||||
use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteHop, RouteParameters, Router};
|
||||
use crate::routing::scoring::ProbabilisticScorer;
|
||||
use crate::ln::msgs;
|
||||
use crate::ln::onion_utils;
|
||||
|
@ -7251,8 +7251,10 @@ impl Readable for HTLCSource {
|
|||
return Err(DecodeError::InvalidValue);
|
||||
}
|
||||
if let Some(params) = payment_params.as_mut() {
|
||||
if params.final_cltv_expiry_delta == 0 {
|
||||
params.final_cltv_expiry_delta = path.final_cltv_expiry_delta().ok_or(DecodeError::InvalidValue)?;
|
||||
if let Payee::Clear { ref mut final_cltv_expiry_delta, .. } = params.payee {
|
||||
if final_cltv_expiry_delta == &0 {
|
||||
*final_cltv_expiry_delta = path.final_cltv_expiry_delta().ok_or(DecodeError::InvalidValue)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(HTLCSource::OutboundRoute {
|
||||
|
@ -9428,7 +9430,7 @@ pub mod bench {
|
|||
macro_rules! send_payment {
|
||||
($node_a: expr, $node_b: expr) => {
|
||||
let payment_params = PaymentParameters::from_node_id($node_b.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features($node_b.invoice_features());
|
||||
.with_bolt11_features($node_b.invoice_features()).unwrap();
|
||||
let mut payment_preimage = PaymentPreimage([0; 32]);
|
||||
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
|
||||
payment_count += 1;
|
||||
|
|
|
@ -532,6 +532,14 @@ impl InvoiceFeatures {
|
|||
}
|
||||
}
|
||||
|
||||
impl Bolt12InvoiceFeatures {
|
||||
/// Converts `Bolt12InvoiceFeatures` to `Features<C>`. Only known `Bolt12InvoiceFeatures` relevant
|
||||
/// to context `C` are included in the result.
|
||||
pub(crate) fn to_context<C: sealed::Context>(&self) -> Features<C> {
|
||||
self.to_context_internal()
|
||||
}
|
||||
}
|
||||
|
||||
impl ChannelTypeFeatures {
|
||||
// Maps the relevant `InitFeatures` to `ChannelTypeFeatures`. Any unknown features to
|
||||
// `ChannelTypeFeatures` are not included in the result.
|
||||
|
@ -791,6 +799,7 @@ impl_feature_len_prefixed_write!(InitFeatures);
|
|||
impl_feature_len_prefixed_write!(ChannelFeatures);
|
||||
impl_feature_len_prefixed_write!(NodeFeatures);
|
||||
impl_feature_len_prefixed_write!(InvoiceFeatures);
|
||||
impl_feature_len_prefixed_write!(Bolt12InvoiceFeatures);
|
||||
impl_feature_len_prefixed_write!(BlindedHopFeatures);
|
||||
|
||||
// Some features only appear inside of TLVs, so they don't have a length prefix when serialized.
|
||||
|
|
|
@ -1757,7 +1757,7 @@ macro_rules! get_route {
|
|||
macro_rules! get_route_and_payment_hash {
|
||||
($send_node: expr, $recv_node: expr, $recv_value: expr) => {{
|
||||
let payment_params = $crate::routing::router::PaymentParameters::from_node_id($recv_node.node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features($recv_node.node.invoice_features());
|
||||
.with_bolt11_features($recv_node.node.invoice_features()).unwrap();
|
||||
$crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value)
|
||||
}};
|
||||
($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr) => {{
|
||||
|
@ -2306,7 +2306,7 @@ pub const TEST_FINAL_CLTV: u32 = 70;
|
|||
|
||||
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret) {
|
||||
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(expected_route.last().unwrap().node.invoice_features());
|
||||
.with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
|
||||
let route = get_route(origin_node, &payment_params, recv_value).unwrap();
|
||||
assert_eq!(route.paths.len(), 1);
|
||||
assert_eq!(route.paths[0].hops.len(), expected_route.len());
|
||||
|
@ -2320,7 +2320,7 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route:
|
|||
|
||||
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
|
||||
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(expected_route.last().unwrap().node.invoice_features());
|
||||
.with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
|
||||
let network_graph = origin_node.network_graph.read_only();
|
||||
let scorer = test_utils::TestScorer::new();
|
||||
let seed = [0u8; 32];
|
||||
|
|
|
@ -1829,7 +1829,7 @@ fn test_channel_reserve_holding_cell_htlcs() {
|
|||
// attempt to send amt_msat > their_max_htlc_value_in_flight_msat
|
||||
{
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(nodes[2].node.invoice_features()).with_max_channel_saturation_power_of_half(0);
|
||||
.with_bolt11_features(nodes[2].node.invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
|
||||
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, recv_value_0);
|
||||
route.paths[0].hops.last_mut().unwrap().fee_msat += 1;
|
||||
assert!(route.paths[0].hops.iter().rev().skip(1).all(|h| h.fee_msat == feemsat));
|
||||
|
@ -1856,7 +1856,7 @@ fn test_channel_reserve_holding_cell_htlcs() {
|
|||
}
|
||||
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(nodes[2].node.invoice_features()).with_max_channel_saturation_power_of_half(0);
|
||||
.with_bolt11_features(nodes[2].node.invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
|
||||
let route = get_route!(nodes[0], payment_params, recv_value_0).unwrap();
|
||||
let (payment_preimage, ..) = send_along_route(&nodes[0], route, &[&nodes[1], &nodes[2]], recv_value_0);
|
||||
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
|
||||
|
@ -4795,7 +4795,7 @@ fn test_duplicate_payment_hash_one_failure_one_success() {
|
|||
// script push size limit so that the below script length checks match
|
||||
// ACCEPTED_HTLC_SCRIPT_WEIGHT.
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV - 40)
|
||||
.with_features(nodes[3].node.invoice_features());
|
||||
.with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
|
||||
let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[3], payment_params, 800_000);
|
||||
send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[2], &nodes[3]]], 800_000, duplicate_payment_hash, payment_secret);
|
||||
|
||||
|
@ -6101,7 +6101,7 @@ fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() {
|
|||
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 0);
|
||||
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 0)
|
||||
.with_features(nodes[1].node.invoice_features());
|
||||
.with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
|
||||
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000);
|
||||
route.paths[0].hops.last_mut().unwrap().cltv_expiry_delta = 500000001;
|
||||
unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash,
|
||||
|
@ -7043,7 +7043,7 @@ fn test_check_htlc_underpaying() {
|
|||
|
||||
let scorer = test_utils::TestScorer::new();
|
||||
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_features(nodes[1].node.invoice_features());
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
|
||||
let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(), None, 10_000, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
|
||||
let (_, our_payment_hash, _) = get_payment_preimage_hash!(nodes[0]);
|
||||
let our_payment_secret = nodes[1].node.create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200, None).unwrap();
|
||||
|
@ -7189,7 +7189,7 @@ fn test_bump_penalty_txn_on_revoked_commitment() {
|
|||
|
||||
let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 30)
|
||||
.with_features(nodes[0].node.invoice_features());
|
||||
.with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
|
||||
let (route,_, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], payment_params, 3000000);
|
||||
send_along_route(&nodes[1], route, &vec!(&nodes[0])[..], 3000000);
|
||||
|
||||
|
@ -7294,13 +7294,13 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
|
|||
|
||||
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000);
|
||||
// Lock HTLC in both directions (using a slightly lower CLTV delay to provide timely RBF bumps)
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 50).with_features(nodes[1].node.invoice_features());
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 50).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
|
||||
let scorer = test_utils::TestScorer::new();
|
||||
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
|
||||
let route = get_route(&nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph.read_only(), None,
|
||||
3_000_000, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
|
||||
let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0;
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 50).with_features(nodes[0].node.invoice_features());
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 50).with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
|
||||
let route = get_route(&nodes[1].node.get_our_node_id(), &payment_params, &nodes[1].network_graph.read_only(), None,
|
||||
3_000_000, nodes[0].logger, &scorer, &random_seed_bytes).unwrap();
|
||||
send_along_route(&nodes[1], route, &[&nodes[0]], 3_000_000);
|
||||
|
@ -9300,7 +9300,7 @@ fn do_test_dup_htlc_second_rejected(test_for_second_fail_panic: bool) {
|
|||
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001);
|
||||
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(nodes[1].node.invoice_features());
|
||||
.with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
|
||||
let route = get_route!(nodes[0], payment_params, 10_000).unwrap();
|
||||
|
||||
let (our_payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(&nodes[1]);
|
||||
|
@ -9409,7 +9409,7 @@ fn test_inconsistent_mpp_params() {
|
|||
let chan_2_3 =create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 100_000, 0);
|
||||
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(nodes[3].node.invoice_features());
|
||||
.with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
|
||||
let mut route = get_route!(nodes[0], payment_params, 15_000_000).unwrap();
|
||||
assert_eq!(route.paths.len(), 2);
|
||||
route.paths.sort_by(|path_a, _| {
|
||||
|
|
|
@ -714,8 +714,8 @@ fn do_test_onion_failure_stale_channel_update(announced_channel: bool) {
|
|||
htlc_minimum_msat: None,
|
||||
}])];
|
||||
let payment_params = PaymentParameters::from_node_id(*channel_to_update_counterparty, TEST_FINAL_CLTV)
|
||||
.with_features(nodes[2].node.invoice_features())
|
||||
.with_route_hints(hop_hints);
|
||||
.with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
|
||||
.with_route_hints(hop_hints).unwrap();
|
||||
get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, PAYMENT_AMT)
|
||||
};
|
||||
send_along_route_with_secret(&nodes[0], route.clone(), &[&[&nodes[1], &nodes[2]]], PAYMENT_AMT,
|
||||
|
@ -861,7 +861,7 @@ fn test_always_create_tlv_format_onion_payloads() {
|
|||
create_announced_chan_between_nodes(&nodes, 1, 2);
|
||||
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(InvoiceFeatures::empty());
|
||||
.with_bolt11_features(InvoiceFeatures::empty()).unwrap();
|
||||
let (route, _payment_hash, _payment_preimage, _payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 40000);
|
||||
|
||||
let hops = &route.paths[0].hops;
|
||||
|
@ -963,7 +963,7 @@ macro_rules! get_phantom_route {
|
|||
let phantom_pubkey = $nodes[1].keys_manager.get_node_id(Recipient::PhantomNode).unwrap();
|
||||
let phantom_route_hint = $nodes[1].node.get_phantom_route_hints();
|
||||
let payment_params = PaymentParameters::from_node_id(phantom_pubkey, TEST_FINAL_CLTV)
|
||||
.with_features($nodes[1].node.invoice_features())
|
||||
.with_bolt11_features($nodes[1].node.invoice_features()).unwrap()
|
||||
.with_route_hints(vec![RouteHint(vec![
|
||||
RouteHintHop {
|
||||
src_node_id: $nodes[0].node.get_our_node_id(),
|
||||
|
@ -987,7 +987,7 @@ macro_rules! get_phantom_route {
|
|||
htlc_minimum_msat: None,
|
||||
htlc_maximum_msat: None,
|
||||
}
|
||||
])]);
|
||||
])]).unwrap();
|
||||
let scorer = test_utils::TestScorer::new();
|
||||
let network_graph = $nodes[0].network_graph.read_only();
|
||||
(get_route(
|
||||
|
|
|
@ -857,7 +857,7 @@ fn get_ldk_payment_preimage() {
|
|||
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(amt_msat), expiry_secs, None).unwrap();
|
||||
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(nodes[1].node.invoice_features());
|
||||
.with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
|
||||
let scorer = test_utils::TestScorer::new();
|
||||
let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
|
@ -1409,8 +1409,8 @@ fn do_test_intercepted_payment(test: InterceptTest) {
|
|||
htlc_minimum_msat: None,
|
||||
htlc_maximum_msat: None,
|
||||
}])
|
||||
])
|
||||
.with_features(nodes[2].node.invoice_features());
|
||||
]).unwrap()
|
||||
.with_bolt11_features(nodes[2].node.invoice_features()).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -1600,7 +1600,7 @@ fn do_automatic_retries(test: AutoRetry) {
|
|||
invoice_features.set_basic_mpp_optional();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_expiry_time(payment_expiry_secs as u64)
|
||||
.with_features(invoice_features);
|
||||
.with_bolt11_features(invoice_features).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -1819,7 +1819,7 @@ fn auto_retry_partial_failure() {
|
|||
invoice_features.set_basic_mpp_optional();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_expiry_time(payment_expiry_secs as u64)
|
||||
.with_features(invoice_features);
|
||||
.with_bolt11_features(invoice_features).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -2031,7 +2031,7 @@ fn auto_retry_zero_attempts_send_error() {
|
|||
invoice_features.set_basic_mpp_optional();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_expiry_time(payment_expiry_secs as u64)
|
||||
.with_features(invoice_features);
|
||||
.with_bolt11_features(invoice_features).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -2071,7 +2071,7 @@ fn fails_paying_after_rejected_by_payee() {
|
|||
invoice_features.set_basic_mpp_optional();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_expiry_time(payment_expiry_secs as u64)
|
||||
.with_features(invoice_features);
|
||||
.with_bolt11_features(invoice_features).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -2118,7 +2118,7 @@ fn retry_multi_path_single_failed_payment() {
|
|||
invoice_features.set_basic_mpp_optional();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_expiry_time(payment_expiry_secs as u64)
|
||||
.with_features(invoice_features);
|
||||
.with_bolt11_features(invoice_features).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params: payment_params.clone(),
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -2212,7 +2212,7 @@ fn immediate_retry_on_failure() {
|
|||
invoice_features.set_basic_mpp_optional();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_expiry_time(payment_expiry_secs as u64)
|
||||
.with_features(invoice_features);
|
||||
.with_bolt11_features(invoice_features).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -2301,7 +2301,7 @@ fn no_extra_retries_on_back_to_back_fail() {
|
|||
invoice_features.set_basic_mpp_optional();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_expiry_time(payment_expiry_secs as u64)
|
||||
.with_features(invoice_features);
|
||||
.with_bolt11_features(invoice_features).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -2503,7 +2503,7 @@ fn test_simple_partial_retry() {
|
|||
invoice_features.set_basic_mpp_optional();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_expiry_time(payment_expiry_secs as u64)
|
||||
.with_features(invoice_features);
|
||||
.with_bolt11_features(invoice_features).unwrap();
|
||||
let route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -2669,7 +2669,7 @@ fn test_threaded_payment_retries() {
|
|||
invoice_features.set_basic_mpp_optional();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_expiry_time(payment_expiry_secs as u64)
|
||||
.with_features(invoice_features);
|
||||
.with_bolt11_features(invoice_features).unwrap();
|
||||
let mut route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
@ -2906,7 +2906,7 @@ fn do_claim_from_closed_chan(fail_payment: bool) {
|
|||
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[3]);
|
||||
let mut route_params = RouteParameters {
|
||||
payment_params: PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(nodes[1].node.invoice_features()),
|
||||
.with_bolt11_features(nodes[1].node.invoice_features()).unwrap(),
|
||||
final_value_msat: 10_000_000,
|
||||
};
|
||||
let mut route = nodes[0].router.find_route(&nodes[0].node.get_our_node_id(), &route_params,
|
||||
|
@ -3050,7 +3050,7 @@ fn do_test_payment_metadata_consistency(do_reload: bool, do_modify: bool) {
|
|||
let payment_metadata = vec![44, 49, 52, 142];
|
||||
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(nodes[1].node.invoice_features());
|
||||
.with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
|
||||
let mut route_params = RouteParameters {
|
||||
payment_params,
|
||||
final_value_msat: amt_msat,
|
||||
|
|
|
@ -67,8 +67,8 @@ fn test_priv_forwarding_rejection() {
|
|||
}]);
|
||||
let last_hops = vec![route_hint];
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
|
||||
.with_features(nodes[2].node.invoice_features())
|
||||
.with_route_hints(last_hops);
|
||||
.with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
|
||||
.with_route_hints(last_hops).unwrap();
|
||||
let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000);
|
||||
|
||||
nodes[0].node.send_payment_with_route(&route, our_payment_hash,
|
||||
|
@ -236,8 +236,8 @@ fn test_routed_scid_alias() {
|
|||
htlc_minimum_msat: None,
|
||||
}])];
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
|
||||
.with_features(nodes[2].node.invoice_features())
|
||||
.with_route_hints(hop_hints);
|
||||
.with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
|
||||
.with_route_hints(hop_hints).unwrap();
|
||||
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000);
|
||||
assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
|
||||
nodes[0].node.send_payment_with_route(&route, payment_hash,
|
||||
|
@ -402,8 +402,8 @@ fn test_inbound_scid_privacy() {
|
|||
htlc_minimum_msat: None,
|
||||
}])];
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
|
||||
.with_features(nodes[2].node.invoice_features())
|
||||
.with_route_hints(hop_hints.clone());
|
||||
.with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
|
||||
.with_route_hints(hop_hints.clone()).unwrap();
|
||||
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000);
|
||||
assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
|
||||
nodes[0].node.send_payment_with_route(&route, payment_hash,
|
||||
|
@ -418,8 +418,8 @@ fn test_inbound_scid_privacy() {
|
|||
hop_hints[0].0[0].short_channel_id = last_hop[0].short_channel_id.unwrap();
|
||||
|
||||
let payment_params_2 = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
|
||||
.with_features(nodes[2].node.invoice_features())
|
||||
.with_route_hints(hop_hints);
|
||||
.with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
|
||||
.with_route_hints(hop_hints).unwrap();
|
||||
let (route_2, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_2, 100_000);
|
||||
assert_eq!(route_2.paths[0].hops[1].short_channel_id, last_hop[0].short_channel_id.unwrap());
|
||||
nodes[0].node.send_payment_with_route(&route_2, payment_hash_2,
|
||||
|
@ -470,8 +470,8 @@ fn test_scid_alias_returned() {
|
|||
htlc_minimum_msat: None,
|
||||
}])];
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
|
||||
.with_features(nodes[2].node.invoice_features())
|
||||
.with_route_hints(hop_hints);
|
||||
.with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
|
||||
.with_route_hints(hop_hints).unwrap();
|
||||
let (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000);
|
||||
assert_eq!(route.paths[0].hops[1].short_channel_id, nodes[2].node.list_usable_channels()[0].inbound_scid_alias.unwrap());
|
||||
|
||||
|
|
|
@ -94,9 +94,9 @@ fn updates_shutdown_wait() {
|
|||
|
||||
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[0]);
|
||||
|
||||
let payment_params_1 = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_features(nodes[1].node.invoice_features());
|
||||
let payment_params_1 = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
|
||||
let route_1 = get_route(&nodes[0].node.get_our_node_id(), &payment_params_1, &nodes[0].network_graph.read_only(), None, 100000, &logger, &scorer, &random_seed_bytes).unwrap();
|
||||
let payment_params_2 = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), TEST_FINAL_CLTV).with_features(nodes[0].node.invoice_features());
|
||||
let payment_params_2 = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), TEST_FINAL_CLTV).with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
|
||||
let route_2 = get_route(&nodes[1].node.get_our_node_id(), &payment_params_2, &nodes[1].network_graph.read_only(), None, 100000, &logger, &scorer, &random_seed_bytes).unwrap();
|
||||
unwrap_send_err!(nodes[0].node.send_payment_with_route(&route_1, payment_hash,
|
||||
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)
|
||||
|
|
|
@ -16,7 +16,7 @@ use bitcoin::hashes::sha256::Hash as Sha256;
|
|||
use crate::blinded_path::{BlindedHop, BlindedPath};
|
||||
use crate::ln::PaymentHash;
|
||||
use crate::ln::channelmanager::{ChannelDetails, PaymentId};
|
||||
use crate::ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
|
||||
use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, InvoiceFeatures, NodeFeatures};
|
||||
use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
|
||||
use crate::offers::invoice::BlindedPayInfo;
|
||||
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
|
||||
|
@ -29,7 +29,7 @@ use crate::io;
|
|||
use crate::prelude::*;
|
||||
use crate::sync::Mutex;
|
||||
use alloc::collections::BinaryHeap;
|
||||
use core::cmp;
|
||||
use core::{cmp, fmt};
|
||||
use core::ops::Deref;
|
||||
|
||||
/// A [`Router`] implemented using [`find_route`].
|
||||
|
@ -442,7 +442,7 @@ impl Writeable for RouteParameters {
|
|||
(2, self.final_value_msat, required),
|
||||
// LDK versions prior to 0.0.114 had the `final_cltv_expiry_delta` parameter in
|
||||
// `RouteParameters` directly. For compatibility, we write it here.
|
||||
(4, self.payment_params.final_cltv_expiry_delta, required),
|
||||
(4, self.payment_params.payee.final_cltv_expiry_delta(), option),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
@ -453,11 +453,13 @@ impl Readable for RouteParameters {
|
|||
_init_and_read_tlv_fields!(reader, {
|
||||
(0, payment_params, (required: ReadableArgs, 0)),
|
||||
(2, final_value_msat, required),
|
||||
(4, final_cltv_expiry_delta, required),
|
||||
(4, final_cltv_delta, option),
|
||||
});
|
||||
let mut payment_params: PaymentParameters = payment_params.0.unwrap();
|
||||
if payment_params.final_cltv_expiry_delta == 0 {
|
||||
payment_params.final_cltv_expiry_delta = final_cltv_expiry_delta.0.unwrap();
|
||||
if let Payee::Clear { ref mut final_cltv_expiry_delta, .. } = payment_params.payee {
|
||||
if final_cltv_expiry_delta == &0 {
|
||||
*final_cltv_expiry_delta = final_cltv_delta.ok_or(DecodeError::InvalidValue)?;
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
payment_params,
|
||||
|
@ -490,22 +492,11 @@ const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40;
|
|||
// down from (1300-93) / 61 = 19.78... to arrive at a conservative estimate of 19.
|
||||
const MAX_PATH_LENGTH_ESTIMATE: u8 = 19;
|
||||
|
||||
/// The recipient of a payment.
|
||||
/// Information used to route a payment.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct PaymentParameters {
|
||||
/// The node id of the payee.
|
||||
pub payee_pubkey: PublicKey,
|
||||
|
||||
/// Features supported by the payee.
|
||||
///
|
||||
/// May be set from the payee's invoice or via [`for_keysend`]. May be `None` if the invoice
|
||||
/// does not contain any features.
|
||||
///
|
||||
/// [`for_keysend`]: Self::for_keysend
|
||||
pub features: Option<InvoiceFeatures>,
|
||||
|
||||
/// Hints for routing to the payee, containing channels connecting the payee to public nodes.
|
||||
pub route_hints: Hints,
|
||||
/// Information about the payee, such as their features and route hints for their channels.
|
||||
pub payee: Payee,
|
||||
|
||||
/// Expiration of a payment to the payee, in seconds relative to the UNIX epoch.
|
||||
pub expiry_time: Option<u64>,
|
||||
|
@ -537,30 +528,27 @@ pub struct PaymentParameters {
|
|||
/// payment to fail. Future attempts for the same payment shouldn't be relayed through any of
|
||||
/// these SCIDs.
|
||||
pub previously_failed_channels: Vec<u64>,
|
||||
|
||||
/// The minimum CLTV delta at the end of the route. This value must not be zero.
|
||||
pub final_cltv_expiry_delta: u32,
|
||||
}
|
||||
|
||||
impl Writeable for PaymentParameters {
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
|
||||
let mut clear_hints = &vec![];
|
||||
let mut blinded_hints = &vec![];
|
||||
match &self.route_hints {
|
||||
Hints::Clear(hints) => clear_hints = hints,
|
||||
Hints::Blinded(hints) => blinded_hints = hints,
|
||||
match &self.payee {
|
||||
Payee::Clear { route_hints, .. } => clear_hints = route_hints,
|
||||
Payee::Blinded { route_hints, .. } => blinded_hints = route_hints,
|
||||
}
|
||||
write_tlv_fields!(writer, {
|
||||
(0, self.payee_pubkey, required),
|
||||
(0, self.payee.node_id(), option),
|
||||
(1, self.max_total_cltv_expiry_delta, required),
|
||||
(2, self.features, option),
|
||||
(2, self.payee.features(), option),
|
||||
(3, self.max_path_count, required),
|
||||
(4, *clear_hints, vec_type),
|
||||
(5, self.max_channel_saturation_power_of_half, required),
|
||||
(6, self.expiry_time, option),
|
||||
(7, self.previously_failed_channels, vec_type),
|
||||
(8, *blinded_hints, optional_vec),
|
||||
(9, self.final_cltv_expiry_delta, required),
|
||||
(9, self.payee.final_cltv_expiry_delta(), option),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
@ -569,9 +557,9 @@ impl Writeable for PaymentParameters {
|
|||
impl ReadableArgs<u32> for PaymentParameters {
|
||||
fn read<R: io::Read>(reader: &mut R, default_final_cltv_expiry_delta: u32) -> Result<Self, DecodeError> {
|
||||
_init_and_read_tlv_fields!(reader, {
|
||||
(0, payee_pubkey, required),
|
||||
(0, payee_pubkey, option),
|
||||
(1, max_total_cltv_expiry_delta, (default_value, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA)),
|
||||
(2, features, option),
|
||||
(2, features, (option: ReadableArgs, payee_pubkey.is_some())),
|
||||
(3, max_path_count, (default_value, DEFAULT_MAX_PATH_COUNT)),
|
||||
(4, route_hints, vec_type),
|
||||
(5, max_channel_saturation_power_of_half, (default_value, 2)),
|
||||
|
@ -582,22 +570,27 @@ impl ReadableArgs<u32> for PaymentParameters {
|
|||
});
|
||||
let clear_route_hints = route_hints.unwrap_or(vec![]);
|
||||
let blinded_route_hints = blinded_route_hints.unwrap_or(vec![]);
|
||||
let route_hints = if blinded_route_hints.len() != 0 {
|
||||
if clear_route_hints.len() != 0 { return Err(DecodeError::InvalidValue) }
|
||||
Hints::Blinded(blinded_route_hints)
|
||||
let payee = if blinded_route_hints.len() != 0 {
|
||||
if clear_route_hints.len() != 0 || payee_pubkey.is_some() { return Err(DecodeError::InvalidValue) }
|
||||
Payee::Blinded {
|
||||
route_hints: blinded_route_hints,
|
||||
features: features.and_then(|f: Features| f.bolt12()),
|
||||
}
|
||||
} else {
|
||||
Hints::Clear(clear_route_hints)
|
||||
Payee::Clear {
|
||||
route_hints: clear_route_hints,
|
||||
node_id: payee_pubkey.ok_or(DecodeError::InvalidValue)?,
|
||||
features: features.and_then(|f| f.bolt11()),
|
||||
final_cltv_expiry_delta: final_cltv_expiry_delta.0.unwrap(),
|
||||
}
|
||||
};
|
||||
Ok(Self {
|
||||
payee_pubkey: _init_tlv_based_struct_field!(payee_pubkey, required),
|
||||
max_total_cltv_expiry_delta: _init_tlv_based_struct_field!(max_total_cltv_expiry_delta, (default_value, unused)),
|
||||
features,
|
||||
max_path_count: _init_tlv_based_struct_field!(max_path_count, (default_value, unused)),
|
||||
route_hints,
|
||||
payee,
|
||||
max_channel_saturation_power_of_half: _init_tlv_based_struct_field!(max_channel_saturation_power_of_half, (default_value, unused)),
|
||||
expiry_time,
|
||||
previously_failed_channels: previously_failed_channels.unwrap_or(Vec::new()),
|
||||
final_cltv_expiry_delta: _init_tlv_based_struct_field!(final_cltv_expiry_delta, (default_value, unused)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -610,15 +603,12 @@ impl PaymentParameters {
|
|||
/// provided.
|
||||
pub fn from_node_id(payee_pubkey: PublicKey, final_cltv_expiry_delta: u32) -> Self {
|
||||
Self {
|
||||
payee_pubkey,
|
||||
features: None,
|
||||
route_hints: Hints::Clear(vec![]),
|
||||
payee: Payee::Clear { node_id: payee_pubkey, route_hints: vec![], features: None, final_cltv_expiry_delta },
|
||||
expiry_time: None,
|
||||
max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
|
||||
max_path_count: DEFAULT_MAX_PATH_COUNT,
|
||||
max_channel_saturation_power_of_half: 2,
|
||||
previously_failed_channels: Vec::new(),
|
||||
final_cltv_expiry_delta,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,21 +617,39 @@ impl PaymentParameters {
|
|||
/// The `final_cltv_expiry_delta` should match the expected final CLTV delta the recipient has
|
||||
/// provided.
|
||||
pub fn for_keysend(payee_pubkey: PublicKey, final_cltv_expiry_delta: u32) -> Self {
|
||||
Self::from_node_id(payee_pubkey, final_cltv_expiry_delta).with_features(InvoiceFeatures::for_keysend())
|
||||
Self::from_node_id(payee_pubkey, final_cltv_expiry_delta).with_bolt11_features(InvoiceFeatures::for_keysend()).expect("PaymentParameters::from_node_id should always initialize the payee as unblinded")
|
||||
}
|
||||
|
||||
/// Includes the payee's features.
|
||||
/// Includes the payee's features. Errors if the parameters were initialized with blinded payment
|
||||
/// paths.
|
||||
///
|
||||
/// This is not exported to bindings users since bindings don't support move semantics
|
||||
pub fn with_features(self, features: InvoiceFeatures) -> Self {
|
||||
Self { features: Some(features), ..self }
|
||||
pub fn with_bolt11_features(self, features: InvoiceFeatures) -> Result<Self, ()> {
|
||||
match self.payee {
|
||||
Payee::Blinded { .. } => Err(()),
|
||||
Payee::Clear { route_hints, node_id, final_cltv_expiry_delta, .. } =>
|
||||
Ok(Self {
|
||||
payee: Payee::Clear {
|
||||
route_hints, node_id, features: Some(features), final_cltv_expiry_delta
|
||||
}, ..self
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Includes hints for routing to the payee.
|
||||
/// Includes hints for routing to the payee. Errors if the parameters were initialized with
|
||||
/// blinded payment paths.
|
||||
///
|
||||
/// This is not exported to bindings users since bindings don't support move semantics
|
||||
pub fn with_route_hints(self, route_hints: Vec<RouteHint>) -> Self {
|
||||
Self { route_hints: Hints::Clear(route_hints), ..self }
|
||||
pub fn with_route_hints(self, route_hints: Vec<RouteHint>) -> Result<Self, ()> {
|
||||
match self.payee {
|
||||
Payee::Blinded { .. } => Err(()),
|
||||
Payee::Clear { node_id, features, final_cltv_expiry_delta, .. } =>
|
||||
Ok(Self {
|
||||
payee: Payee::Clear {
|
||||
route_hints, node_id, features, final_cltv_expiry_delta,
|
||||
}, ..self
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Includes a payment expiration in seconds relative to the UNIX epoch.
|
||||
|
@ -673,14 +681,111 @@ impl PaymentParameters {
|
|||
}
|
||||
}
|
||||
|
||||
/// Routing hints for the tail of the route.
|
||||
/// The recipient of a payment, differing based on whether they've hidden their identity with route
|
||||
/// blinding.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum Hints {
|
||||
pub enum Payee {
|
||||
/// The recipient provided blinded paths and payinfo to reach them. The blinded paths themselves
|
||||
/// will be included in the final [`Route`].
|
||||
Blinded(Vec<(BlindedPayInfo, BlindedPath)>),
|
||||
Blinded {
|
||||
/// Aggregated routing info and blinded paths, for routing to the payee without knowing their
|
||||
/// node id.
|
||||
route_hints: Vec<(BlindedPayInfo, BlindedPath)>,
|
||||
/// Features supported by the payee.
|
||||
///
|
||||
/// May be set from the payee's invoice. May be `None` if the invoice does not contain any
|
||||
/// features.
|
||||
features: Option<Bolt12InvoiceFeatures>,
|
||||
},
|
||||
/// The recipient included these route hints in their BOLT11 invoice.
|
||||
Clear(Vec<RouteHint>),
|
||||
Clear {
|
||||
/// The node id of the payee.
|
||||
node_id: PublicKey,
|
||||
/// Hints for routing to the payee, containing channels connecting the payee to public nodes.
|
||||
route_hints: Vec<RouteHint>,
|
||||
/// Features supported by the payee.
|
||||
///
|
||||
/// May be set from the payee's invoice or via [`for_keysend`]. May be `None` if the invoice
|
||||
/// does not contain any features.
|
||||
///
|
||||
/// [`for_keysend`]: PaymentParameters::for_keysend
|
||||
features: Option<InvoiceFeatures>,
|
||||
/// The minimum CLTV delta at the end of the route. This value must not be zero.
|
||||
final_cltv_expiry_delta: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl Payee {
|
||||
fn node_id(&self) -> Option<PublicKey> {
|
||||
match self {
|
||||
Self::Clear { node_id, .. } => Some(*node_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn node_features(&self) -> Option<NodeFeatures> {
|
||||
match self {
|
||||
Self::Clear { features, .. } => features.as_ref().map(|f| f.to_context()),
|
||||
Self::Blinded { features, .. } => features.as_ref().map(|f| f.to_context()),
|
||||
}
|
||||
}
|
||||
fn supports_basic_mpp(&self) -> bool {
|
||||
match self {
|
||||
Self::Clear { features, .. } => features.as_ref().map_or(false, |f| f.supports_basic_mpp()),
|
||||
Self::Blinded { features, .. } => features.as_ref().map_or(false, |f| f.supports_basic_mpp()),
|
||||
}
|
||||
}
|
||||
fn features(&self) -> Option<FeaturesRef> {
|
||||
match self {
|
||||
Self::Clear { features, .. } => features.as_ref().map(|f| FeaturesRef::Bolt11(f)),
|
||||
Self::Blinded { features, .. } => features.as_ref().map(|f| FeaturesRef::Bolt12(f)),
|
||||
}
|
||||
}
|
||||
fn final_cltv_expiry_delta(&self) -> Option<u32> {
|
||||
match self {
|
||||
Self::Clear { final_cltv_expiry_delta, .. } => Some(*final_cltv_expiry_delta),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum FeaturesRef<'a> {
|
||||
Bolt11(&'a InvoiceFeatures),
|
||||
Bolt12(&'a Bolt12InvoiceFeatures),
|
||||
}
|
||||
enum Features {
|
||||
Bolt11(InvoiceFeatures),
|
||||
Bolt12(Bolt12InvoiceFeatures),
|
||||
}
|
||||
|
||||
impl Features {
|
||||
fn bolt12(self) -> Option<Bolt12InvoiceFeatures> {
|
||||
match self {
|
||||
Self::Bolt12(f) => Some(f),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn bolt11(self) -> Option<InvoiceFeatures> {
|
||||
match self {
|
||||
Self::Bolt11(f) => Some(f),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Writeable for FeaturesRef<'a> {
|
||||
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
|
||||
match self {
|
||||
Self::Bolt11(f) => Ok(f.write(w)?),
|
||||
Self::Bolt12(f) => Ok(f.write(w)?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadableArgs<bool> for Features {
|
||||
fn read<R: io::Read>(reader: &mut R, bolt11: bool) -> Result<Self, DecodeError> {
|
||||
if bolt11 { return Ok(Self::Bolt11(Readable::read(reader)?)) }
|
||||
Ok(Self::Bolt12(Readable::read(reader)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of hops along a payment path terminating with a channel to the recipient.
|
||||
|
@ -1067,6 +1172,21 @@ fn default_node_features() -> NodeFeatures {
|
|||
features
|
||||
}
|
||||
|
||||
struct LoggedPayeePubkey(Option<PublicKey>);
|
||||
impl fmt::Display for LoggedPayeePubkey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.0 {
|
||||
Some(pk) => {
|
||||
"payee node id ".fmt(f)?;
|
||||
pk.fmt(f)
|
||||
},
|
||||
None => {
|
||||
"blinded payee".fmt(f)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds a route from us (payer) to the given target node (payee).
|
||||
///
|
||||
/// If the payee provided features in their invoice, they should be provided via `params.payee`.
|
||||
|
@ -1116,10 +1236,16 @@ pub(crate) fn get_route<L: Deref, S: Score>(
|
|||
_random_seed_bytes: &[u8; 32]
|
||||
) -> Result<Route, LightningError>
|
||||
where L::Target: Logger {
|
||||
let payee_node_id = NodeId::from_pubkey(&payment_params.payee_pubkey);
|
||||
// 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,
|
||||
// so use a dummy id for this in the blinded case.
|
||||
let payee_node_id_opt = payment_params.payee.node_id().map(|pk| NodeId::from_pubkey(&pk));
|
||||
const DUMMY_BLINDED_PAYEE_ID: [u8; 33] = [42u8; 33];
|
||||
let maybe_dummy_payee_pk = payment_params.payee.node_id().unwrap_or_else(|| PublicKey::from_slice(&DUMMY_BLINDED_PAYEE_ID).unwrap());
|
||||
let maybe_dummy_payee_node_id = NodeId::from_pubkey(&maybe_dummy_payee_pk);
|
||||
let our_node_id = NodeId::from_pubkey(&our_node_pubkey);
|
||||
|
||||
if payee_node_id == our_node_id {
|
||||
if payee_node_id_opt.map_or(false, |payee| payee == our_node_id) {
|
||||
return Err(LightningError{err: "Cannot generate a route to ourselves".to_owned(), action: ErrorAction::IgnoreError});
|
||||
}
|
||||
|
||||
|
@ -1131,11 +1257,11 @@ where L::Target: Logger {
|
|||
return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError});
|
||||
}
|
||||
|
||||
match &payment_params.route_hints {
|
||||
Hints::Clear(hints) => {
|
||||
for route in hints.iter() {
|
||||
match &payment_params.payee {
|
||||
Payee::Clear { route_hints, node_id, .. } => {
|
||||
for route in route_hints.iter() {
|
||||
for hop in &route.0 {
|
||||
if hop.src_node_id == payment_params.payee_pubkey {
|
||||
if hop.src_node_id == *node_id {
|
||||
return Err(LightningError{err: "Route hint cannot have the payee as the source.".to_owned(), action: ErrorAction::IgnoreError});
|
||||
}
|
||||
}
|
||||
|
@ -1144,7 +1270,8 @@ where L::Target: Logger {
|
|||
_ => return Err(LightningError{err: "Routing to blinded paths isn't supported yet".to_owned(), action: ErrorAction::IgnoreError}),
|
||||
|
||||
}
|
||||
if payment_params.max_total_cltv_expiry_delta <= payment_params.final_cltv_expiry_delta {
|
||||
let final_cltv_expiry_delta = payment_params.payee.final_cltv_expiry_delta().unwrap_or(0);
|
||||
if payment_params.max_total_cltv_expiry_delta <= final_cltv_expiry_delta {
|
||||
return Err(LightningError{err: "Can't find a route where the maximum total CLTV expiry delta is below the final CLTV expiry.".to_owned(), action: ErrorAction::IgnoreError});
|
||||
}
|
||||
|
||||
|
@ -1216,16 +1343,15 @@ where L::Target: Logger {
|
|||
// work reliably.
|
||||
let allow_mpp = if payment_params.max_path_count == 1 {
|
||||
false
|
||||
} else if let Some(features) = &payment_params.features {
|
||||
features.supports_basic_mpp()
|
||||
} else if let Some(node) = network_nodes.get(&payee_node_id) {
|
||||
if let Some(node_info) = node.announcement_info.as_ref() {
|
||||
node_info.features.supports_basic_mpp()
|
||||
} else { false }
|
||||
} else if payment_params.payee.supports_basic_mpp() {
|
||||
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()))
|
||||
} else { false };
|
||||
|
||||
log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP and {} first hops {}overriding the network graph", our_node_pubkey,
|
||||
payment_params.payee_pubkey, if allow_mpp { "with" } else { "without" },
|
||||
log_trace!(logger, "Searching for a route from payer {} to {} {} MPP and {} first hops {}overriding the network graph", 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 " });
|
||||
|
||||
// Step (1).
|
||||
|
@ -1328,7 +1454,8 @@ where L::Target: Logger {
|
|||
});
|
||||
}
|
||||
|
||||
log_trace!(logger, "Building path from {} (payee) to {} (us/payer) for value {} msat.", payment_params.payee_pubkey, our_node_pubkey, final_value_msat);
|
||||
log_trace!(logger, "Building path from {} to payer {} for value {} msat.",
|
||||
LoggedPayeePubkey(payment_params.payee.node_id()), our_node_pubkey, final_value_msat);
|
||||
|
||||
macro_rules! add_entry {
|
||||
// Adds entry which goes from $src_node_id to $dest_node_id over the $candidate hop.
|
||||
|
@ -1375,9 +1502,9 @@ where L::Target: Logger {
|
|||
// In order to already account for some of the privacy enhancing random CLTV
|
||||
// expiry delta offset we add on top later, we subtract a rough estimate
|
||||
// (2*MEDIAN_HOP_CLTV_EXPIRY_DELTA) here.
|
||||
let max_total_cltv_expiry_delta = (payment_params.max_total_cltv_expiry_delta - payment_params.final_cltv_expiry_delta)
|
||||
let max_total_cltv_expiry_delta = (payment_params.max_total_cltv_expiry_delta - final_cltv_expiry_delta)
|
||||
.checked_sub(2*MEDIAN_HOP_CLTV_EXPIRY_DELTA)
|
||||
.unwrap_or(payment_params.max_total_cltv_expiry_delta - payment_params.final_cltv_expiry_delta);
|
||||
.unwrap_or(payment_params.max_total_cltv_expiry_delta - final_cltv_expiry_delta);
|
||||
let hop_total_cltv_delta = ($next_hops_cltv_delta as u32)
|
||||
.saturating_add($candidate.cltv_expiry_delta());
|
||||
let exceeds_cltv_delta_limit = hop_total_cltv_delta > max_total_cltv_expiry_delta;
|
||||
|
@ -1577,7 +1704,7 @@ where L::Target: Logger {
|
|||
// Entries are added to dist in add_entry!() when there is a channel from a node.
|
||||
// Because there are no channels from payee, it will not have a dist entry at this point.
|
||||
// If we're processing any other node, it is always be the result of a channel from it.
|
||||
assert_eq!($node_id, payee_node_id);
|
||||
debug_assert_eq!($node_id, maybe_dummy_payee_node_id);
|
||||
false
|
||||
};
|
||||
|
||||
|
@ -1637,35 +1764,35 @@ where L::Target: Logger {
|
|||
|
||||
// If first hop is a private channel and the only way to reach the payee, this is the only
|
||||
// place where it could be added.
|
||||
if let Some(first_channels) = first_hop_targets.get(&payee_node_id) {
|
||||
payee_node_id_opt.map(|payee| first_hop_targets.get(&payee).map(|first_channels| {
|
||||
for details in first_channels {
|
||||
let candidate = CandidateRouteHop::FirstHop { details };
|
||||
let added = add_entry!(candidate, our_node_id, payee_node_id, 0, path_value_msat,
|
||||
let added = add_entry!(candidate, our_node_id, payee, 0, path_value_msat,
|
||||
0, 0u64, 0, 0);
|
||||
log_trace!(logger, "{} direct route to payee via SCID {}",
|
||||
if added { "Added" } else { "Skipped" }, candidate.short_channel_id());
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Add the payee as a target, so that the payee-to-payer
|
||||
// search algorithm knows what to start with.
|
||||
match network_nodes.get(&payee_node_id) {
|
||||
payee_node_id_opt.map(|payee| match network_nodes.get(&payee) {
|
||||
// The payee is not in our network graph, so nothing to add here.
|
||||
// There is still a chance of reaching them via last_hops though,
|
||||
// so don't yet fail the payment here.
|
||||
// If not, targets.pop() will not even let us enter the loop in step 2.
|
||||
None => {},
|
||||
Some(node) => {
|
||||
add_entries_to_cheapest_to_target_node!(node, payee_node_id, 0, path_value_msat, 0, 0u64, 0, 0);
|
||||
add_entries_to_cheapest_to_target_node!(node, payee, 0, path_value_msat, 0, 0u64, 0, 0);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
// Step (2).
|
||||
// If a caller provided us with last hops, add them to routing targets. Since this happens
|
||||
// earlier than general path finding, they will be somewhat prioritized, although currently
|
||||
// it matters only if the fees are exactly the same.
|
||||
let route_hints = match &payment_params.route_hints {
|
||||
Hints::Clear(hints) => hints,
|
||||
let route_hints = match &payment_params.payee {
|
||||
Payee::Clear { route_hints, .. } => route_hints,
|
||||
_ => return Err(LightningError{err: "Routing to blinded paths isn't supported yet".to_owned(), action: ErrorAction::IgnoreError}),
|
||||
};
|
||||
for route in route_hints.iter().filter(|route| !route.0.is_empty()) {
|
||||
|
@ -1680,7 +1807,7 @@ where L::Target: Logger {
|
|||
// We start building the path from reverse, i.e., from payee
|
||||
// to the first RouteHintHop in the path.
|
||||
let hop_iter = route.0.iter().rev();
|
||||
let prev_hop_iter = core::iter::once(&payment_params.payee_pubkey).chain(
|
||||
let prev_hop_iter = core::iter::once(&maybe_dummy_payee_pk).chain(
|
||||
route.0.iter().skip(1).rev().map(|hop| &hop.src_node_id));
|
||||
let mut hop_used = true;
|
||||
let mut aggregate_next_hops_fee_msat: u64 = 0;
|
||||
|
@ -1840,7 +1967,7 @@ where L::Target: Logger {
|
|||
// 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.
|
||||
if ordered_hops.last().unwrap().0.node_id == payee_node_id {
|
||||
if ordered_hops.last().unwrap().0.node_id == maybe_dummy_payee_node_id {
|
||||
break 'path_walk;
|
||||
}
|
||||
|
||||
|
@ -1923,7 +2050,7 @@ where L::Target: Logger {
|
|||
// If we found a path back to the payee, we shouldn't try to process it again. This is
|
||||
// the equivalent of the `elem.was_processed` check in
|
||||
// add_entries_to_cheapest_to_target_node!() (see comment there for more info).
|
||||
if node_id == payee_node_id { continue 'path_construction; }
|
||||
if node_id == maybe_dummy_payee_node_id { continue 'path_construction; }
|
||||
|
||||
// Otherwise, since the current target node is not us,
|
||||
// keep "unrolling" the payment graph from payee to payer by
|
||||
|
@ -2067,7 +2194,7 @@ where L::Target: Logger {
|
|||
}).collect::<Vec<_>>();
|
||||
// Propagate the cltv_expiry_delta one hop backwards since the delta from the current hop is
|
||||
// applicable for the previous hop.
|
||||
path.iter_mut().rev().fold(payment_params.final_cltv_expiry_delta, |prev_cltv_expiry_delta, hop| {
|
||||
path.iter_mut().rev().fold(final_cltv_expiry_delta, |prev_cltv_expiry_delta, hop| {
|
||||
core::mem::replace(&mut hop.as_mut().unwrap().cltv_expiry_delta, prev_cltv_expiry_delta)
|
||||
});
|
||||
selected_paths.push(path);
|
||||
|
@ -2075,10 +2202,10 @@ where L::Target: Logger {
|
|||
// Make sure we would never create a route with more paths than we allow.
|
||||
debug_assert!(selected_paths.len() <= payment_params.max_path_count.into());
|
||||
|
||||
if let Some(features) = &payment_params.features {
|
||||
if let Some(node_features) = payment_params.payee.node_features() {
|
||||
for path in selected_paths.iter_mut() {
|
||||
if let Ok(route_hop) = path.last_mut().unwrap() {
|
||||
route_hop.node_features = features.to_context();
|
||||
route_hop.node_features = node_features.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2093,7 +2220,7 @@ where L::Target: Logger {
|
|||
paths,
|
||||
payment_params: Some(payment_params.clone()),
|
||||
};
|
||||
log_info!(logger, "Got route to {}: {}", payment_params.payee_pubkey, log_route!(route));
|
||||
log_info!(logger, "Got route: {}", log_route!(route));
|
||||
Ok(route)
|
||||
}
|
||||
|
||||
|
@ -2528,7 +2655,7 @@ mod tests {
|
|||
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
|
||||
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
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();
|
||||
|
@ -2926,13 +3053,13 @@ mod tests {
|
|||
let mut invalid_last_hops = last_hops_multi_private_channels(&nodes);
|
||||
invalid_last_hops.push(invalid_last_hop);
|
||||
{
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(invalid_last_hops);
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(invalid_last_hops).unwrap();
|
||||
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &random_seed_bytes) {
|
||||
assert_eq!(err, "Route hint cannot have the payee as the source.");
|
||||
} else { panic!(); }
|
||||
}
|
||||
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops_multi_private_channels(&nodes));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops_multi_private_channels(&nodes)).unwrap();
|
||||
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
|
||||
assert_eq!(route.paths[0].hops.len(), 5);
|
||||
|
||||
|
@ -3002,7 +3129,7 @@ mod tests {
|
|||
fn ignores_empty_last_hops_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[6], 42).with_route_hints(empty_last_hop(&nodes));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(empty_last_hop(&nodes)).unwrap();
|
||||
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();
|
||||
|
@ -3082,7 +3209,7 @@ mod tests {
|
|||
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
|
||||
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
|
||||
let last_hops = multi_hop_last_hops_hint([nodes[2], nodes[3]]);
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone());
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone()).unwrap();
|
||||
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();
|
||||
|
@ -3156,7 +3283,7 @@ mod tests {
|
|||
let non_announced_pubkey = PublicKey::from_secret_key(&secp_ctx, &non_announced_privkey);
|
||||
|
||||
let last_hops = multi_hop_last_hops_hint([nodes[2], non_announced_pubkey]);
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone());
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone()).unwrap();
|
||||
let scorer = ln_test_utils::TestScorer::new();
|
||||
// Test through channels 2, 3, 0xff00, 0xff01.
|
||||
// Test shows that multiple hop hints are considered.
|
||||
|
@ -3262,7 +3389,7 @@ mod tests {
|
|||
fn last_hops_with_public_channel_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[6], 42).with_route_hints(last_hops_with_public_channel(&nodes));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops_with_public_channel(&nodes)).unwrap();
|
||||
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();
|
||||
|
@ -3321,7 +3448,7 @@ mod tests {
|
|||
// Simple test with outbound channel to 4 to test that last_hops and first_hops connect
|
||||
let our_chans = vec![get_channel_details(Some(42), nodes[3].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
|
||||
let mut last_hops = last_hops(&nodes);
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone());
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops.clone()).unwrap();
|
||||
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&our_chans.iter().collect::<Vec<_>>()), 100, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
|
||||
assert_eq!(route.paths[0].hops.len(), 2);
|
||||
|
||||
|
@ -3342,7 +3469,7 @@ mod tests {
|
|||
last_hops[0].0[0].fees.base_msat = 1000;
|
||||
|
||||
// Revert to via 6 as the fee on 8 goes up
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops);
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops).unwrap();
|
||||
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
|
||||
assert_eq!(route.paths[0].hops.len(), 4);
|
||||
|
||||
|
@ -3435,7 +3562,7 @@ mod tests {
|
|||
htlc_minimum_msat: None,
|
||||
htlc_maximum_msat: last_hop_htlc_max,
|
||||
}]);
|
||||
let payment_params = PaymentParameters::from_node_id(target_node_id, 42).with_route_hints(vec![last_hops]);
|
||||
let payment_params = PaymentParameters::from_node_id(target_node_id, 42).with_route_hints(vec![last_hops]).unwrap();
|
||||
let our_chans = vec![get_channel_details(Some(42), middle_node_id, InitFeatures::from_le_bytes(vec![0b11]), outbound_capacity_msat)];
|
||||
let scorer = ln_test_utils::TestScorer::new();
|
||||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
|
@ -3501,7 +3628,7 @@ mod tests {
|
|||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
|
||||
// We will use a simple single-path route from
|
||||
// our node to node2 via node0: channels {1, 3}.
|
||||
|
@ -3777,7 +3904,7 @@ mod tests {
|
|||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
|
||||
// Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
|
||||
// {12, 13, 11} have the capacities of 100, {6} has a capacity of 50.
|
||||
|
@ -3952,7 +4079,7 @@ mod tests {
|
|||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
|
||||
.with_features(channelmanager::provided_invoice_features(&config));
|
||||
.with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
|
||||
// We need a route consisting of 3 paths:
|
||||
// From our node to node2 via node0, node7, node1 (three paths one hop each).
|
||||
|
@ -4111,7 +4238,7 @@ mod tests {
|
|||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
|
||||
// We need a route consisting of 3 paths:
|
||||
// From our node to node3 via {node0, node2}, {node7, node2, node4} and {node7, node2}.
|
||||
|
@ -4276,7 +4403,7 @@ mod tests {
|
|||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
|
||||
// This test checks that if we have two cheaper paths and one more expensive path,
|
||||
// so that liquidity-wise any 2 of 3 combination is sufficient,
|
||||
|
@ -4446,7 +4573,7 @@ mod tests {
|
|||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
|
||||
// We need a route consisting of 2 paths:
|
||||
// From our node to node3 via {node0, node2} and {node7, node2, node4}.
|
||||
|
@ -4628,7 +4755,7 @@ mod tests {
|
|||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42).with_features(channelmanager::provided_invoice_features(&config))
|
||||
let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
|
||||
.with_route_hints(vec![RouteHint(vec![RouteHintHop {
|
||||
src_node_id: nodes[2],
|
||||
short_channel_id: 42,
|
||||
|
@ -4636,7 +4763,7 @@ mod tests {
|
|||
cltv_expiry_delta: 42,
|
||||
htlc_minimum_msat: None,
|
||||
htlc_maximum_msat: None,
|
||||
}])]).with_max_channel_saturation_power_of_half(0);
|
||||
}])]).unwrap().with_max_channel_saturation_power_of_half(0);
|
||||
|
||||
// Keep only two paths from us to nodes[2], both with a 99sat HTLC maximum, with one with
|
||||
// no fee and one with a 1msat fee. Previously, trying to route 100 sats to nodes[2] here
|
||||
|
@ -4720,7 +4847,7 @@ mod tests {
|
|||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config))
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
|
||||
.with_max_channel_saturation_power_of_half(0);
|
||||
|
||||
// We need a route consisting of 3 paths:
|
||||
|
@ -5076,7 +5203,7 @@ mod tests {
|
|||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
|
||||
// We modify the graph to set the htlc_minimum of channel 2 and 4 as needed - channel 2
|
||||
// gets an htlc_maximum_msat of 80_000 and channel 4 an htlc_minimum_msat of 90_000. We
|
||||
|
@ -5144,7 +5271,7 @@ mod tests {
|
|||
let network_graph = NetworkGraph::new(Network::Testnet, Arc::clone(&logger));
|
||||
let scorer = ln_test_utils::TestScorer::new();
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[0], 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[0], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
|
||||
|
@ -5210,7 +5337,7 @@ mod tests {
|
|||
fn prefers_shorter_route_with_higher_fees() {
|
||||
let (secp_ctx, network_graph, _, _, logger) = build_graph();
|
||||
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes)).unwrap();
|
||||
|
||||
// Without penalizing each hop 100 msats, a longer path with lower fees is chosen.
|
||||
let scorer = ln_test_utils::TestScorer::new();
|
||||
|
@ -5283,7 +5410,7 @@ mod tests {
|
|||
fn avoids_routing_through_bad_channels_and_nodes() {
|
||||
let (secp_ctx, network, _, _, logger) = build_graph();
|
||||
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes)).unwrap();
|
||||
let network_graph = network.read_only();
|
||||
|
||||
// A path to nodes[6] exists when no penalties are applied to any channel.
|
||||
|
@ -5406,7 +5533,7 @@ mod tests {
|
|||
|
||||
// Make sure that generally there is at least one route available
|
||||
let feasible_max_total_cltv_delta = 1008;
|
||||
let feasible_payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes))
|
||||
let feasible_payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes)).unwrap()
|
||||
.with_max_total_cltv_expiry_delta(feasible_max_total_cltv_delta);
|
||||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
|
@ -5416,7 +5543,7 @@ mod tests {
|
|||
|
||||
// But not if we exclude all paths on the basis of their accumulated CLTV delta
|
||||
let fail_max_total_cltv_delta = 23;
|
||||
let fail_payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes))
|
||||
let fail_payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes)).unwrap()
|
||||
.with_max_total_cltv_expiry_delta(fail_max_total_cltv_delta);
|
||||
match get_route(&our_id, &fail_payment_params, &network_graph, None, 100, Arc::clone(&logger), &scorer, &random_seed_bytes)
|
||||
{
|
||||
|
@ -5436,7 +5563,7 @@ mod tests {
|
|||
let network_graph = network.read_only();
|
||||
|
||||
let scorer = ln_test_utils::TestScorer::new();
|
||||
let mut payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes))
|
||||
let mut payment_params = PaymentParameters::from_node_id(nodes[6], 0).with_route_hints(last_hops(&nodes)).unwrap()
|
||||
.with_max_path_count(1);
|
||||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
|
@ -5492,7 +5619,7 @@ mod tests {
|
|||
|
||||
let scorer = ln_test_utils::TestScorer::new();
|
||||
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[6], 42).with_route_hints(last_hops(&nodes)).unwrap();
|
||||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
|
||||
|
@ -5638,7 +5765,7 @@ mod tests {
|
|||
});
|
||||
|
||||
let config = UserConfig::default();
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
|
||||
let random_seed_bytes = keys_manager.get_secure_random_bytes();
|
||||
// 100,000 sats is less than the available liquidity on each channel, set above.
|
||||
|
@ -5723,7 +5850,7 @@ mod tests {
|
|||
let src = &PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
|
||||
seed = seed.overflowing_mul(0xdeadbeef).0;
|
||||
let dst = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
|
||||
let payment_params = PaymentParameters::from_node_id(dst, 42).with_features(channelmanager::provided_invoice_features(&config));
|
||||
let payment_params = PaymentParameters::from_node_id(dst, 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
|
||||
let amt = seed as u64 % 200_000_000;
|
||||
let params = ProbabilisticScoringParameters::default();
|
||||
let scorer = ProbabilisticScorer::new(params, &graph, &logger);
|
||||
|
@ -6069,7 +6196,7 @@ mod benches {
|
|||
let src = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
|
||||
seed *= 0xdeadbeef;
|
||||
let dst = PublicKey::from_slice(nodes.unordered_keys().skip(seed % nodes.len()).next().unwrap().as_slice()).unwrap();
|
||||
let params = PaymentParameters::from_node_id(dst, 42).with_features(features.clone());
|
||||
let params = PaymentParameters::from_node_id(dst, 42).with_bolt11_features(features.clone()).unwrap();
|
||||
let first_hop = first_hop(src);
|
||||
let amt = seed as u64 % 1_000_000;
|
||||
if let Ok(route) = get_route(&payer, ¶ms, &graph.read_only(), Some(&[&first_hop]), amt, &DummyLogger{}, &scorer, &random_seed_bytes) {
|
||||
|
|
3
pending_changelog/blinded_pay_param_compat.txt
Normal file
3
pending_changelog/blinded_pay_param_compat.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
## Backwards Compatibility
|
||||
|
||||
* `PaymentParameters` written with blinded path info using 0.0.115 will not be readable in 0.0.116
|
Loading…
Add table
Reference in a new issue