Skip to content

Commit 36af1f0

Browse files
authored
Merge pull request #2534 from tnull/2023-08-upstream-preflight-probing
Upstream and fix preflight probing
2 parents f97d520 + f75ac9a commit 36af1f0

File tree

10 files changed

+413
-80
lines changed

10 files changed

+413
-80
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p
371371
channel_features: dest.channel_features(),
372372
fee_msat: amt,
373373
cltv_expiry_delta: 200,
374+
maybe_announced_channel: true,
374375
}], blinded_tail: None }],
375376
route_params: None,
376377
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {
@@ -405,13 +406,15 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des
405406
channel_features: middle.channel_features(),
406407
fee_msat: first_hop_fee,
407408
cltv_expiry_delta: 100,
409+
maybe_announced_channel: true,
408410
}, RouteHop {
409411
pubkey: dest.get_our_node_id(),
410412
node_features: dest.node_features(),
411413
short_channel_id: dest_chan_id,
412414
channel_features: dest.channel_features(),
413415
fee_msat: amt,
414416
cltv_expiry_delta: 200,
417+
maybe_announced_channel: true,
415418
}], blinded_tail: None }],
416419
route_params: None,
417420
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {

lightning-background-processor/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,6 +1683,7 @@ mod tests {
16831683
channel_features: ChannelFeatures::empty(),
16841684
fee_msat: 0,
16851685
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA as u32,
1686+
maybe_announced_channel: true,
16861687
}], blinded_tail: None };
16871688

16881689
$nodes[0].scorer.lock().unwrap().expect(TestResult::PaymentFailure { path: path.clone(), short_channel_id: scored_scid });

lightning-invoice/src/payment.rs

Lines changed: 83 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
// You may not use this file except in accordance with one or both of these
88
// licenses.
99

10-
//! Convenient utilities for paying Lightning invoices and sending spontaneous payments.
10+
//! Convenient utilities for paying Lightning invoices.
1111
12-
use crate::Bolt11Invoice;
12+
use crate::{Bolt11Invoice, Vec};
1313

1414
use bitcoin_hashes::Hash;
1515

1616
use lightning::chain;
1717
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
1818
use lightning::sign::{NodeSigner, SignerProvider, EntropySource};
1919
use lightning::ln::PaymentHash;
20-
use lightning::ln::channelmanager::{ChannelManager, PaymentId, Retry, RetryableSendFailure, RecipientOnionFields};
20+
use lightning::ln::channelmanager::{AChannelManager, ChannelManager, PaymentId, Retry, RetryableSendFailure, RecipientOnionFields, ProbeSendFailure};
2121
use lightning::routing::router::{PaymentParameters, RouteParameters, Router};
2222
use lightning::util::logger::Logger;
2323

@@ -32,22 +32,12 @@ use core::time::Duration;
3232
/// with the same [`PaymentHash`] is never sent.
3333
///
3434
/// If you wish to use a different payment idempotency token, see [`pay_invoice_with_id`].
35-
pub fn pay_invoice<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>(
36-
invoice: &Bolt11Invoice, retry_strategy: Retry,
37-
channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>
35+
pub fn pay_invoice<C: AChannelManager>(
36+
invoice: &Bolt11Invoice, retry_strategy: Retry, channelmanager: &C
3837
) -> Result<PaymentId, PaymentError>
39-
where
40-
M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
41-
T::Target: BroadcasterInterface,
42-
ES::Target: EntropySource,
43-
NS::Target: NodeSigner,
44-
SP::Target: SignerProvider,
45-
F::Target: FeeEstimator,
46-
R::Target: Router,
47-
L::Target: Logger,
4838
{
4939
let payment_id = PaymentId(invoice.payment_hash().into_inner());
50-
pay_invoice_with_id(invoice, payment_id, retry_strategy, channelmanager)
40+
pay_invoice_with_id(invoice, payment_id, retry_strategy, channelmanager.get_cm())
5141
.map(|()| payment_id)
5242
}
5343

@@ -61,22 +51,12 @@ where
6151
/// [`PaymentHash`] has never been paid before.
6252
///
6353
/// See [`pay_invoice`] for a variant which uses the [`PaymentHash`] for the idempotency token.
64-
pub fn pay_invoice_with_id<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>(
65-
invoice: &Bolt11Invoice, payment_id: PaymentId, retry_strategy: Retry,
66-
channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>
54+
pub fn pay_invoice_with_id<C: AChannelManager>(
55+
invoice: &Bolt11Invoice, payment_id: PaymentId, retry_strategy: Retry, channelmanager: &C
6756
) -> Result<(), PaymentError>
68-
where
69-
M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
70-
T::Target: BroadcasterInterface,
71-
ES::Target: EntropySource,
72-
NS::Target: NodeSigner,
73-
SP::Target: SignerProvider,
74-
F::Target: FeeEstimator,
75-
R::Target: Router,
76-
L::Target: Logger,
7757
{
7858
let amt_msat = invoice.amount_milli_satoshis().ok_or(PaymentError::Invoice("amount missing"))?;
79-
pay_invoice_using_amount(invoice, amt_msat, payment_id, retry_strategy, channelmanager)
59+
pay_invoice_using_amount(invoice, amt_msat, payment_id, retry_strategy, channelmanager.get_cm())
8060
}
8161

8262
/// Pays the given zero-value [`Bolt11Invoice`] using the given amount, retrying if needed based on
@@ -88,19 +68,9 @@ where
8868
///
8969
/// If you wish to use a different payment idempotency token, see
9070
/// [`pay_zero_value_invoice_with_id`].
91-
pub fn pay_zero_value_invoice<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>(
92-
invoice: &Bolt11Invoice, amount_msats: u64, retry_strategy: Retry,
93-
channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>
71+
pub fn pay_zero_value_invoice<C: AChannelManager>(
72+
invoice: &Bolt11Invoice, amount_msats: u64, retry_strategy: Retry, channelmanager: &C
9473
) -> Result<PaymentId, PaymentError>
95-
where
96-
M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
97-
T::Target: BroadcasterInterface,
98-
ES::Target: EntropySource,
99-
NS::Target: NodeSigner,
100-
SP::Target: SignerProvider,
101-
F::Target: FeeEstimator,
102-
R::Target: Router,
103-
L::Target: Logger,
10474
{
10575
let payment_id = PaymentId(invoice.payment_hash().into_inner());
10676
pay_zero_value_invoice_with_id(invoice, amount_msats, payment_id, retry_strategy,
@@ -119,25 +89,16 @@ where
11989
///
12090
/// See [`pay_zero_value_invoice`] for a variant which uses the [`PaymentHash`] for the
12191
/// idempotency token.
122-
pub fn pay_zero_value_invoice_with_id<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>(
92+
pub fn pay_zero_value_invoice_with_id<C: AChannelManager>(
12393
invoice: &Bolt11Invoice, amount_msats: u64, payment_id: PaymentId, retry_strategy: Retry,
124-
channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>
94+
channelmanager: &C
12595
) -> Result<(), PaymentError>
126-
where
127-
M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
128-
T::Target: BroadcasterInterface,
129-
ES::Target: EntropySource,
130-
NS::Target: NodeSigner,
131-
SP::Target: SignerProvider,
132-
F::Target: FeeEstimator,
133-
R::Target: Router,
134-
L::Target: Logger,
13596
{
13697
if invoice.amount_milli_satoshis().is_some() {
13798
Err(PaymentError::Invoice("amount unexpected"))
13899
} else {
139100
pay_invoice_using_amount(invoice, amount_msats, payment_id, retry_strategy,
140-
channelmanager)
101+
channelmanager.get_cm())
141102
}
142103
}
143104

@@ -163,6 +124,66 @@ fn pay_invoice_using_amount<P: Deref>(
163124
payer.send_payment(payment_hash, recipient_onion, payment_id, route_params, retry_strategy)
164125
}
165126

127+
/// Sends payment probes over all paths of a route that would be used to pay the given invoice.
128+
///
129+
/// See [`ChannelManager::send_preflight_probes`] for more information.
130+
pub fn preflight_probe_invoice<C: AChannelManager>(
131+
invoice: &Bolt11Invoice, channelmanager: &C, liquidity_limit_multiplier: Option<u64>,
132+
) -> Result<Vec<(PaymentHash, PaymentId)>, ProbingError>
133+
{
134+
let amount_msat = if let Some(invoice_amount_msat) = invoice.amount_milli_satoshis() {
135+
invoice_amount_msat
136+
} else {
137+
return Err(ProbingError::Invoice("Failed to send probe as no amount was given in the invoice."));
138+
};
139+
140+
let mut payment_params = PaymentParameters::from_node_id(
141+
invoice.recover_payee_pub_key(),
142+
invoice.min_final_cltv_expiry_delta() as u32,
143+
)
144+
.with_expiry_time(expiry_time_from_unix_epoch(invoice).as_secs())
145+
.with_route_hints(invoice.route_hints())
146+
.unwrap();
147+
148+
if let Some(features) = invoice.features() {
149+
payment_params = payment_params.with_bolt11_features(features.clone()).unwrap();
150+
}
151+
let route_params = RouteParameters { payment_params, final_value_msat: amount_msat };
152+
153+
channelmanager.get_cm().send_preflight_probes(route_params, liquidity_limit_multiplier)
154+
.map_err(ProbingError::Sending)
155+
}
156+
157+
/// Sends payment probes over all paths of a route that would be used to pay the given zero-value
158+
/// invoice using the given amount.
159+
///
160+
/// See [`ChannelManager::send_preflight_probes`] for more information.
161+
pub fn preflight_probe_zero_value_invoice<C: AChannelManager>(
162+
invoice: &Bolt11Invoice, amount_msat: u64, channelmanager: &C,
163+
liquidity_limit_multiplier: Option<u64>,
164+
) -> Result<Vec<(PaymentHash, PaymentId)>, ProbingError>
165+
{
166+
if invoice.amount_milli_satoshis().is_some() {
167+
return Err(ProbingError::Invoice("amount unexpected"));
168+
}
169+
170+
let mut payment_params = PaymentParameters::from_node_id(
171+
invoice.recover_payee_pub_key(),
172+
invoice.min_final_cltv_expiry_delta() as u32,
173+
)
174+
.with_expiry_time(expiry_time_from_unix_epoch(invoice).as_secs())
175+
.with_route_hints(invoice.route_hints())
176+
.unwrap();
177+
178+
if let Some(features) = invoice.features() {
179+
payment_params = payment_params.with_bolt11_features(features.clone()).unwrap();
180+
}
181+
let route_params = RouteParameters { payment_params, final_value_msat: amount_msat };
182+
183+
channelmanager.get_cm().send_preflight_probes(route_params, liquidity_limit_multiplier)
184+
.map_err(ProbingError::Sending)
185+
}
186+
166187
fn expiry_time_from_unix_epoch(invoice: &Bolt11Invoice) -> Duration {
167188
invoice.signed_invoice.raw_invoice.data.timestamp.0 + invoice.expiry_time()
168189
}
@@ -176,6 +197,15 @@ pub enum PaymentError {
176197
Sending(RetryableSendFailure),
177198
}
178199

200+
/// An error that may occur when sending a payment probe.
201+
#[derive(Clone, Debug, PartialEq, Eq)]
202+
pub enum ProbingError {
203+
/// An error resulting from the provided [`Bolt11Invoice`].
204+
Invoice(&'static str),
205+
/// An error occurring when sending a payment probe.
206+
Sending(ProbeSendFailure),
207+
}
208+
179209
/// A trait defining behavior of a [`Bolt11Invoice`] payer.
180210
///
181211
/// Useful for unit testing internal methods.

0 commit comments

Comments
 (0)