Skip to content

Commit 5d6e41a

Browse files
committed
Add send_payment_probe method
We add the capability to send a payment probe given an invoice.
1 parent 0acfddf commit 5d6e41a

File tree

4 files changed

+81
-3
lines changed

4 files changed

+81
-3
lines changed

bindings/ldk_node.udl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ interface LDKNode {
7272
[Throws=NodeError]
7373
PaymentHash send_spontaneous_payment(u64 amount_msat, PublicKey node_id);
7474
[Throws=NodeError]
75+
void send_payment_probe([ByRef]Invoice invoice);
76+
[Throws=NodeError]
7577
Invoice receive_payment(u64 amount_msat, [ByRef]string description, u32 expiry_secs);
7678
[Throws=NodeError]
7779
Invoice receive_variable_amount_payment([ByRef]string description, u32 expiry_secs);
@@ -94,6 +96,7 @@ enum NodeError {
9496
"ConnectionFailed",
9597
"InvoiceCreationFailed",
9698
"PaymentSendingFailed",
99+
"ProbeSendingFailed",
97100
"ChannelCreationFailed",
98101
"ChannelClosingFailed",
99102
"ChannelConfigUpdateFailed",

src/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
730730
gossip_source,
731731
kv_store,
732732
logger,
733+
router,
733734
scorer,
734735
peer_store,
735736
payment_store,

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub enum Error {
1515
InvoiceCreationFailed,
1616
/// Sending a payment has failed.
1717
PaymentSendingFailed,
18+
/// Sending a payment probe has failed.
19+
ProbeSendingFailed,
1820
/// A channel could not be opened.
1921
ChannelCreationFailed,
2022
/// A channel could not be closed.
@@ -72,6 +74,7 @@ impl fmt::Display for Error {
7274
Self::ConnectionFailed => write!(f, "Network connection closed."),
7375
Self::InvoiceCreationFailed => write!(f, "Failed to create invoice."),
7476
Self::PaymentSendingFailed => write!(f, "Failed to send the given payment."),
77+
Self::ProbeSendingFailed => write!(f, "Failed to send the given payment probe."),
7578
Self::ChannelCreationFailed => write!(f, "Failed to create channel."),
7679
Self::ChannelClosingFailed => write!(f, "Failed to close channel."),
7780
Self::ChannelConfigUpdateFailed => write!(f, "Failed to update channel config."),

src/lib.rs

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ use io::KVStore;
118118
use payment_store::PaymentStore;
119119
pub use payment_store::{PaymentDetails, PaymentDirection, PaymentStatus};
120120
use peer_store::{PeerInfo, PeerStore};
121-
use types::{ChainMonitor, ChannelManager, KeysManager, NetworkGraph, PeerManager, Scorer};
121+
use types::{ChainMonitor, ChannelManager, KeysManager, NetworkGraph, PeerManager, Router, Scorer};
122122
pub use types::{ChannelDetails, ChannelId, PeerDetails, UserChannelId};
123123
use wallet::Wallet;
124124

125-
use logger::{log_error, log_info, log_trace, FilesystemLogger, Logger};
125+
use logger::{log_debug, log_error, log_info, log_trace, FilesystemLogger, Logger};
126126

127127
use lightning::chain::keysinterface::EntropySource;
128128
use lightning::chain::Confirm;
@@ -136,7 +136,7 @@ use lightning_background_processor::process_events_async;
136136

137137
use lightning_transaction_sync::EsploraSyncClient;
138138

139-
use lightning::routing::router::{PaymentParameters, RouteParameters};
139+
use lightning::routing::router::{PaymentParameters, RouteParameters, Router as LdkRouter};
140140
use lightning_invoice::{payment, Currency, Invoice};
141141

142142
use bitcoin::hashes::sha256::Hash as Sha256;
@@ -284,6 +284,7 @@ pub struct Node<K: KVStore + Sync + Send + 'static> {
284284
gossip_source: Arc<GossipSource>,
285285
kv_store: Arc<K>,
286286
logger: Arc<FilesystemLogger>,
287+
router: Arc<Router>,
287288
scorer: Arc<Mutex<Scorer>>,
288289
peer_store: Arc<PeerStore<K, Arc<FilesystemLogger>>>,
289290
payment_store: Arc<PaymentStore<K, Arc<FilesystemLogger>>>,
@@ -1287,6 +1288,76 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
12871288
}
12881289
}
12891290

1291+
/// Sends payment probes over all paths of a route that would be used to pay the given invoice.
1292+
///
1293+
/// This may be used to send "pre-flight" probes, i.e., to train our scorer before conducting
1294+
/// the actual payment. Note this is only useful if there likely is sufficient time for the
1295+
/// probe to settle before sending out the actual payment, e.g., when waiting for user
1296+
/// confirmation in a wallet UI.
1297+
pub fn send_payment_probe(&self, invoice: &Invoice) -> Result<(), Error> {
1298+
let rt_lock = self.runtime.read().unwrap();
1299+
if rt_lock.is_none() {
1300+
return Err(Error::NotRunning);
1301+
}
1302+
1303+
let amount_msat = if let Some(invoice_amount_msat) = invoice.amount_milli_satoshis() {
1304+
invoice_amount_msat
1305+
} else {
1306+
log_error!(self.logger, "Failed to send probe as no amount was given in the invoice.");
1307+
return Err(Error::InvalidAmount);
1308+
};
1309+
1310+
let expiry_time = invoice.duration_since_epoch().saturating_add(invoice.expiry_time());
1311+
let mut payment_params = PaymentParameters::from_node_id(
1312+
invoice.recover_payee_pub_key(),
1313+
invoice.min_final_cltv_expiry_delta() as u32,
1314+
)
1315+
.with_expiry_time(expiry_time.as_secs())
1316+
.with_route_hints(invoice.route_hints());
1317+
if let Some(features) = invoice.features() {
1318+
payment_params = payment_params.with_features(features.clone());
1319+
}
1320+
let route_params = RouteParameters { payment_params, final_value_msat: amount_msat };
1321+
1322+
self.send_payment_probe_internal(route_params)
1323+
}
1324+
1325+
fn send_payment_probe_internal(&self, route_params: RouteParameters) -> Result<(), Error> {
1326+
let payer = self.channel_manager.get_our_node_id();
1327+
let first_hops = self.channel_manager.list_usable_channels();
1328+
let inflight_htlcs = self.channel_manager.compute_inflight_htlcs();
1329+
1330+
let route = self
1331+
.router
1332+
.find_route(
1333+
&payer,
1334+
&route_params,
1335+
Some(&first_hops.iter().collect::<Vec<_>>()),
1336+
&inflight_htlcs,
1337+
)
1338+
.map_err(|e| {
1339+
log_error!(self.logger, "Failed to find path for payment probe: {:?}", e);
1340+
Error::ProbeSendingFailed
1341+
})?;
1342+
1343+
for path in route.paths {
1344+
if path.hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len()) < 2 {
1345+
log_debug!(
1346+
self.logger,
1347+
"Skipped sending payment probe over path with less than two hops."
1348+
);
1349+
continue;
1350+
}
1351+
1352+
self.channel_manager.send_probe(path).map_err(|e| {
1353+
log_error!(self.logger, "Failed to send payment probe: {:?}", e);
1354+
Error::ProbeSendingFailed
1355+
})?;
1356+
}
1357+
1358+
Ok(())
1359+
}
1360+
12901361
/// Returns a payable invoice that can be used to request and receive a payment of the amount
12911362
/// given.
12921363
pub fn receive_payment(

0 commit comments

Comments
 (0)