/// The simulation state.
///
/// You're free to put whatever you want here.
-pub struct State {
+pub struct State<'a> {
+ /// As a demonstration, the default run calculates the probability the LDK historical model
+ /// assigns to results.
+ scorer: lightning::routing::scoring::ProbabilisticScorer<&'a NetworkGraph<DevNullLogger>, DevNullLogger>,
+ // We demonstrate calculating log-loss of the LDK historical model
+ loss_sum: f64,
+ result_count: u64,
}
/// Creates a new [`State`] before any probe results are processed.
pub fn do_setup<'a>(graph: &'a NetworkGraph<DevNullLogger>) -> State {
- State {}
+ State {
+ scorer: lightning::routing::scoring::ProbabilisticScorer::new(Default::default(), graph, internal::DevNullLogger),
+ loss_sum: 0.0,
+ result_count: 0,
+ }
}
/// Processes one probe result.
/// The network graph as it existed at `result.timestamp` is provided, as well as a reference to
/// the current state.
pub fn process_probe_result(network_graph: ReadOnlyNetworkGraph, result: ProbeResult, state: &mut State) {
+ // Update the model's time
+ use lightning::routing::scoring::ScoreUpdate;
+ let cur_time = std::time::Duration::from_secs(result.timestamp);
+ state.scorer.time_passed(cur_time);
+ // Evaluate the model
+ for hop in result.channels_with_sufficient_liquidity.iter() {
+ // You can get additional information about the channel from the network_graph:
+ let _chan = network_graph.channels().get(&hop.short_channel_id).unwrap();
+ let mut model_probability =
+ state.scorer.historical_estimated_payment_success_probability(hop.short_channel_id, &hop.dst_node_id, hop.amount_msat, &Default::default())
+ .unwrap_or_else(||
+ state.scorer
+ .live_estimated_payment_success_probability(hop.short_channel_id, &hop.dst_node_id, hop.amount_msat, &Default::default())
+ .expect("We should have some estimated probability, even without history data")
+ );
+ if model_probability < 0.01 { model_probability = 0.01; }
+ state.loss_sum -= model_probability.log2();
+ state.result_count += 1;
+ }
+ if let Some(hop) = &result.channel_that_rejected_payment {
+ // You can get additional information about the channel from the network_graph:
+ let _chan = network_graph.channels().get(&hop.short_channel_id).unwrap();
+ let mut model_probability =
+ state.scorer.historical_estimated_payment_success_probability(hop.short_channel_id, &hop.dst_node_id, hop.amount_msat, &Default::default())
+ .unwrap_or_else(||
+ state.scorer
+ .live_estimated_payment_success_probability(hop.short_channel_id, &hop.dst_node_id, hop.amount_msat, &Default::default())
+ .expect("We should have some estimated probability, even without history data")
+ );
+ if model_probability > 0.99 { model_probability = 0.99; }
+ state.loss_sum -= (1.0 - model_probability).log2();
+ state.result_count += 1;
+ }
+
+ // Update the model with the information we learned
+ let mut path = lightning::routing::router::Path {
+ hops: Vec::new(),
+ blinded_tail: None,
+ };
+ let mut hops = result.channels_with_sufficient_liquidity.iter().chain(result.channel_that_rejected_payment.iter()).peekable();
+ while hops.peek().is_some() {
+ // Sadly, we have to munge the `DirectedChannel` into an LDK `RouteHop`.
+ let hop = hops.next().unwrap();
+ path.hops.push(lightning::routing::router::RouteHop {
+ pubkey: hop.dst_node_id.try_into().unwrap(),
+ node_features: lightning::types::features::NodeFeatures::empty(),
+ short_channel_id: hop.short_channel_id,
+ channel_features: lightning::types::features::ChannelFeatures::empty(),
+ fee_msat: hop.amount_msat - hops.peek().map(|hop| hop.amount_msat).unwrap_or(0),
+ cltv_expiry_delta: 42,
+ maybe_announced_channel: true,
+ });
+ }
+ if let Some(hop) = result.channel_that_rejected_payment {
+ state.scorer.payment_path_failed(&path, hop.short_channel_id, cur_time);
+ } else {
+ state.scorer.payment_path_successful(&path, cur_time);
+ }
}
/// This is run after all probe results have been processed, and should be used for printing
/// results or any required teardown.
pub fn results_complete(state: State) {
-
+ println!("Avg log-loss {}", state.loss_sum / (state.result_count as f64));
}
/// A hop in a route, consisting of a channel and the source public key, as well as the amount