Skip to content

Commit 9fd58d2

Browse files
committed
sim-lib/feat: make Simulation generic over Clock
1 parent c356a4a commit 9fd58d2

File tree

3 files changed

+50
-13
lines changed

3 files changed

+50
-13
lines changed

sim-cli/src/parsing.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use bitcoin::secp256k1::PublicKey;
33
use clap::{builder::TypedValueParser, Parser};
44
use log::LevelFilter;
55
use serde::{Deserialize, Serialize};
6+
use simln_lib::clock::SimulationClock;
67
use simln_lib::sim_node::{
78
ln_node_from_graph, populate_network_graph, ChannelPolicy, SimGraph, SimulatedChannel,
89
};
@@ -198,7 +199,7 @@ pub async fn create_simulation_with_network(
198199
cli: &Cli,
199200
sim_params: &SimParams,
200201
tasks: TaskTracker,
201-
) -> Result<(Simulation, Vec<ActivityDefinition>), anyhow::Error> {
202+
) -> Result<(Simulation<SimulationClock>, Vec<ActivityDefinition>), anyhow::Error> {
202203
let cfg: SimulationCfg = SimulationCfg::try_from(cli)?;
203204
let SimParams {
204205
nodes: _,
@@ -241,7 +242,14 @@ pub async fn create_simulation_with_network(
241242
get_validated_activities(&nodes, nodes_info, sim_params.activity.clone()).await?;
242243

243244
Ok((
244-
Simulation::new(cfg, nodes, tasks, shutdown_trigger, shutdown_listener),
245+
Simulation::new(
246+
cfg,
247+
nodes,
248+
tasks,
249+
Arc::new(SimulationClock::new(1)?),
250+
shutdown_trigger,
251+
shutdown_listener,
252+
),
245253
validated_activities,
246254
))
247255
}
@@ -252,7 +260,7 @@ pub async fn create_simulation(
252260
cli: &Cli,
253261
sim_params: &SimParams,
254262
tasks: TaskTracker,
255-
) -> Result<(Simulation, Vec<ActivityDefinition>), anyhow::Error> {
263+
) -> Result<(Simulation<SimulationClock>, Vec<ActivityDefinition>), anyhow::Error> {
256264
let cfg: SimulationCfg = SimulationCfg::try_from(cli)?;
257265
let SimParams {
258266
nodes,
@@ -267,7 +275,14 @@ pub async fn create_simulation(
267275
get_validated_activities(&clients, clients_info, sim_params.activity.clone()).await?;
268276

269277
Ok((
270-
Simulation::new(cfg, clients, tasks, shutdown_trigger, shutdown_listener),
278+
Simulation::new(
279+
cfg,
280+
clients,
281+
tasks,
282+
Arc::new(SimulationClock::new(1)?),
283+
shutdown_trigger,
284+
shutdown_listener,
285+
),
271286
validated_activities,
272287
))
273288
}

simln-lib/src/lib.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![deny(rustdoc::broken_intra_doc_links)]
22

3+
use self::clock::Clock;
34
use async_trait::async_trait;
45
use bitcoin::secp256k1::PublicKey;
56
use bitcoin::Network;
@@ -545,7 +546,7 @@ impl SimulationCfg {
545546
/// The simulator can execute both predefined payment patterns and generate random payment activity
546547
/// based on configuration parameters.
547548
#[derive(Clone)]
548-
pub struct Simulation {
549+
pub struct Simulation<C: Clock + 'static> {
549550
/// Config for the simulation itself.
550551
cfg: SimulationCfg,
551552
/// The lightning node that is being simulated.
@@ -558,6 +559,8 @@ pub struct Simulation {
558559
/// High level triggers used to manage simulation tasks and shutdown.
559560
shutdown_trigger: Trigger,
560561
shutdown_listener: Listener,
562+
/// Clock for the simulation.
563+
clock: Arc<C>,
561564
}
562565

563566
/// Configuration for writing simulation results to CSV files.
@@ -579,11 +582,12 @@ struct ExecutorKit {
579582
payment_generator: Box<dyn PaymentGenerator>,
580583
}
581584

582-
impl Simulation {
585+
impl<C: Clock + 'static> Simulation<C> {
583586
pub fn new(
584587
cfg: SimulationCfg,
585588
nodes: HashMap<PublicKey, Arc<Mutex<dyn LightningNode>>>,
586589
tasks: TaskTracker,
590+
clock: Arc<C>,
587591
shutdown_trigger: Trigger,
588592
shutdown_listener: Listener,
589593
) -> Self {
@@ -594,6 +598,7 @@ impl Simulation {
594598
tasks,
595599
shutdown_trigger,
596600
shutdown_listener,
601+
clock,
597602
}
598603
}
599604

@@ -771,6 +776,7 @@ impl Simulation {
771776
if let Some(total_time) = self.cfg.total_time {
772777
let shutdown = self.shutdown_trigger.clone();
773778
let listener = self.shutdown_listener.clone();
779+
let clock = self.clock.clone();
774780

775781
self.tasks.spawn(async move {
776782
select! {
@@ -779,7 +785,7 @@ impl Simulation {
779785
log::debug!("Timeout task exited on listener signal");
780786
}
781787

782-
_ = time::sleep(total_time) => {
788+
_ = clock.sleep(total_time) => {
783789
log::info!(
784790
"Simulation run for {}s. Shutting down.",
785791
total_time.as_secs()
@@ -846,23 +852,27 @@ impl Simulation {
846852

847853
let result_logger_clone = result_logger.clone();
848854
let result_logger_listener = listener.clone();
855+
let clock = self.clock.clone();
849856
tasks.spawn(async move {
850857
log::debug!("Starting results logger.");
851858
run_results_logger(
852859
result_logger_listener,
853860
result_logger_clone,
854861
Duration::from_secs(60),
862+
clock,
855863
)
856864
.await;
857865
log::debug!("Exiting results logger.");
858866
});
859867

860868
// csr: consume simulation results
861869
let csr_write_results = self.cfg.write_results.clone();
870+
let clock = self.clock.clone();
862871
tasks.spawn(async move {
863872
log::debug!("Starting simulation results consumer.");
864873
if let Err(e) = consume_simulation_results(
865874
result_logger,
875+
clock,
866876
results_receiver,
867877
listener,
868878
csr_write_results,
@@ -1003,11 +1013,12 @@ impl Simulation {
10031013
let ce_shutdown = self.shutdown_trigger.clone();
10041014
let ce_output_sender = output_sender.clone();
10051015
let ce_node = node.clone();
1016+
let clock = self.clock.clone();
10061017
tasks.spawn(async move {
10071018
let node_info = ce_node.lock().await.get_info().clone();
10081019
log::debug!("Starting events consumer for {}.", node_info);
10091020
if let Err(e) =
1010-
consume_events(ce_node, receiver, ce_output_sender, ce_listener).await
1021+
consume_events(ce_node, clock, receiver, ce_output_sender, ce_listener).await
10111022
{
10121023
ce_shutdown.trigger();
10131024
log::error!("Event consumer for node {node_info} exited with error: {e:?}.");
@@ -1040,6 +1051,8 @@ impl Simulation {
10401051
let pe_shutdown = self.shutdown_trigger.clone();
10411052
let pe_listener = self.shutdown_listener.clone();
10421053
let pe_sender = sender.clone();
1054+
let clock = self.clock.clone();
1055+
10431056
tasks.spawn(async move {
10441057
let source = executor.source_info.clone();
10451058

@@ -1053,6 +1066,7 @@ impl Simulation {
10531066
executor.source_info,
10541067
executor.network_generator,
10551068
executor.payment_generator,
1069+
clock,
10561070
pe_sender,
10571071
pe_listener,
10581072
)
@@ -1074,6 +1088,7 @@ impl Simulation {
10741088
/// event being executed is piped into a channel to handle the result of the event.
10751089
async fn consume_events(
10761090
node: Arc<Mutex<dyn LightningNode>>,
1091+
clock: Arc<dyn Clock>,
10771092
mut receiver: Receiver<SimulationEvent>,
10781093
sender: Sender<SimulationOutput>,
10791094
listener: Listener,
@@ -1095,7 +1110,7 @@ async fn consume_events(
10951110
hash: None,
10961111
amount_msat: amt_msat,
10971112
destination: dest.pubkey,
1098-
dispatch_time: SystemTime::now(),
1113+
dispatch_time: clock.now(),
10991114
};
11001115

11011116
let outcome = match node.send_payment(dest.pubkey, amt_msat).await {
@@ -1157,6 +1172,7 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
11571172
source: NodeInfo,
11581173
network_generator: Arc<Mutex<N>>,
11591174
node_generator: Box<A>,
1175+
clock: Arc<dyn Clock>,
11601176
sender: Sender<SimulationEvent>,
11611177
listener: Listener,
11621178
) -> Result<(), SimulationError> {
@@ -1180,7 +1196,7 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
11801196
},
11811197
// Wait until our time to next payment has elapsed then execute a random amount payment to a random
11821198
// destination.
1183-
_ = time::sleep(wait) => {
1199+
_ = clock.sleep(wait) => {
11841200
let (destination, capacity) = network_generator.lock().await.choose_destination(source.pubkey).map_err(SimulationError::DestinationGenerationError)?;
11851201

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

12571273
async fn consume_simulation_results(
12581274
logger: Arc<Mutex<PaymentResultLogger>>,
1275+
clock: Arc<dyn Clock>,
12591276
mut receiver: Receiver<(Payment, PaymentResult)>,
12601277
listener: Listener,
12611278
write_results: Option<WriteResults>,
12621279
) -> Result<(), SimulationError> {
12631280
let mut writer = match write_results {
12641281
Some(res) => {
1265-
let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
1282+
let duration = clock.now().duration_since(SystemTime::UNIX_EPOCH)?;
12661283
let file = res
12671284
.results_dir
12681285
.join(format!("simulation_{:?}.csv", duration));
@@ -1361,6 +1378,7 @@ async fn run_results_logger(
13611378
listener: Listener,
13621379
logger: Arc<Mutex<PaymentResultLogger>>,
13631380
interval: Duration,
1381+
clock: Arc<dyn Clock>,
13641382
) {
13651383
log::info!("Summary of results will be reported every {:?}.", interval);
13661384

@@ -1371,7 +1389,7 @@ async fn run_results_logger(
13711389
break
13721390
}
13731391

1374-
_ = time::sleep(interval) => {
1392+
_ = clock.sleep(interval) => {
13751393
log::info!("{}", logger.lock().await)
13761394
}
13771395
}

simln-lib/src/test_utils.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::{collections::HashMap, fmt, sync::Arc, time::Duration};
1010
use tokio::sync::Mutex;
1111
use tokio_util::task::TaskTracker;
1212

13+
use crate::clock::SystemClock;
1314
use crate::{
1415
ActivityDefinition, LightningError, LightningNode, NodeInfo, PaymentGenerationError,
1516
PaymentGenerator, Simulation, SimulationCfg, ValueOrRange,
@@ -195,12 +196,15 @@ impl LightningTestNodeBuilder {
195196

196197
/// Creates a new simulation with the given clients and activity definitions.
197198
/// Note: This sets a runtime for the simulation of 0, so run() will exit immediately.
198-
pub fn create_simulation(clients: HashMap<PublicKey, Arc<Mutex<dyn LightningNode>>>) -> Simulation {
199+
pub fn create_simulation(
200+
clients: HashMap<PublicKey, Arc<Mutex<dyn LightningNode>>>,
201+
) -> Simulation<SystemClock> {
199202
let (shutdown_trigger, shutdown_listener) = triggered::trigger();
200203
Simulation::new(
201204
SimulationCfg::new(Some(0), 0, 0.0, None, None),
202205
clients,
203206
TaskTracker::new(),
207+
Arc::new(SystemClock {}),
204208
shutdown_trigger,
205209
shutdown_listener,
206210
)

0 commit comments

Comments
 (0)