@@ -84,14 +84,15 @@ pub struct Cli {
8484 /// Seed to run random activity generator deterministically
8585 #[ clap( long, short) ]
8686 pub fix_seed : Option < u64 > ,
87- /// A multiplier to wall time to speed up the simulation's clock. Only available when when running on a network of
88- /// simulated nodes.
89- #[ clap( long) ]
90- pub speedup_clock : Option < u16 > ,
9187 /// Latency to optionally introduce for payments in a simulated network expressed in
9288 /// milliseconds.
9389 #[ clap( long) ]
9490 pub latency_ms : Option < u32 > ,
91+ /// Run the simulated network on virtual time: time advances instantly to the next event instead of sleeping on the
92+ /// wall clock, so the run finishes as fast as the CPU allows and is reproducible for a given seed. Only available on
93+ /// a simulated network, and requires --total-time to bound the run.
94+ #[ clap( long, default_value_t = false ) ]
95+ pub virtual_time : bool ,
9596}
9697
9798impl Cli {
@@ -111,18 +112,25 @@ impl Cli {
111112 nodes or sim_graph to run with simulated nodes"
112113 ) ) ;
113114 }
114- if !sim_params. nodes . is_empty ( ) && self . speedup_clock . is_some ( ) {
115- return Err ( anyhow ! (
116- "Clock speedup is only allowed when running on a simulated network"
117- ) ) ;
118- }
119-
120115 if !sim_params. nodes . is_empty ( ) && self . latency_ms . is_some ( ) {
121116 return Err ( anyhow ! (
122117 "Latency for payments is only allowed when running on a simulated network"
123118 ) ) ;
124119 }
125120
121+ if self . virtual_time {
122+ if !sim_params. nodes . is_empty ( ) {
123+ return Err ( anyhow ! (
124+ "Virtual time is only allowed when running on a simulated network; real nodes run on wall time"
125+ ) ) ;
126+ }
127+ if self . total_time . is_none ( ) {
128+ return Err ( anyhow ! (
129+ "Virtual time requires --total-time, otherwise it advances forever"
130+ ) ) ;
131+ }
132+ }
133+
126134 if !sim_params. exclude . is_empty ( ) {
127135 if sim_params. sim_network . is_empty ( ) {
128136 return Err ( anyhow ! (
@@ -339,14 +347,15 @@ pub async fn create_simulation_with_network(
339347 ) )
340348}
341349
342- /// Parses the cli options provided and creates a simulation to be run, connecting to lightning nodes and validating
343- /// any activity described in the simulation file.
350+ /// Creates a simulation to be run against a set of real lightning nodes, connecting to the nodes described in
351+ /// `sim_params` and validating any activity described in the simulation file. The simulation is driven by `clock`,
352+ /// which must be constructed on the runtime that will run the simulation.
344353pub async fn create_simulation (
345- cli : & Cli ,
354+ cfg : SimulationCfg ,
346355 sim_params : & SimParams ,
356+ clock : Arc < SimulationClock > ,
347357 tasks : TaskTracker ,
348358) -> Result < ( Simulation < SimulationClock > , Vec < ActivityDefinition > ) , anyhow:: Error > {
349- let cfg: SimulationCfg = SimulationCfg :: try_from ( cli) ?;
350359 let SimParams {
351360 nodes,
352361 sim_network : _sim_network,
@@ -365,9 +374,7 @@ pub async fn create_simulation(
365374 cfg,
366375 clients,
367376 tasks,
368- // When running on a real network, the underlying node may use wall time so we always use a clock with no
369- // speedup.
370- Arc :: new ( SimulationClock :: new ( 1 ) ?) ,
377+ clock,
371378 shutdown_trigger,
372379 shutdown_listener,
373380 ) ,
@@ -531,7 +538,7 @@ async fn validate_activities(
531538 Ok ( validated_activities)
532539}
533540
534- async fn read_sim_path ( data_dir : PathBuf , sim_file : PathBuf ) -> anyhow:: Result < PathBuf > {
541+ fn read_sim_path ( data_dir : PathBuf , sim_file : PathBuf ) -> anyhow:: Result < PathBuf > {
535542 if sim_file. exists ( ) {
536543 Ok ( sim_file)
537544 } else if sim_file. is_relative ( ) {
@@ -540,15 +547,15 @@ async fn read_sim_path(data_dir: PathBuf, sim_file: PathBuf) -> anyhow::Result<P
540547 Ok ( sim_path)
541548 } else {
542549 log:: info!( "Simulation file '{}' does not exist." , sim_path. display( ) ) ;
543- select_sim_file ( data_dir) . await
550+ select_sim_file ( data_dir)
544551 }
545552 } else {
546553 log:: info!( "Simulation file '{}' does not exist." , sim_file. display( ) ) ;
547- select_sim_file ( data_dir) . await
554+ select_sim_file ( data_dir)
548555 }
549556}
550557
551- pub async fn select_sim_file ( data_dir : PathBuf ) -> anyhow:: Result < PathBuf > {
558+ pub fn select_sim_file ( data_dir : PathBuf ) -> anyhow:: Result < PathBuf > {
552559 let sim_files = std:: fs:: read_dir ( data_dir. clone ( ) ) ?
553560 . filter_map ( |f| {
554561 f. ok ( ) . and_then ( |f| {
@@ -585,8 +592,8 @@ fn mkdir(dir: PathBuf) -> anyhow::Result<PathBuf> {
585592 Ok ( dir)
586593}
587594
588- pub async fn parse_sim_params ( cli : & Cli ) -> anyhow:: Result < SimParams > {
589- let sim_path = read_sim_path ( cli. data_dir . clone ( ) , cli. sim_file . clone ( ) ) . await ?;
595+ pub fn parse_sim_params ( cli : & Cli ) -> anyhow:: Result < SimParams > {
596+ let sim_path = read_sim_path ( cli. data_dir . clone ( ) , cli. sim_file . clone ( ) ) ?;
590597 let sim_params = serde_json:: from_str ( & std:: fs:: read_to_string ( sim_path) ?) . map_err ( |e| {
591598 anyhow ! (
592599 "Could not deserialize node connection data or activity description from simulation file (line {}, col {}, err: {})." ,
0 commit comments