Skip to content

Commit ea89286

Browse files
authored
Merge pull request #1197 from jkczyz/2021-11-score-successful-payment-path
Score successful payment paths
2 parents a3e4af0 + 857b4c0 commit ea89286

File tree

3 files changed

+190
-24
lines changed

3 files changed

+190
-24
lines changed

lightning-invoice/src/payment.rs

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
//! and payee using information provided by the payer and from the payee's [`Invoice`], when
1616
//! applicable.
1717
//!
18+
//! [`InvoicePayer`] is parameterized by a [`LockableScore`], which it uses for scoring failed and
19+
//! successful payment paths upon receiving [`Event::PaymentPathFailed`] and
20+
//! [`Event::PaymentPathSuccessful`] events, respectively.
21+
//!
1822
//! [`InvoicePayer`] is capable of retrying failed payments. It accomplishes this by implementing
1923
//! [`EventHandler`] which decorates a user-provided handler. It will intercept any
2024
//! [`Event::PaymentPathFailed`] events and retry the failed paths for a fixed number of total
@@ -80,6 +84,7 @@
8084
//! # &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
8185
//! # ) -> u64 { 0 }
8286
//! # fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
87+
//! # fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
8388
//! # }
8489
//! #
8590
//! # struct FakeLogger {}
@@ -140,6 +145,10 @@ use std::sync::Mutex;
140145
use std::time::{Duration, SystemTime};
141146

142147
/// A utility for paying [`Invoice`]s and sending spontaneous payments.
148+
///
149+
/// See [module-level documentation] for details.
150+
///
151+
/// [module-level documentation]: crate::payment
143152
pub struct InvoicePayer<P: Deref, R, S: Deref, L: Deref, E>
144153
where
145154
P::Target: Payer,
@@ -474,6 +483,10 @@ where
474483

475484
if *all_paths_failed { self.payment_cache.lock().unwrap().remove(payment_hash); }
476485
},
486+
Event::PaymentPathSuccessful { path, .. } => {
487+
let path = path.iter().collect::<Vec<_>>();
488+
self.scorer.lock().payment_path_successful(&path);
489+
},
477490
Event::PaymentSent { payment_hash, .. } => {
478491
let mut payment_cache = self.payment_cache.lock().unwrap();
479492
let attempts = payment_cache
@@ -1128,7 +1141,9 @@ mod tests {
11281141
.expect_send(Amount::ForInvoice(final_value_msat))
11291142
.expect_send(Amount::OnRetry(final_value_msat / 2));
11301143
let router = TestRouter {};
1131-
let scorer = RefCell::new(TestScorer::new().expect_channel_failure(short_channel_id.unwrap()));
1144+
let scorer = RefCell::new(TestScorer::new().expect(PaymentPath::Failure {
1145+
path: path.clone(), short_channel_id: path[0].short_channel_id,
1146+
}));
11321147
let logger = TestLogger::new();
11331148
let invoice_payer =
11341149
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, RetryAttempts(2));
@@ -1147,6 +1162,39 @@ mod tests {
11471162
invoice_payer.handle_event(&event);
11481163
}
11491164

1165+
#[test]
1166+
fn scores_successful_channels() {
1167+
let event_handled = core::cell::RefCell::new(false);
1168+
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
1169+
1170+
let payment_preimage = PaymentPreimage([1; 32]);
1171+
let invoice = invoice(payment_preimage);
1172+
let payment_hash = Some(PaymentHash(invoice.payment_hash().clone().into_inner()));
1173+
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
1174+
let route = TestRouter::route_for_value(final_value_msat);
1175+
1176+
// Expect that scorer is given short_channel_id upon handling the event.
1177+
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
1178+
let router = TestRouter {};
1179+
let scorer = RefCell::new(TestScorer::new()
1180+
.expect(PaymentPath::Success { path: route.paths[0].clone() })
1181+
.expect(PaymentPath::Success { path: route.paths[1].clone() })
1182+
);
1183+
let logger = TestLogger::new();
1184+
let invoice_payer =
1185+
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, RetryAttempts(2));
1186+
1187+
let payment_id = invoice_payer.pay_invoice(&invoice).unwrap();
1188+
let event = Event::PaymentPathSuccessful {
1189+
payment_id, payment_hash, path: route.paths[0].clone()
1190+
};
1191+
invoice_payer.handle_event(&event);
1192+
let event = Event::PaymentPathSuccessful {
1193+
payment_id, payment_hash, path: route.paths[1].clone()
1194+
};
1195+
invoice_payer.handle_event(&event);
1196+
}
1197+
11501198
struct TestRouter;
11511199

11521200
impl TestRouter {
@@ -1213,18 +1261,24 @@ mod tests {
12131261
}
12141262

12151263
struct TestScorer {
1216-
expectations: VecDeque<u64>,
1264+
expectations: Option<VecDeque<PaymentPath>>,
1265+
}
1266+
1267+
#[derive(Debug)]
1268+
enum PaymentPath {
1269+
Failure { path: Vec<RouteHop>, short_channel_id: u64 },
1270+
Success { path: Vec<RouteHop> },
12171271
}
12181272

12191273
impl TestScorer {
12201274
fn new() -> Self {
12211275
Self {
1222-
expectations: VecDeque::new(),
1276+
expectations: None,
12231277
}
12241278
}
12251279

1226-
fn expect_channel_failure(mut self, short_channel_id: u64) -> Self {
1227-
self.expectations.push_back(short_channel_id);
1280+
fn expect(mut self, expectation: PaymentPath) -> Self {
1281+
self.expectations.get_or_insert_with(|| VecDeque::new()).push_back(expectation);
12281282
self
12291283
}
12301284
}
@@ -1233,14 +1287,38 @@ mod tests {
12331287
impl lightning::util::ser::Writeable for TestScorer {
12341288
fn write<W: lightning::util::ser::Writer>(&self, _: &mut W) -> Result<(), std::io::Error> { unreachable!(); }
12351289
}
1290+
12361291
impl Score for TestScorer {
12371292
fn channel_penalty_msat(
12381293
&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
12391294
) -> u64 { 0 }
12401295

1241-
fn payment_path_failed(&mut self, _path: &[&RouteHop], short_channel_id: u64) {
1242-
if let Some(expected_short_channel_id) = self.expectations.pop_front() {
1243-
assert_eq!(short_channel_id, expected_short_channel_id);
1296+
fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {
1297+
if let Some(expectations) = &mut self.expectations {
1298+
match expectations.pop_front() {
1299+
Some(PaymentPath::Failure { path, short_channel_id }) => {
1300+
assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
1301+
assert_eq!(actual_short_channel_id, short_channel_id);
1302+
},
1303+
Some(PaymentPath::Success { path }) => {
1304+
panic!("Unexpected successful payment path: {:?}", path)
1305+
},
1306+
None => panic!("Unexpected payment_path_failed call: {:?}", actual_path),
1307+
}
1308+
}
1309+
}
1310+
1311+
fn payment_path_successful(&mut self, actual_path: &[&RouteHop]) {
1312+
if let Some(expectations) = &mut self.expectations {
1313+
match expectations.pop_front() {
1314+
Some(PaymentPath::Failure { path, .. }) => {
1315+
panic!("Unexpected payment path failure: {:?}", path)
1316+
},
1317+
Some(PaymentPath::Success { path }) => {
1318+
assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
1319+
},
1320+
None => panic!("Unexpected payment_path_successful call: {:?}", actual_path),
1321+
}
12441322
}
12451323
}
12461324
}
@@ -1251,8 +1329,10 @@ mod tests {
12511329
return;
12521330
}
12531331

1254-
if !self.expectations.is_empty() {
1255-
panic!("Unsatisfied channel failure expectations: {:?}", self.expectations);
1332+
if let Some(expectations) = &self.expectations {
1333+
if !expectations.is_empty() {
1334+
panic!("Unsatisfied scorer expectations: {:?}", expectations);
1335+
}
12561336
}
12571337
}
12581338
}

lightning/src/routing/router.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4665,6 +4665,7 @@ mod tests {
46654665
}
46664666

46674667
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
4668+
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
46684669
}
46694670

46704671
struct BadNodeScorer {
@@ -4682,6 +4683,7 @@ mod tests {
46824683
}
46834684

46844685
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
4686+
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
46854687
}
46864688

46874689
#[test]

0 commit comments

Comments
 (0)