Skip to content

Commit 9b2da47

Browse files
committed
multi: make simulation generic over clock with default impl
Add a generic clock parameter to our simulation so that we can add the ability to mock time.
1 parent 3153add commit 9b2da47

File tree

1 file changed

+23
-6
lines changed

1 file changed

+23
-6
lines changed

simln-lib/src/lib.rs

+23-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use self::clock::{Clock, SystemClock};
12
use async_trait::async_trait;
23
use bitcoin::secp256k1::PublicKey;
34
use bitcoin::Network;
@@ -491,6 +492,8 @@ pub struct Simulation {
491492
/// High level triggers used to manage simulation tasks and shutdown.
492493
shutdown_trigger: Trigger,
493494
shutdown_listener: Listener,
495+
/// Clock for the simulation.
496+
clock: Arc<dyn Clock>,
494497
}
495498

496499
#[derive(Clone)]
@@ -527,6 +530,7 @@ impl Simulation {
527530
tasks,
528531
shutdown_trigger,
529532
shutdown_listener,
533+
clock: Arc::new(SystemClock {}),
530534
}
531535
}
532536

@@ -701,6 +705,7 @@ impl Simulation {
701705
if let Some(total_time) = self.cfg.total_time {
702706
let shutdown = self.shutdown_trigger.clone();
703707
let listener = self.shutdown_listener.clone();
708+
let clock = self.clock.clone();
704709

705710
self.tasks.spawn(async move {
706711
select! {
@@ -709,7 +714,7 @@ impl Simulation {
709714
log::debug!("Timeout task exited on listener signal");
710715
}
711716

712-
_ = time::sleep(total_time) => {
717+
_ = clock.sleep(total_time) => {
713718
log::info!(
714719
"Simulation run for {}s. Shutting down.",
715720
total_time.as_secs()
@@ -776,23 +781,27 @@ impl Simulation {
776781

777782
let result_logger_clone = result_logger.clone();
778783
let result_logger_listener = listener.clone();
784+
let clock = self.clock.clone();
779785
tasks.spawn(async move {
780786
log::debug!("Starting results logger.");
781787
run_results_logger(
782788
result_logger_listener,
783789
result_logger_clone,
784790
Duration::from_secs(60),
791+
clock,
785792
)
786793
.await;
787794
log::debug!("Exiting results logger.");
788795
});
789796

790797
// csr: consume simulation results
791798
let csr_write_results = self.cfg.write_results.clone();
799+
let clock = self.clock.clone();
792800
tasks.spawn(async move {
793801
log::debug!("Starting simulation results consumer.");
794802
if let Err(e) = consume_simulation_results(
795803
result_logger,
804+
clock,
796805
results_receiver,
797806
listener,
798807
csr_write_results,
@@ -930,11 +939,12 @@ impl Simulation {
930939
let ce_shutdown = self.shutdown_trigger.clone();
931940
let ce_output_sender = output_sender.clone();
932941
let ce_node = node.clone();
942+
let clock = self.clock.clone();
933943
tasks.spawn(async move {
934944
let node_info = ce_node.lock().await.get_info().clone();
935945
log::debug!("Starting events consumer for {}.", node_info);
936946
if let Err(e) =
937-
consume_events(ce_node, receiver, ce_output_sender, ce_listener).await
947+
consume_events(ce_node, clock, receiver, ce_output_sender, ce_listener).await
938948
{
939949
ce_shutdown.trigger();
940950
log::error!("Event consumer for node {node_info} exited with error: {e:?}.");
@@ -967,6 +977,8 @@ impl Simulation {
967977
let pe_shutdown = self.shutdown_trigger.clone();
968978
let pe_listener = self.shutdown_listener.clone();
969979
let pe_sender = sender.clone();
980+
let clock = self.clock.clone();
981+
970982
tasks.spawn(async move {
971983
let source = executor.source_info.clone();
972984

@@ -980,6 +992,7 @@ impl Simulation {
980992
executor.source_info,
981993
executor.network_generator,
982994
executor.payment_generator,
995+
clock,
983996
pe_sender,
984997
pe_listener,
985998
)
@@ -1001,6 +1014,7 @@ impl Simulation {
10011014
/// event being executed is piped into a channel to handle the result of the event.
10021015
async fn consume_events(
10031016
node: Arc<Mutex<dyn LightningNode>>,
1017+
clock: Arc<dyn Clock>,
10041018
mut receiver: Receiver<SimulationEvent>,
10051019
sender: Sender<SimulationOutput>,
10061020
listener: Listener,
@@ -1022,7 +1036,7 @@ async fn consume_events(
10221036
hash: None,
10231037
amount_msat: amt_msat,
10241038
destination: dest.pubkey,
1025-
dispatch_time: SystemTime::now(),
1039+
dispatch_time: clock.now(),
10261040
};
10271041

10281042
let outcome = match node.send_payment(dest.pubkey, amt_msat).await {
@@ -1084,6 +1098,7 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
10841098
source: NodeInfo,
10851099
network_generator: Arc<Mutex<N>>,
10861100
node_generator: Box<A>,
1101+
clock: Arc<dyn Clock>,
10871102
sender: Sender<SimulationEvent>,
10881103
listener: Listener,
10891104
) -> Result<(), SimulationError> {
@@ -1107,7 +1122,7 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
11071122
},
11081123
// Wait until our time to next payment has elapsed then execute a random amount payment to a random
11091124
// destination.
1110-
_ = time::sleep(wait) => {
1125+
_ = clock.sleep(wait) => {
11111126
let (destination, capacity) = network_generator.lock().await.choose_destination(source.pubkey).map_err(SimulationError::DestinationGenerationError)?;
11121127

11131128
// Only proceed with a payment if the amount is non-zero, otherwise skip this round. If we can't get
@@ -1183,13 +1198,14 @@ fn get_payment_delay<A: PaymentGenerator + ?Sized>(
11831198

11841199
async fn consume_simulation_results(
11851200
logger: Arc<Mutex<PaymentResultLogger>>,
1201+
clock: Arc<dyn Clock>,
11861202
mut receiver: Receiver<(Payment, PaymentResult)>,
11871203
listener: Listener,
11881204
write_results: Option<WriteResults>,
11891205
) -> Result<(), SimulationError> {
11901206
let mut writer = match write_results {
11911207
Some(res) => {
1192-
let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
1208+
let duration = clock.now().duration_since(SystemTime::UNIX_EPOCH)?;
11931209
let file = res
11941210
.results_dir
11951211
.join(format!("simulation_{:?}.csv", duration));
@@ -1288,6 +1304,7 @@ async fn run_results_logger(
12881304
listener: Listener,
12891305
logger: Arc<Mutex<PaymentResultLogger>>,
12901306
interval: Duration,
1307+
clock: Arc<dyn Clock>,
12911308
) {
12921309
log::info!("Summary of results will be reported every {:?}.", interval);
12931310

@@ -1298,7 +1315,7 @@ async fn run_results_logger(
12981315
break
12991316
}
13001317

1301-
_ = time::sleep(interval) => {
1318+
_ = clock.sleep(interval) => {
13021319
log::info!("{}", logger.lock().await)
13031320
}
13041321
}

0 commit comments

Comments
 (0)