Skip to content

Commit 2f9c3e5

Browse files
Score payment paths in BackgroundProcessor
1 parent 5aea2cf commit 2f9c3e5

File tree

2 files changed

+176
-5
lines changed

2 files changed

+176
-5
lines changed

lightning-background-processor/src/lib.rs

+170-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#[cfg(any(test, feature = "std"))]
1717
extern crate core;
1818

19+
#[cfg(not(feature = "std"))]
20+
extern crate alloc;
21+
1922
#[macro_use] extern crate lightning;
2023
extern crate lightning_rapid_gossip_sync;
2124

@@ -28,7 +31,7 @@ use lightning::ln::msgs::{ChannelMessageHandler, OnionMessageHandler, RoutingMes
2831
use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescriptor};
2932
use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
3033
use lightning::routing::router::Router;
31-
use lightning::routing::scoring::WriteableScore;
34+
use lightning::routing::scoring::{Score, WriteableScore};
3235
use lightning::util::events::{Event, EventHandler, EventsProvider};
3336
use lightning::util::logger::Logger;
3437
use lightning::util::persist::Persister;
@@ -49,6 +52,8 @@ use std::time::Instant;
4952

5053
#[cfg(feature = "futures")]
5154
use futures_util::{select_biased, future::FutureExt, task};
55+
#[cfg(not(feature = "std"))]
56+
use alloc::vec::Vec;
5257

5358
/// `BackgroundProcessor` takes care of tasks that (1) need to happen periodically to keep
5459
/// Rust-Lightning running properly, and (2) either can or should be run in the background. Its
@@ -216,6 +221,37 @@ fn handle_network_graph_update<L: Deref>(
216221
}
217222
}
218223

224+
fn update_scorer<'a, S: 'static + Deref<Target = SC> + Send + Sync, SC: 'a + WriteableScore<'a>>(
225+
scorer: &'a S, event: &Event
226+
) {
227+
let mut score = scorer.lock();
228+
match event {
229+
Event::PaymentPathFailed { ref path, short_channel_id: Some(scid), .. } => {
230+
let path = path.iter().collect::<Vec<_>>();
231+
score.payment_path_failed(&path, *scid);
232+
},
233+
Event::PaymentPathFailed { ref path, payment_failed_permanently: true, .. } => {
234+
// Reached if the destination explicitly failed it back. We treat this as a successful probe
235+
// because the payment made it all the way to the destination with sufficient liquidity.
236+
let path = path.iter().collect::<Vec<_>>();
237+
score.probe_successful(&path);
238+
},
239+
Event::PaymentPathSuccessful { path, .. } => {
240+
let path = path.iter().collect::<Vec<_>>();
241+
score.payment_path_successful(&path);
242+
},
243+
Event::ProbeSuccessful { path, .. } => {
244+
let path = path.iter().collect::<Vec<_>>();
245+
score.probe_successful(&path);
246+
},
247+
Event::ProbeFailed { path, short_channel_id: Some(scid), .. } => {
248+
let path = path.iter().collect::<Vec<_>>();
249+
score.probe_failed(&path, *scid);
250+
},
251+
_ => {},
252+
}
253+
}
254+
219255
macro_rules! define_run_body {
220256
($persister: ident, $chain_monitor: ident, $process_chain_monitor_events: expr,
221257
$channel_manager: ident, $process_channel_manager_events: expr,
@@ -387,7 +423,7 @@ pub async fn process_events_async<
387423
UMH: 'static + Deref + Send + Sync,
388424
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, OMH, L, UMH, NS>> + Send + Sync,
389425
S: 'static + Deref<Target = SC> + Send + Sync,
390-
SC: WriteableScore<'a>,
426+
SC: for<'b> WriteableScore<'b>,
391427
SleepFuture: core::future::Future<Output = bool> + core::marker::Unpin,
392428
Sleeper: Fn(Duration) -> SleepFuture
393429
>(
@@ -417,10 +453,14 @@ where
417453
let async_event_handler = |event| {
418454
let network_graph = gossip_sync.network_graph();
419455
let event_handler = &event_handler;
456+
let scorer = &scorer;
420457
async move {
421458
if let Some(network_graph) = network_graph {
422459
handle_network_graph_update(network_graph, &event)
423460
}
461+
if let Some(ref scorer) = scorer {
462+
update_scorer(scorer, &event);
463+
}
424464
event_handler(event).await;
425465
}
426466
};
@@ -516,7 +556,7 @@ impl BackgroundProcessor {
516556
UMH: 'static + Deref + Send + Sync,
517557
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, OMH, L, UMH, NS>> + Send + Sync,
518558
S: 'static + Deref<Target = SC> + Send + Sync,
519-
SC: WriteableScore<'a>,
559+
SC: for <'b> WriteableScore<'b>,
520560
>(
521561
persister: PS, event_handler: EH, chain_monitor: M, channel_manager: CM,
522562
gossip_sync: GossipSync<PGS, RGS, G, CA, L>, peer_manager: PM, logger: L, scorer: Option<S>,
@@ -547,6 +587,9 @@ impl BackgroundProcessor {
547587
if let Some(network_graph) = network_graph {
548588
handle_network_graph_update(network_graph, &event)
549589
}
590+
if let Some(ref scorer) = scorer {
591+
update_scorer(scorer, &event);
592+
}
550593
event_handler.handle_event(event);
551594
};
552595
define_run_body!(persister, chain_monitor, chain_monitor.process_pending_events(&event_handler),
@@ -613,14 +656,16 @@ mod tests {
613656
use bitcoin::blockdata::locktime::PackedLockTime;
614657
use bitcoin::blockdata::transaction::{Transaction, TxOut};
615658
use bitcoin::network::constants::Network;
659+
use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1};
616660
use lightning::chain::{BestBlock, Confirm, chainmonitor};
617661
use lightning::chain::channelmonitor::ANTI_REORG_DELAY;
618662
use lightning::chain::keysinterface::{InMemorySigner, EntropySource, KeysManager};
619663
use lightning::chain::transaction::OutPoint;
620664
use lightning::get_event_msg;
665+
use lightning::ln::PaymentHash;
621666
use lightning::ln::channelmanager;
622-
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters};
623-
use lightning::ln::features::ChannelFeatures;
667+
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
668+
use lightning::ln::features::{ChannelFeatures, NodeFeatures};
624669
use lightning::ln::msgs::{ChannelMessageHandler, Init};
625670
use lightning::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler};
626671
use lightning::routing::gossip::{NetworkGraph, NodeId, P2PGossipSync};
@@ -1296,4 +1341,124 @@ mod tests {
12961341
let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].no_gossip_sync(), nodes[0].peer_manager.clone(), nodes[0].logger.clone(), Some(nodes[0].scorer.clone()));
12971342
assert!(bg_processor.stop().is_ok());
12981343
}
1344+
1345+
#[test]
1346+
fn test_payment_path_scoring() {
1347+
// Ensure that we update the scorer when relevant events are processed. In this case, we ensure
1348+
// that we update the scorer upon a payment path succeeding (note that the channel must be
1349+
// public or else we won't score it).
1350+
// Set up a background event handler for FundingGenerationReady events.
1351+
let (sender, receiver) = std::sync::mpsc::sync_channel(1);
1352+
let event_handler = move |event: Event| match event {
1353+
Event::PaymentPathFailed { .. } => sender.send(event).unwrap(),
1354+
Event::PaymentPathSuccessful { .. } => sender.send(event).unwrap(),
1355+
Event::ProbeSuccessful { .. } => sender.send(event).unwrap(),
1356+
Event::ProbeFailed { .. } => sender.send(event).unwrap(),
1357+
_ => panic!("Unexpected event: {:?}", event),
1358+
};
1359+
1360+
let nodes = create_nodes(1, "test_payment_path_scoring".to_string());
1361+
let data_dir = nodes[0].persister.get_data_dir();
1362+
let persister = Arc::new(Persister::new(data_dir.clone()));
1363+
let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].no_gossip_sync(), nodes[0].peer_manager.clone(), nodes[0].logger.clone(), Some(nodes[0].scorer.clone()));
1364+
1365+
let scored_scid = 4242;
1366+
let secp_ctx = Secp256k1::new();
1367+
let node_1_privkey = SecretKey::from_slice(&[42; 32]).unwrap();
1368+
let node_1_id = PublicKey::from_secret_key(&secp_ctx, &node_1_privkey);
1369+
1370+
let path = vec![RouteHop {
1371+
pubkey: node_1_id,
1372+
node_features: NodeFeatures::empty(),
1373+
short_channel_id: scored_scid,
1374+
channel_features: ChannelFeatures::empty(),
1375+
fee_msat: 0,
1376+
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA as u32,
1377+
}];
1378+
1379+
nodes[0].scorer.lock().unwrap().expect(TestResult::PaymentFailure { path: path.clone(), short_channel_id: scored_scid });
1380+
nodes[0].node.push_pending_event(Event::PaymentPathFailed {
1381+
payment_id: None,
1382+
payment_hash: PaymentHash([42; 32]),
1383+
payment_failed_permanently: false,
1384+
network_update: None,
1385+
all_paths_failed: true,
1386+
path: path.clone(),
1387+
short_channel_id: Some(scored_scid),
1388+
retry: None,
1389+
});
1390+
let event = receiver
1391+
.recv_timeout(Duration::from_secs(EVENT_DEADLINE))
1392+
.expect("PaymentPathFailed not handled within deadline");
1393+
match event {
1394+
Event::PaymentPathFailed { .. } => {},
1395+
_ => panic!("Unexpected event"),
1396+
}
1397+
1398+
// Ensure we'll score payments that were explicitly failed back by the destination as
1399+
// ProbeSuccess.
1400+
nodes[0].scorer.lock().unwrap().expect(TestResult::ProbeSuccess { path: path.clone() });
1401+
nodes[0].node.push_pending_event(Event::PaymentPathFailed {
1402+
payment_id: None,
1403+
payment_hash: PaymentHash([42; 32]),
1404+
payment_failed_permanently: true,
1405+
network_update: None,
1406+
all_paths_failed: true,
1407+
path: path.clone(),
1408+
short_channel_id: None,
1409+
retry: None,
1410+
});
1411+
let event = receiver
1412+
.recv_timeout(Duration::from_secs(EVENT_DEADLINE))
1413+
.expect("PaymentPathFailed not handled within deadline");
1414+
match event {
1415+
Event::PaymentPathFailed { .. } => {},
1416+
_ => panic!("Unexpected event"),
1417+
}
1418+
1419+
nodes[0].scorer.lock().unwrap().expect(TestResult::PaymentSuccess { path: path.clone() });
1420+
nodes[0].node.push_pending_event(Event::PaymentPathSuccessful {
1421+
payment_id: PaymentId([42; 32]),
1422+
payment_hash: None,
1423+
path: path.clone(),
1424+
});
1425+
let event = receiver
1426+
.recv_timeout(Duration::from_secs(EVENT_DEADLINE))
1427+
.expect("PaymentPathSuccessful not handled within deadline");
1428+
match event {
1429+
Event::PaymentPathSuccessful { .. } => {},
1430+
_ => panic!("Unexpected event"),
1431+
}
1432+
1433+
nodes[0].scorer.lock().unwrap().expect(TestResult::ProbeSuccess { path: path.clone() });
1434+
nodes[0].node.push_pending_event(Event::ProbeSuccessful {
1435+
payment_id: PaymentId([42; 32]),
1436+
payment_hash: PaymentHash([42; 32]),
1437+
path: path.clone(),
1438+
});
1439+
let event = receiver
1440+
.recv_timeout(Duration::from_secs(EVENT_DEADLINE))
1441+
.expect("ProbeSuccessful not handled within deadline");
1442+
match event {
1443+
Event::ProbeSuccessful { .. } => {},
1444+
_ => panic!("Unexpected event"),
1445+
}
1446+
1447+
nodes[0].scorer.lock().unwrap().expect(TestResult::ProbeFailure { path: path.clone() });
1448+
nodes[0].node.push_pending_event(Event::ProbeFailed {
1449+
payment_id: PaymentId([42; 32]),
1450+
payment_hash: PaymentHash([42; 32]),
1451+
path: path.clone(),
1452+
short_channel_id: Some(scored_scid),
1453+
});
1454+
let event = receiver
1455+
.recv_timeout(Duration::from_secs(EVENT_DEADLINE))
1456+
.expect("ProbeFailure not handled within deadline");
1457+
match event {
1458+
Event::ProbeFailed { .. } => {},
1459+
_ => panic!("Unexpected event"),
1460+
}
1461+
1462+
assert!(bg_processor.stop().is_ok());
1463+
}
12991464
}

lightning/src/ln/channelmanager.rs

+6
Original file line numberDiff line numberDiff line change
@@ -5520,6 +5520,12 @@ where
55205520
events.into_inner()
55215521
}
55225522

5523+
#[cfg(feature = "_test_utils")]
5524+
pub fn push_pending_event(&self, event: events::Event) {
5525+
let mut events = self.pending_events.lock().unwrap();
5526+
events.push(event);
5527+
}
5528+
55235529
#[cfg(test)]
55245530
pub fn pop_pending_event(&self) -> Option<events::Event> {
55255531
let mut events = self.pending_events.lock().unwrap();

0 commit comments

Comments
 (0)