Skip to content

Commit fde7504

Browse files
committed
Score successful payment paths
Expand the Score trait with a payment_path_successful function for scoring successful payment paths. Called by InvoicePayer's EventHandler implementation when processing PaymentPathSuccessful events. May be used by Score implementations to revert any channel penalties that were applied by calls to payment_path_failed.
1 parent 0cdea66 commit fde7504

File tree

3 files changed

+94
-7
lines changed

3 files changed

+94
-7
lines changed

lightning-invoice/src/payment.rs

Lines changed: 83 additions & 7 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 {};
@@ -139,6 +144,10 @@ use std::sync::Mutex;
139144
use std::time::{Duration, SystemTime};
140145

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

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

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

11511199
impl TestRouter {
@@ -1212,7 +1260,13 @@ mod tests {
12121260
}
12131261

12141262
struct TestScorer {
1215-
expectations: VecDeque<u64>,
1263+
expectations: VecDeque<PaymentPath>,
1264+
}
1265+
1266+
#[derive(Debug)]
1267+
enum PaymentPath {
1268+
Failure { path: Vec<RouteHop>, short_channel_id: u64 },
1269+
Success { path: Vec<RouteHop> },
12161270
}
12171271

12181272
impl TestScorer {
@@ -1222,8 +1276,8 @@ mod tests {
12221276
}
12231277
}
12241278

1225-
fn expect_channel_failure(mut self, short_channel_id: u64) -> Self {
1226-
self.expectations.push_back(short_channel_id);
1279+
fn expect(mut self, expectation: PaymentPath) -> Self {
1280+
self.expectations.push_back(expectation);
12271281
self
12281282
}
12291283
}
@@ -1232,14 +1286,36 @@ mod tests {
12321286
impl lightning::util::ser::Writeable for TestScorer {
12331287
fn write<W: lightning::util::ser::Writer>(&self, _: &mut W) -> Result<(), std::io::Error> { unreachable!(); }
12341288
}
1289+
12351290
impl Score for TestScorer {
12361291
fn channel_penalty_msat(
12371292
&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
12381293
) -> u64 { 0 }
12391294

1240-
fn payment_path_failed(&mut self, _path: &[&RouteHop], short_channel_id: u64) {
1241-
if let Some(expected_short_channel_id) = self.expectations.pop_front() {
1242-
assert_eq!(short_channel_id, expected_short_channel_id);
1295+
fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {
1296+
if let Some(expectation) = self.expectations.pop_front() {
1297+
match expectation {
1298+
PaymentPath::Failure { path, short_channel_id } => {
1299+
assert_eq!(actual_path, path.iter().collect::<Vec<_>>());
1300+
assert_eq!(actual_short_channel_id, short_channel_id);
1301+
},
1302+
PaymentPath::Success { path } => {
1303+
panic!("Unexpected successful payment path: {:?}", path)
1304+
},
1305+
}
1306+
}
1307+
}
1308+
1309+
fn payment_path_successful(&mut self, actual_path: &[&RouteHop]) {
1310+
if let Some(expectation) = self.expectations.pop_front() {
1311+
match expectation {
1312+
PaymentPath::Failure { path, .. } => {
1313+
panic!("Unexpected payment path failure: {:?}", path)
1314+
},
1315+
PaymentPath::Success { path } => {
1316+
assert_eq!(actual_path, path.iter().collect::<Vec<_>>());
1317+
},
1318+
}
12431319
}
12441320
}
12451321
}

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]

lightning/src/routing/scoring.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ pub trait Score $(: $supertrait)* {
9595

9696
/// Handles updating channel penalties after failing to route through a channel.
9797
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64);
98+
99+
/// Handles updating channel penalties after successfully routing along a path.
100+
fn payment_path_successful(&mut self, path: &[&RouteHop]);
98101
}
99102

100103
impl<S: Score, T: DerefMut<Target=S> $(+ $supertrait)*> Score for T {
@@ -105,6 +108,10 @@ impl<S: Score, T: DerefMut<Target=S> $(+ $supertrait)*> Score for T {
105108
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
106109
self.deref_mut().payment_path_failed(path, short_channel_id)
107110
}
111+
112+
fn payment_path_successful(&mut self, path: &[&RouteHop]) {
113+
self.deref_mut().payment_path_successful(path)
114+
}
108115
}
109116
} }
110117

@@ -377,6 +384,8 @@ impl<T: Time> Score for ScorerUsingTime<T> {
377384
.and_modify(|failure| failure.add_penalty(failure_penalty_msat, half_life))
378385
.or_insert_with(|| ChannelFailure::new(failure_penalty_msat));
379386
}
387+
388+
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
380389
}
381390

382391
impl<T: Time> Writeable for ScorerUsingTime<T> {

0 commit comments

Comments
 (0)