Skip to content

Commit 25ea96a

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 8a8c339 commit 25ea96a

File tree

2 files changed

+146
-15
lines changed

2 files changed

+146
-15
lines changed

lightning-invoice/src/payment.rs

Lines changed: 138 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
//! # extern crate lightning_invoice;
2828
//! # extern crate secp256k1;
2929
//! #
30-
//! # use lightning::ln::{PaymentHash, PaymentSecret};
30+
//! # use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
3131
//! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
3232
//! # use lightning::ln::msgs::LightningError;
3333
//! # use lightning::routing;
@@ -53,6 +53,9 @@
5353
//! # fn send_payment(
5454
//! # &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
5555
//! # ) -> Result<PaymentId, PaymentSendFailure> { unimplemented!() }
56+
//! # fn send_spontaneous_payment(
57+
//! # &self, route: &Route, payment_preimage: PaymentPreimage
58+
//! # ) -> Result<PaymentId, PaymentSendFailure> { unimplemented!() }
5659
//! # fn retry_payment(
5760
//! # &self, route: &Route, payment_id: PaymentId
5861
//! # ) -> Result<(), PaymentSendFailure> { unimplemented!() }
@@ -110,11 +113,12 @@
110113
//! as updates to the network graph or changes to channel scores should be applied prior to
111114
//! retries, typically by way of composing [`EventHandler`]s accordingly.
112115
113-
use crate::Invoice;
116+
use crate::{DEFAULT_MIN_FINAL_CLTV_EXPIRY, Invoice};
114117

115118
use bitcoin_hashes::Hash;
119+
use bitcoin_hashes::sha256::Hash as Sha256;
116120

117-
use lightning::ln::{PaymentHash, PaymentSecret};
121+
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
118122
use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
119123
use lightning::ln::msgs::LightningError;
120124
use lightning::routing;
@@ -161,6 +165,11 @@ pub trait Payer {
161165
&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
162166
) -> Result<PaymentId, PaymentSendFailure>;
163167

168+
/// Sends a spontaneous payment over the Lightning Network using the given [`Route`].
169+
fn send_spontaneous_payment(
170+
&self, route: &Route, payment_preimage: PaymentPreimage
171+
) -> Result<PaymentId, PaymentSendFailure>;
172+
164173
/// Retries a failed payment path for the [`PaymentId`] using the given [`Route`].
165174
fn retry_payment(&self, route: &Route, payment_id: PaymentId) -> Result<(), PaymentSendFailure>;
166175
}
@@ -282,15 +291,54 @@ where
282291
final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
283292
};
284293

285-
self.pay_internal(&mut entry, &params, payment_hash, &payment_secret)
294+
let send_payment = |route: &Route| {
295+
self.payer.send_payment(route, payment_hash, &payment_secret)
296+
};
297+
self.pay_internal(&mut entry, &params, send_payment)
298+
.map_err(|e| { entry.remove(); e })
299+
}
300+
301+
/// Pays `pubkey` an amount using the hash of the given preimage, caching it for later use in
302+
/// case a retry is needed.
303+
///
304+
/// You should ensure that `payment_preimage` is unique and that its `payment_hash` has never
305+
/// been paid before. Because [`InvoicePayer`] is stateless no effort is made to do so for you.
306+
pub fn pay_pubkey(
307+
&self, pubkey: PublicKey, payment_preimage: PaymentPreimage, amount_msats: u64
308+
) -> Result<PaymentId, PaymentError> {
309+
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
310+
let mut payment_cache = self.payment_cache.lock().unwrap();
311+
let mut entry = match payment_cache.entry(payment_hash) {
312+
hash_map::Entry::Occupied(_) => return Err(PaymentError::Invoice("payment pending")),
313+
hash_map::Entry::Vacant(entry) => {
314+
// TODO: Replace with hash_map::Entry::insert once no longer experimental
315+
entry.insert(0);
316+
match payment_cache.entry(payment_hash) {
317+
hash_map::Entry::Occupied(entry) => entry,
318+
hash_map::Entry::Vacant(_) => unreachable!(),
319+
}
320+
},
321+
};
322+
323+
let payee = Payee::for_keysend(pubkey);
324+
let params = RouteParameters {
325+
payee,
326+
final_value_msat: amount_msats,
327+
final_cltv_expiry_delta: DEFAULT_MIN_FINAL_CLTV_EXPIRY as u32,
328+
};
329+
330+
let send_payment = |route: &Route| {
331+
self.payer.send_spontaneous_payment(route, payment_preimage)
332+
};
333+
self.pay_internal(&mut entry, &params, send_payment)
286334
.map_err(|e| { entry.remove(); e })
287335
}
288336

289-
fn pay_internal(
290-
&self, entry: &mut PaymentEntry, params: &RouteParameters, payment_hash: PaymentHash,
291-
payment_secret: &Option<PaymentSecret>,
337+
fn pay_internal<F: FnOnce(&Route) -> Result<PaymentId, PaymentSendFailure> + Copy>(
338+
&self, entry: &mut PaymentEntry, params: &RouteParameters, send_payment: F,
292339
) -> Result<PaymentId, PaymentError> {
293340
if has_expired(params) {
341+
let payment_hash = entry.key();
294342
log_trace!(self.logger, "Invoice expired prior to send for payment {}", log_bytes!(payment_hash.0));
295343
return Err(PaymentError::Invoice("Invoice expired prior to send"));
296344
}
@@ -304,7 +352,7 @@ where
304352
&self.scorer.lock(),
305353
).map_err(|e| PaymentError::Routing(e))?;
306354

307-
match self.payer.send_payment(&route, payment_hash, payment_secret) {
355+
match send_payment(&route) {
308356
Ok(payment_id) => Ok(payment_id),
309357
Err(e) => match e {
310358
PaymentSendFailure::ParameterError(_) => Err(e),
@@ -315,7 +363,7 @@ where
315363
Err(e)
316364
} else {
317365
*retry_count += 1;
318-
Ok(self.pay_internal(entry, params, payment_hash, payment_secret)?)
366+
Ok(self.pay_internal(entry, params, send_payment)?)
319367
}
320368
},
321369
PaymentSendFailure::PartialFailure { failed_paths_retry, payment_id, .. } => {
@@ -530,6 +578,10 @@ mod tests {
530578
.unwrap()
531579
}
532580

581+
fn pubkey() -> PublicKey {
582+
PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap()
583+
}
584+
533585
#[test]
534586
fn pays_invoice_on_first_attempt() {
535587
let event_handled = core::cell::RefCell::new(false);
@@ -985,6 +1037,55 @@ mod tests {
9851037
}
9861038
}
9871039

1040+
#[test]
1041+
fn pays_pubkey_with_amount() {
1042+
let event_handled = core::cell::RefCell::new(false);
1043+
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
1044+
1045+
let pubkey = pubkey();
1046+
let payment_preimage = PaymentPreimage([1; 32]);
1047+
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
1048+
let final_value_msat = 100;
1049+
1050+
let payer = TestPayer::new()
1051+
.expect_send(Amount::Spontaneous(final_value_msat))
1052+
.expect_send(Amount::ForInvoiceOrRetry(final_value_msat));
1053+
let router = TestRouter {};
1054+
let scorer = RefCell::new(TestScorer::new());
1055+
let logger = TestLogger::new();
1056+
let invoice_payer =
1057+
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, RetryAttempts(2));
1058+
1059+
let payment_id =
1060+
Some(invoice_payer.pay_pubkey(pubkey, payment_preimage, final_value_msat).unwrap());
1061+
assert_eq!(*payer.attempts.borrow(), 1);
1062+
1063+
let retry = RouteParameters {
1064+
payee: Payee::for_keysend(pubkey),
1065+
final_value_msat,
1066+
final_cltv_expiry_delta: DEFAULT_MIN_FINAL_CLTV_EXPIRY as u32,
1067+
};
1068+
let event = Event::PaymentPathFailed {
1069+
payment_id,
1070+
payment_hash,
1071+
network_update: None,
1072+
rejected_by_dest: false,
1073+
all_paths_failed: false,
1074+
path: vec![],
1075+
short_channel_id: None,
1076+
retry: Some(retry),
1077+
};
1078+
invoice_payer.handle_event(&event);
1079+
assert_eq!(*event_handled.borrow(), false);
1080+
assert_eq!(*payer.attempts.borrow(), 2);
1081+
1082+
invoice_payer.handle_event(&Event::PaymentSent {
1083+
payment_id, payment_preimage, payment_hash, fee_paid_msat: None
1084+
});
1085+
assert_eq!(*event_handled.borrow(), true);
1086+
assert_eq!(*payer.attempts.borrow(), 2);
1087+
}
1088+
9881089
#[test]
9891090
fn scores_failed_channel() {
9901091
let event_handled = core::cell::RefCell::new(false);
@@ -1132,11 +1233,17 @@ mod tests {
11321233
}
11331234

11341235
struct TestPayer {
1135-
expectations: core::cell::RefCell<VecDeque<u64>>,
1236+
expectations: core::cell::RefCell<VecDeque<Amount>>,
11361237
attempts: core::cell::RefCell<usize>,
11371238
failing_on_attempt: Option<usize>,
11381239
}
11391240

1241+
#[derive(Clone, Debug, PartialEq, Eq)]
1242+
enum Amount {
1243+
ForInvoiceOrRetry(u64),
1244+
Spontaneous(u64),
1245+
}
1246+
11401247
impl TestPayer {
11411248
fn new() -> Self {
11421249
Self {
@@ -1147,6 +1254,11 @@ mod tests {
11471254
}
11481255

11491256
fn expect_value_msat(self, value_msat: u64) -> Self {
1257+
self.expectations.borrow_mut().push_back(Amount::ForInvoiceOrRetry(value_msat));
1258+
self
1259+
}
1260+
1261+
fn expect_send(self, value_msat: Amount) -> Self {
11501262
self.expectations.borrow_mut().push_back(value_msat);
11511263
self
11521264
}
@@ -1169,10 +1281,9 @@ mod tests {
11691281
}
11701282
}
11711283

1172-
fn check_value_msats(&self, route: &Route) {
1284+
fn check_value_msats(&self, actual_value_msats: Amount) {
11731285
let expected_value_msats = self.expectations.borrow_mut().pop_front();
11741286
if let Some(expected_value_msats) = expected_value_msats {
1175-
let actual_value_msats = route.get_total_amount();
11761287
assert_eq!(actual_value_msats, expected_value_msats);
11771288
}
11781289
}
@@ -1207,7 +1318,20 @@ mod tests {
12071318
_payment_secret: &Option<PaymentSecret>
12081319
) -> Result<PaymentId, PaymentSendFailure> {
12091320
if self.check_attempts() {
1210-
self.check_value_msats(route);
1321+
self.check_value_msats(Amount::ForInvoiceOrRetry(route.get_total_amount()));
1322+
Ok(PaymentId([1; 32]))
1323+
} else {
1324+
Err(PaymentSendFailure::ParameterError(APIError::MonitorUpdateFailed))
1325+
}
1326+
}
1327+
1328+
fn send_spontaneous_payment(
1329+
&self,
1330+
route: &Route,
1331+
_payment_preimage: PaymentPreimage,
1332+
) -> Result<PaymentId, PaymentSendFailure> {
1333+
if self.check_attempts() {
1334+
self.check_value_msats(Amount::Spontaneous(route.get_total_amount()));
12111335
Ok(PaymentId([1; 32]))
12121336
} else {
12131337
Err(PaymentSendFailure::ParameterError(APIError::MonitorUpdateFailed))
@@ -1218,7 +1342,7 @@ mod tests {
12181342
&self, route: &Route, _payment_id: PaymentId
12191343
) -> Result<(), PaymentSendFailure> {
12201344
if self.check_attempts() {
1221-
self.check_value_msats(route);
1345+
self.check_value_msats(Amount::ForInvoiceOrRetry(route.get_total_amount()));
12221346
Ok(())
12231347
} else {
12241348
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)