Skip to content

Commit b4c7370

Browse files
committed
Support spontaneous payments in InvoicePayer
InvoicePayer handles retries not only when handling PaymentPathFailed events but also for some types of PaymentSendFailure on the initial send. Expand InvoicePayer's interface with a pay_pubkey function for spontaneous (keysend) payments. Add a send_spontaneous_payment function to the Payer trait to support this and implement it for ChannelManager.
1 parent c9ce344 commit b4c7370

File tree

2 files changed

+143
-18
lines changed

2 files changed

+143
-18
lines changed

lightning-invoice/src/payment.rs

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

10-
//! A module for paying Lightning invoices.
10+
//! A module for paying Lightning invoices and sending spontaneous payments.
1111
//!
12-
//! Defines an [`InvoicePayer`] utility for paying invoices, parameterized by [`Payer`] and
12+
//! Defines an [`InvoicePayer`] utility for sending payments, parameterized by [`Payer`] and
1313
//! [`Router`] traits. Implementations of [`Payer`] provide the payer's node id, channels, and means
1414
//! to send a payment over a [`Route`]. Implementations of [`Router`] find a [`Route`] between payer
15-
//! and payee using information provided by the payer and from the payee's [`Invoice`].
15+
//! and payee using information provided by the payer and from the payee's [`Invoice`], when
16+
//! applicable.
1617
//!
1718
//! [`InvoicePayer`] is capable of retrying failed payments. It accomplishes this by implementing
1819
//! [`EventHandler`] which decorates a user-provided handler. It will intercept any
@@ -27,7 +28,7 @@
2728
//! # extern crate lightning_invoice;
2829
//! # extern crate secp256k1;
2930
//! #
30-
//! # use lightning::ln::{PaymentHash, PaymentSecret};
31+
//! # use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
3132
//! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
3233
//! # use lightning::ln::msgs::LightningError;
3334
//! # use lightning::routing;
@@ -53,6 +54,9 @@
5354
//! # fn send_payment(
5455
//! # &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
5556
//! # ) -> Result<PaymentId, PaymentSendFailure> { unimplemented!() }
57+
//! # fn send_spontaneous_payment(
58+
//! # &self, route: &Route, payment_preimage: PaymentPreimage
59+
//! # ) -> Result<PaymentId, PaymentSendFailure> { unimplemented!() }
5660
//! # fn retry_payment(
5761
//! # &self, route: &Route, payment_id: PaymentId
5862
//! # ) -> Result<(), PaymentSendFailure> { unimplemented!() }
@@ -113,8 +117,9 @@
113117
use crate::Invoice;
114118

115119
use bitcoin_hashes::Hash;
120+
use bitcoin_hashes::sha256::Hash as Sha256;
116121

117-
use lightning::ln::{PaymentHash, PaymentSecret};
122+
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
118123
use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
119124
use lightning::ln::msgs::LightningError;
120125
use lightning::routing;
@@ -130,7 +135,7 @@ use std::ops::Deref;
130135
use std::sync::Mutex;
131136
use std::time::{Duration, SystemTime};
132137

133-
/// A utility for paying [`Invoice]`s.
138+
/// A utility for paying [`Invoice`]s and sending spontaneous payments.
134139
pub struct InvoicePayer<P: Deref, R, S: Deref, L: Deref, E>
135140
where
136141
P::Target: Payer,
@@ -162,6 +167,11 @@ pub trait Payer {
162167
&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
163168
) -> Result<PaymentId, PaymentSendFailure>;
164169

170+
/// Sends a spontaneous payment over the Lightning Network using the given [`Route`].
171+
fn send_spontaneous_payment(
172+
&self, route: &Route, payment_preimage: PaymentPreimage
173+
) -> Result<PaymentId, PaymentSendFailure>;
174+
165175
/// Retries a failed payment path for the [`PaymentId`] using the given [`Route`].
166176
fn retry_payment(&self, route: &Route, payment_id: PaymentId) -> Result<(), PaymentSendFailure>;
167177
}
@@ -273,13 +283,43 @@ where
273283
final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
274284
};
275285

276-
self.pay_internal(&params, payment_hash, &payment_secret)
286+
let send_payment = |route: &Route| {
287+
self.payer.send_payment(route, payment_hash, &payment_secret)
288+
};
289+
self.pay_internal(&params, payment_hash, send_payment)
290+
.map_err(|e| { self.payment_cache.lock().unwrap().remove(&payment_hash); e })
291+
}
292+
293+
/// Pays `pubkey` an amount using the hash of the given preimage, caching it for later use in
294+
/// case a retry is needed.
295+
///
296+
/// You should ensure that `payment_preimage` is unique and that its `payment_hash` has never
297+
/// been paid before. Because [`InvoicePayer`] is stateless no effort is made to do so for you.
298+
pub fn pay_pubkey(
299+
&self, pubkey: PublicKey, payment_preimage: PaymentPreimage, amount_msats: u64,
300+
final_cltv_expiry_delta: u32
301+
) -> Result<PaymentId, PaymentError> {
302+
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
303+
match self.payment_cache.lock().unwrap().entry(payment_hash) {
304+
hash_map::Entry::Occupied(_) => return Err(PaymentError::Invoice("payment pending")),
305+
hash_map::Entry::Vacant(entry) => entry.insert(0),
306+
};
307+
308+
let params = RouteParameters {
309+
payee: Payee::for_keysend(pubkey),
310+
final_value_msat: amount_msats,
311+
final_cltv_expiry_delta,
312+
};
313+
314+
let send_payment = |route: &Route| {
315+
self.payer.send_spontaneous_payment(route, payment_preimage)
316+
};
317+
self.pay_internal(&params, payment_hash, send_payment)
277318
.map_err(|e| { self.payment_cache.lock().unwrap().remove(&payment_hash); e })
278319
}
279320

280-
fn pay_internal(
281-
&self, params: &RouteParameters, payment_hash: PaymentHash,
282-
payment_secret: &Option<PaymentSecret>,
321+
fn pay_internal<F: FnOnce(&Route) -> Result<PaymentId, PaymentSendFailure> + Copy>(
322+
&self, params: &RouteParameters, payment_hash: PaymentHash, send_payment: F,
283323
) -> Result<PaymentId, PaymentError> {
284324
if has_expired(params) {
285325
log_trace!(self.logger, "Invoice expired prior to send for payment {}", log_bytes!(payment_hash.0));
@@ -295,7 +335,7 @@ where
295335
&self.scorer.lock(),
296336
).map_err(|e| PaymentError::Routing(e))?;
297337

298-
match self.payer.send_payment(&route, payment_hash, payment_secret) {
338+
match send_payment(&route) {
299339
Ok(payment_id) => Ok(payment_id),
300340
Err(e) => match e {
301341
PaymentSendFailure::ParameterError(_) => Err(e),
@@ -308,7 +348,7 @@ where
308348
} else {
309349
*retry_count += 1;
310350
std::mem::drop(payment_cache);
311-
Ok(self.pay_internal(params, payment_hash, payment_secret)?)
351+
Ok(self.pay_internal(params, payment_hash, send_payment)?)
312352
}
313353
},
314354
PaymentSendFailure::PartialFailure { failed_paths_retry, payment_id, .. } => {
@@ -514,6 +554,10 @@ mod tests {
514554
.unwrap()
515555
}
516556

557+
fn pubkey() -> PublicKey {
558+
PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap()
559+
}
560+
517561
#[test]
518562
fn pays_invoice_on_first_attempt() {
519563
let event_handled = core::cell::RefCell::new(false);
@@ -969,6 +1013,57 @@ mod tests {
9691013
}
9701014
}
9711015

1016+
#[test]
1017+
fn pays_pubkey_with_amount() {
1018+
let event_handled = core::cell::RefCell::new(false);
1019+
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
1020+
1021+
let pubkey = pubkey();
1022+
let payment_preimage = PaymentPreimage([1; 32]);
1023+
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
1024+
let final_value_msat = 100;
1025+
let final_cltv_expiry_delta = 42;
1026+
1027+
let payer = TestPayer::new()
1028+
.expect_send(Amount::Spontaneous(final_value_msat))
1029+
.expect_send(Amount::ForInvoiceOrRetry(final_value_msat));
1030+
let router = TestRouter {};
1031+
let scorer = RefCell::new(TestScorer::new());
1032+
let logger = TestLogger::new();
1033+
let invoice_payer =
1034+
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, RetryAttempts(2));
1035+
1036+
let payment_id = Some(invoice_payer.pay_pubkey(
1037+
pubkey, payment_preimage, final_value_msat, final_cltv_expiry_delta
1038+
).unwrap());
1039+
assert_eq!(*payer.attempts.borrow(), 1);
1040+
1041+
let retry = RouteParameters {
1042+
payee: Payee::for_keysend(pubkey),
1043+
final_value_msat,
1044+
final_cltv_expiry_delta,
1045+
};
1046+
let event = Event::PaymentPathFailed {
1047+
payment_id,
1048+
payment_hash,
1049+
network_update: None,
1050+
rejected_by_dest: false,
1051+
all_paths_failed: false,
1052+
path: vec![],
1053+
short_channel_id: None,
1054+
retry: Some(retry),
1055+
};
1056+
invoice_payer.handle_event(&event);
1057+
assert_eq!(*event_handled.borrow(), false);
1058+
assert_eq!(*payer.attempts.borrow(), 2);
1059+
1060+
invoice_payer.handle_event(&Event::PaymentSent {
1061+
payment_id, payment_preimage, payment_hash, fee_paid_msat: None
1062+
});
1063+
assert_eq!(*event_handled.borrow(), true);
1064+
assert_eq!(*payer.attempts.borrow(), 2);
1065+
}
1066+
9721067
#[test]
9731068
fn scores_failed_channel() {
9741069
let event_handled = core::cell::RefCell::new(false);
@@ -1116,11 +1211,17 @@ mod tests {
11161211
}
11171212

11181213
struct TestPayer {
1119-
expectations: core::cell::RefCell<VecDeque<u64>>,
1214+
expectations: core::cell::RefCell<VecDeque<Amount>>,
11201215
attempts: core::cell::RefCell<usize>,
11211216
failing_on_attempt: Option<usize>,
11221217
}
11231218

1219+
#[derive(Clone, Debug, PartialEq, Eq)]
1220+
enum Amount {
1221+
ForInvoiceOrRetry(u64),
1222+
Spontaneous(u64),
1223+
}
1224+
11241225
impl TestPayer {
11251226
fn new() -> Self {
11261227
Self {
@@ -1131,6 +1232,11 @@ mod tests {
11311232
}
11321233

11331234
fn expect_value_msat(self, value_msat: u64) -> Self {
1235+
self.expectations.borrow_mut().push_back(Amount::ForInvoiceOrRetry(value_msat));
1236+
self
1237+
}
1238+
1239+
fn expect_send(self, value_msat: Amount) -> Self {
11341240
self.expectations.borrow_mut().push_back(value_msat);
11351241
self
11361242
}
@@ -1153,10 +1259,9 @@ mod tests {
11531259
}
11541260
}
11551261

1156-
fn check_value_msats(&self, route: &Route) {
1262+
fn check_value_msats(&self, actual_value_msats: Amount) {
11571263
let expected_value_msats = self.expectations.borrow_mut().pop_front();
11581264
if let Some(expected_value_msats) = expected_value_msats {
1159-
let actual_value_msats = route.get_total_amount();
11601265
assert_eq!(actual_value_msats, expected_value_msats);
11611266
}
11621267
}
@@ -1191,7 +1296,20 @@ mod tests {
11911296
_payment_secret: &Option<PaymentSecret>
11921297
) -> Result<PaymentId, PaymentSendFailure> {
11931298
if self.check_attempts() {
1194-
self.check_value_msats(route);
1299+
self.check_value_msats(Amount::ForInvoiceOrRetry(route.get_total_amount()));
1300+
Ok(PaymentId([1; 32]))
1301+
} else {
1302+
Err(PaymentSendFailure::ParameterError(APIError::MonitorUpdateFailed))
1303+
}
1304+
}
1305+
1306+
fn send_spontaneous_payment(
1307+
&self,
1308+
route: &Route,
1309+
_payment_preimage: PaymentPreimage,
1310+
) -> Result<PaymentId, PaymentSendFailure> {
1311+
if self.check_attempts() {
1312+
self.check_value_msats(Amount::Spontaneous(route.get_total_amount()));
11951313
Ok(PaymentId([1; 32]))
11961314
} else {
11971315
Err(PaymentSendFailure::ParameterError(APIError::MonitorUpdateFailed))
@@ -1202,7 +1320,7 @@ mod tests {
12021320
&self, route: &Route, _payment_id: PaymentId
12031321
) -> Result<(), PaymentSendFailure> {
12041322
if self.check_attempts() {
1205-
self.check_value_msats(route);
1323+
self.check_value_msats(Amount::ForInvoiceOrRetry(route.get_total_amount()));
12061324
Ok(())
12071325
} else {
12081326
Err(PaymentSendFailure::ParameterError(APIError::MonitorUpdateFailed))

lightning-invoice/src/utils.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use bitcoin_hashes::Hash;
88
use lightning::chain;
99
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
1010
use lightning::chain::keysinterface::{Sign, KeysInterface};
11-
use lightning::ln::{PaymentHash, PaymentSecret};
11+
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
1212
use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY};
1313
use lightning::ln::msgs::LightningError;
1414
use lightning::routing;
@@ -141,6 +141,13 @@ where
141141
self.send_payment(route, payment_hash, payment_secret)
142142
}
143143

144+
fn send_spontaneous_payment(
145+
&self, route: &Route, payment_preimage: PaymentPreimage,
146+
) -> Result<PaymentId, PaymentSendFailure> {
147+
self.send_spontaneous_payment(route, Some(payment_preimage))
148+
.map(|(_, payment_id)| payment_id)
149+
}
150+
144151
fn retry_payment(
145152
&self, route: &Route, payment_id: PaymentId
146153
) -> Result<(), PaymentSendFailure> {

0 commit comments

Comments
 (0)