Skip to content

Commit d7e2ff6

Browse files
committed
Introduce RouteParameters::max_total_routing_fee_msat
Currently, users have no means to upper-bound the total fees accruing when finding a route. Here, we add a corresponding field to `RouteParameters` which will be used to limit the candidate set during path finding in the following commits.
1 parent 7e7e7a0 commit d7e2ff6

File tree

6 files changed

+85
-39
lines changed

6 files changed

+85
-39
lines changed

lightning-invoice/src/payment.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,7 @@ fn pay_invoice_using_amount<P: Deref>(
116116
if let Some(features) = invoice.features() {
117117
payment_params = payment_params.with_bolt11_features(features.clone()).unwrap();
118118
}
119-
let route_params = RouteParameters {
120-
payment_params,
121-
final_value_msat: amount_msats,
122-
};
119+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, amount_msats);
123120

124121
payer.send_payment(payment_hash, recipient_onion, payment_id, route_params, retry_strategy)
125122
}
@@ -148,7 +145,7 @@ pub fn preflight_probe_invoice<C: AChannelManager>(
148145
if let Some(features) = invoice.features() {
149146
payment_params = payment_params.with_bolt11_features(features.clone()).unwrap();
150147
}
151-
let route_params = RouteParameters { payment_params, final_value_msat: amount_msat };
148+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, amount_msat);
152149

153150
channelmanager.get_cm().send_preflight_probes(route_params, liquidity_limit_multiplier)
154151
.map_err(ProbingError::Sending)
@@ -178,7 +175,7 @@ pub fn preflight_probe_zero_value_invoice<C: AChannelManager>(
178175
if let Some(features) = invoice.features() {
179176
payment_params = payment_params.with_bolt11_features(features.clone()).unwrap();
180177
}
181-
let route_params = RouteParameters { payment_params, final_value_msat: amount_msat };
178+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, amount_msat);
182179

183180
channelmanager.get_cm().send_preflight_probes(route_params, liquidity_limit_multiplier)
184181
.map_err(ProbingError::Sending)

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ fn do_one_hop_blinded_path(success: bool) {
4747
nodes[1].node.get_our_node_id(), payee_tlvs, &chanmon_cfgs[1].keys_manager, &secp_ctx
4848
).unwrap();
4949

50-
let route_params = RouteParameters {
51-
payment_params: PaymentParameters::blinded(vec![blinded_path]),
52-
final_value_msat: amt_msat
53-
};
50+
let route_params = RouteParameters::from_payment_params_and_value(
51+
PaymentParameters::blinded(vec![blinded_path]),
52+
amt_msat,
53+
);
5454
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(),
5555
PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
5656
check_added_monitors(&nodes[0], 1);
@@ -90,11 +90,11 @@ fn mpp_to_one_hop_blinded_path() {
9090

9191
let bolt12_features: Bolt12InvoiceFeatures =
9292
channelmanager::provided_invoice_features(&UserConfig::default()).to_context();
93-
let route_params = RouteParameters {
94-
payment_params: PaymentParameters::blinded(vec![blinded_path])
93+
let route_params = RouteParameters::from_payment_params_and_value(
94+
PaymentParameters::blinded(vec![blinded_path])
9595
.with_bolt12_features(bolt12_features).unwrap(),
96-
final_value_msat: amt_msat,
97-
};
96+
amt_msat,
97+
);
9898
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
9999
check_added_monitors(&nodes[0], 2);
100100

lightning/src/ln/channelmanager.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3570,7 +3570,7 @@ where
35703570
let payment_params =
35713571
PaymentParameters::from_node_id(node_id, final_cltv_expiry_delta);
35723572

3573-
let route_params = RouteParameters { payment_params, final_value_msat: amount_msat };
3573+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, amount_msat);
35743574

35753575
self.send_preflight_probes(route_params, liquidity_limit_multiplier)
35763576
}
@@ -9559,6 +9559,7 @@ where
95599559
pending_fee_msat: Some(path_fee),
95609560
total_msat: path_amt,
95619561
starting_block_height: best_block_height,
9562+
remaining_max_total_routing_fee_msat: None, // only used for retries, and we'll never retry on startup
95629563
});
95639564
log_info!(args.logger, "Added a pending payment for {} msat with payment hash {} for path with session priv {}",
95649565
path_amt, &htlc.payment_hash, log_bytes!(session_priv_bytes));

lightning/src/ln/outbound_payment.rs

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,14 @@ pub(crate) enum PendingOutboundPayment {
5454
AwaitingInvoice {
5555
timer_ticks_without_response: u8,
5656
retry_strategy: Retry,
57+
max_total_routing_fee_msat: Option<u64>,
5758
},
5859
InvoiceReceived {
5960
payment_hash: PaymentHash,
6061
retry_strategy: Retry,
62+
// Note this field is currently just replicated from AwaitingInvoice but not actually
63+
// used anywhere.
64+
max_total_routing_fee_msat: Option<u64>,
6165
},
6266
Retryable {
6367
retry_strategy: Option<Retry>,
@@ -76,6 +80,7 @@ pub(crate) enum PendingOutboundPayment {
7680
total_msat: u64,
7781
/// Our best known block height at the time this payment was initiated.
7882
starting_block_height: u32,
83+
remaining_max_total_routing_fee_msat: Option<u64>,
7984
},
8085
/// When a pending payment is fulfilled, we continue tracking it until all pending HTLCs have
8186
/// been resolved. This ensures we don't look up pending payments in ChannelMonitors on restart
@@ -731,12 +736,15 @@ impl OutboundPayments {
731736
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
732737
{
733738
let payment_hash = invoice.payment_hash();
739+
let mut max_total_routing_fee_msat = None;
734740
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
735741
hash_map::Entry::Occupied(entry) => match entry.get() {
736-
PendingOutboundPayment::AwaitingInvoice { retry_strategy, .. } => {
742+
PendingOutboundPayment::AwaitingInvoice { retry_strategy, max_total_routing_fee_msat: max_total_fee, .. } => {
743+
max_total_routing_fee_msat = *max_total_fee;
737744
*entry.into_mut() = PendingOutboundPayment::InvoiceReceived {
738745
payment_hash,
739746
retry_strategy: *retry_strategy,
747+
max_total_routing_fee_msat,
740748
};
741749
},
742750
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
@@ -747,6 +755,7 @@ impl OutboundPayments {
747755
let route_params = RouteParameters {
748756
payment_params: PaymentParameters::from_bolt12_invoice(&invoice),
749757
final_value_msat: invoice.amount_msats(),
758+
max_total_routing_fee_msat,
750759
};
751760

752761
self.find_route_and_send_payment(
@@ -779,11 +788,12 @@ impl OutboundPayments {
779788
let mut retry_id_route_params = None;
780789
for (pmt_id, pmt) in outbounds.iter_mut() {
781790
if pmt.is_auto_retryable_now() {
782-
if let PendingOutboundPayment::Retryable { pending_amt_msat, total_msat, payment_params: Some(params), payment_hash, .. } = pmt {
791+
if let PendingOutboundPayment::Retryable { pending_amt_msat, total_msat, payment_params: Some(params), payment_hash, remaining_max_total_routing_fee_msat, .. } = pmt {
783792
if pending_amt_msat < total_msat {
784793
retry_id_route_params = Some((*payment_hash, *pmt_id, RouteParameters {
785794
final_value_msat: *total_msat - *pending_amt_msat,
786795
payment_params: params.clone(),
796+
max_total_routing_fee_msat: *remaining_max_total_routing_fee_msat,
787797
}));
788798
break
789799
}
@@ -987,7 +997,7 @@ impl OutboundPayments {
987997
log_error!(logger, "Payment not yet sent");
988998
return
989999
},
990-
PendingOutboundPayment::InvoiceReceived { payment_hash, retry_strategy } => {
1000+
PendingOutboundPayment::InvoiceReceived { payment_hash, retry_strategy, .. } => {
9911001
let total_amount = route_params.final_value_msat;
9921002
let recipient_onion = RecipientOnionFields {
9931003
payment_secret: None,
@@ -1207,6 +1217,8 @@ impl OutboundPayments {
12071217
custom_tlvs: recipient_onion.custom_tlvs,
12081218
starting_block_height: best_block_height,
12091219
total_msat: route.get_total_amount(),
1220+
remaining_max_total_routing_fee_msat:
1221+
route.route_params.as_ref().and_then(|p| p.max_total_routing_fee_msat),
12101222
};
12111223

12121224
for (path, session_priv_bytes) in route.paths.iter().zip(onion_session_privs.iter()) {
@@ -1218,7 +1230,7 @@ impl OutboundPayments {
12181230

12191231
#[allow(unused)]
12201232
pub(super) fn add_new_awaiting_invoice(
1221-
&self, payment_id: PaymentId, retry_strategy: Retry
1233+
&self, payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
12221234
) -> Result<(), ()> {
12231235
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
12241236
match pending_outbounds.entry(payment_id) {
@@ -1227,6 +1239,7 @@ impl OutboundPayments {
12271239
entry.insert(PendingOutboundPayment::AwaitingInvoice {
12281240
timer_ticks_without_response: 0,
12291241
retry_strategy,
1242+
max_total_routing_fee_msat,
12301243
});
12311244

12321245
Ok(())
@@ -1328,8 +1341,9 @@ impl OutboundPayments {
13281341
failed_paths_retry: if pending_amt_unsent != 0 {
13291342
if let Some(payment_params) = route.route_params.as_ref().map(|p| p.payment_params.clone()) {
13301343
Some(RouteParameters {
1331-
payment_params: payment_params,
1344+
payment_params,
13321345
final_value_msat: pending_amt_unsent,
1346+
max_total_routing_fee_msat: None,
13331347
})
13341348
} else { None }
13351349
} else { None },
@@ -1689,6 +1703,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
16891703
(8, pending_amt_msat, required),
16901704
(9, custom_tlvs, optional_vec),
16911705
(10, starting_block_height, required),
1706+
(11, remaining_max_total_routing_fee_msat, option),
16921707
(not_written, retry_strategy, (static_value, None)),
16931708
(not_written, attempts, (static_value, PaymentAttempts::new())),
16941709
},
@@ -1700,10 +1715,12 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
17001715
(5, AwaitingInvoice) => {
17011716
(0, timer_ticks_without_response, required),
17021717
(2, retry_strategy, required),
1718+
(4, max_total_routing_fee_msat, option),
17031719
},
17041720
(7, InvoiceReceived) => {
17051721
(0, payment_hash, required),
17061722
(2, retry_strategy, required),
1723+
(4, max_total_routing_fee_msat, option),
17071724
},
17081725
);
17091726

@@ -1926,7 +1943,9 @@ mod tests {
19261943
let payment_id = PaymentId([0; 32]);
19271944

19281945
assert!(!outbound_payments.has_pending_payments());
1929-
assert!(outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0)).is_ok());
1946+
assert!(
1947+
outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
1948+
);
19301949
assert!(outbound_payments.has_pending_payments());
19311950

19321951
for _ in 0..INVOICE_REQUEST_TIMEOUT_TICKS {
@@ -1944,10 +1963,15 @@ mod tests {
19441963
);
19451964
assert!(pending_events.lock().unwrap().is_empty());
19461965

1947-
assert!(outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0)).is_ok());
1966+
assert!(
1967+
outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
1968+
);
19481969
assert!(outbound_payments.has_pending_payments());
19491970

1950-
assert!(outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0)).is_err());
1971+
assert!(
1972+
outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None)
1973+
.is_err()
1974+
);
19511975
}
19521976

19531977
#[test]
@@ -1957,7 +1981,9 @@ mod tests {
19571981
let payment_id = PaymentId([0; 32]);
19581982

19591983
assert!(!outbound_payments.has_pending_payments());
1960-
assert!(outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0)).is_ok());
1984+
assert!(
1985+
outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
1986+
);
19611987
assert!(outbound_payments.has_pending_payments());
19621988

19631989
outbound_payments.abandon_payment(
@@ -1985,7 +2011,9 @@ mod tests {
19852011
let outbound_payments = OutboundPayments::new();
19862012
let payment_id = PaymentId([0; 32]);
19872013

1988-
assert!(outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0)).is_ok());
2014+
assert!(
2015+
outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
2016+
);
19892017
assert!(outbound_payments.has_pending_payments());
19902018

19912019
let created_at = now() - DEFAULT_RELATIVE_EXPIRY;
@@ -2031,7 +2059,9 @@ mod tests {
20312059
let outbound_payments = OutboundPayments::new();
20322060
let payment_id = PaymentId([0; 32]);
20332061

2034-
assert!(outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0)).is_ok());
2062+
assert!(
2063+
outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
2064+
);
20352065
assert!(outbound_payments.has_pending_payments());
20362066

20372067
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
@@ -2045,10 +2075,10 @@ mod tests {
20452075
.sign(recipient_sign).unwrap();
20462076

20472077
router.expect_find_route(
2048-
RouteParameters {
2049-
payment_params: PaymentParameters::from_bolt12_invoice(&invoice),
2050-
final_value_msat: invoice.amount_msats(),
2051-
},
2078+
RouteParameters::from_payment_params_and_value(
2079+
PaymentParameters::from_bolt12_invoice(&invoice),
2080+
invoice.amount_msats(),
2081+
),
20522082
Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError }),
20532083
);
20542084

@@ -2084,7 +2114,9 @@ mod tests {
20842114
let outbound_payments = OutboundPayments::new();
20852115
let payment_id = PaymentId([0; 32]);
20862116

2087-
assert!(outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0)).is_ok());
2117+
assert!(
2118+
outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
2119+
);
20882120
assert!(outbound_payments.has_pending_payments());
20892121

20902122
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
@@ -2097,10 +2129,10 @@ mod tests {
20972129
.build().unwrap()
20982130
.sign(recipient_sign).unwrap();
20992131

2100-
let route_params = RouteParameters {
2101-
payment_params: PaymentParameters::from_bolt12_invoice(&invoice),
2102-
final_value_msat: invoice.amount_msats(),
2103-
};
2132+
let route_params = RouteParameters::from_payment_params_and_value(
2133+
PaymentParameters::from_bolt12_invoice(&invoice),
2134+
invoice.amount_msats(),
2135+
);
21042136
router.expect_find_route(
21052137
route_params.clone(), Ok(Route { paths: vec![], route_params: Some(route_params) })
21062138
);
@@ -2150,6 +2182,7 @@ mod tests {
21502182
let route_params = RouteParameters {
21512183
payment_params: PaymentParameters::from_bolt12_invoice(&invoice),
21522184
final_value_msat: invoice.amount_msats(),
2185+
max_total_routing_fee_msat: Some(1234),
21532186
};
21542187
router.expect_find_route(
21552188
route_params.clone(),
@@ -2185,7 +2218,9 @@ mod tests {
21852218
assert!(!outbound_payments.has_pending_payments());
21862219
assert!(pending_events.lock().unwrap().is_empty());
21872220

2188-
assert!(outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0)).is_ok());
2221+
assert!(
2222+
outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), Some(1234)).is_ok()
2223+
);
21892224
assert!(outbound_payments.has_pending_payments());
21902225

21912226
assert_eq!(

lightning/src/ln/payment_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,7 @@ fn preflight_probes_yield_event_and_skip() {
13371337
let mut payment_params = PaymentParameters::from_node_id(nodes[4].node.get_our_node_id(), TEST_FINAL_CLTV)
13381338
.with_bolt11_features(invoice_features).unwrap();
13391339

1340-
let route_params = RouteParameters { payment_params, final_value_msat: 80_000_000 };
1340+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 80_000_000);
13411341
let res = nodes[0].node.send_preflight_probes(route_params, None).unwrap();
13421342

13431343
// We check that only one probe was sent, the other one was skipped due to limited liquidity.

0 commit comments

Comments
 (0)