Skip to content

Commit

Permalink
Merge pull request #132 from okjodom/config-dir
Browse files Browse the repository at this point in the history
sim-cli: source simulation file from data directory
  • Loading branch information
carlaKC authored Nov 20, 2023
2 parents 3416e02 + 9975d8a commit 6a056fe
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ sim.json
package-lock.json
activity-generator/releases/*
.DS_Store
*simulation_*.csv
/results
79 changes: 77 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@ Install the CLI:
cargo install --locked --path sim-cli/
```

Run with Simulation File (see [setup instructions](#simulation-file-setup) for details):
```
sim-cli sim.json
To run the simulation, create a simulation file `sim.json` in the working directory (see [setup instructions](#simulation-file-setup) for details) and run:

```
sim-cli
```

Run `sim-cli -h` for details on `--data-dir` and `--sim-file` options that allow specifying custom simulation file locations.

### Simulation File Setup
The simulator requires access details for a set of `nodes` that you
have permission to execute commands on. Note that the current version
Expand Down Expand Up @@ -190,10 +193,12 @@ described above). Activity sources and destinations may reference the
*must* provide a valid public key.

### Simulation Output
A summary of the results will be logged by the simulator, and a full
list of payments made with their outcomes is available in
`simulation_{timestamp}.csv` in the directory that the simulation was
executed in. For more detailed logs, use the `--log-level` cli flag.

A summary of the results will be logged by the simulator. A full list of payments made with their outcomes
is available in `simulation_{timestamp}.csv` within the configured `{data_dir}/results`.

Run `sim-cli -h` for details on data directory (`--data-dir`) and other options including `--print-batch-size`
which affect how simulation outputs are persisted

## Lightning Environments
If you're looking to get started with local lightning development, we
Expand Down
1 change: 1 addition & 0 deletions sim-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Instantly simulate real-world Lightning network activity
[dependencies]
anyhow = { version = "1.0.69", features = ["backtrace"] }
clap = { version = "4.1.6", features = ["derive", "env", "std", "help", "usage", "error-context", "suggestions"], default-features = false }
dialoguer = "0.11.0"
log = "0.4.20"
serde = "1.0.183"
serde_json = "1.0.104"
Expand Down
81 changes: 76 additions & 5 deletions sim-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ use clap::Parser;
use log::LevelFilter;
use sim_lib::{
cln::ClnNode, lnd::LndNode, ActivityDefinition, LightningError, LightningNode, NodeConnection,
NodeId, SimParams, Simulation,
NodeId, SimParams, Simulation, WriteResults,
};
use simple_logger::SimpleLogger;

/// The default directory where the simulation files are stored and where the results will be written to.
pub const DEFAULT_DATA_DIR: &str = ".";

/// The default simulation file to be used by the simulator.
pub const DEFAULT_SIM_FILE: &str = "sim.json";

/// The default expected payment amount for the simulation, around ~$10 at the time of writing.
pub const EXPECTED_PAYMENT_AMOUNT: u64 = 3_800_000;

Expand Down Expand Up @@ -42,8 +48,12 @@ fn deserialize_f64_greater_than_zero(x: String) -> Result<f64, String> {
#[derive(Parser)]
#[command(version, about)]
struct Cli {
/// Path to a directory containing simulation files, and where simulation results will be stored
#[clap(long, short, default_value = DEFAULT_DATA_DIR)]
data_dir: PathBuf,
/// Path to the simulation file to be used by the simulator
#[clap(index = 1)]
/// This can either be an absolute path, or relative path with respect to data_dir
#[clap(long, short, default_value = DEFAULT_SIM_FILE)]
sim_file: PathBuf,
/// Total time the simulator will be running
#[clap(long, short)]
Expand Down Expand Up @@ -77,8 +87,9 @@ async fn main() -> anyhow::Result<()> {
.init()
.unwrap();

let sim_path = read_sim_path(cli.data_dir.clone(), cli.sim_file).await?;
let SimParams { nodes, activity } =
serde_json::from_str(&std::fs::read_to_string(cli.sim_file)?)
serde_json::from_str(&std::fs::read_to_string(sim_path)?)
.map_err(|e| anyhow!("Could not deserialize node connection data or activity description from simulation file (line {}, col {}).", e.line(), e.column()))?;

let mut clients: HashMap<PublicKey, Arc<Mutex<dyn LightningNode + Send>>> = HashMap::new();
Expand Down Expand Up @@ -179,14 +190,22 @@ async fn main() -> anyhow::Result<()> {
});
}

let write_results = if !cli.no_results {
Some(WriteResults {
results_dir: mkdir(cli.data_dir.join("results")).await?,
batch_size: cli.print_batch_size,
})
} else {
None
};

let sim = Simulation::new(
clients,
validated_activities,
cli.total_time,
cli.print_batch_size,
cli.expected_pmt_amt,
cli.capacity_multiplier,
cli.no_results,
write_results,
);
let sim2 = sim.clone();

Expand All @@ -199,3 +218,55 @@ async fn main() -> anyhow::Result<()> {

Ok(())
}

async fn read_sim_path(data_dir: PathBuf, sim_file: PathBuf) -> anyhow::Result<PathBuf> {
let sim_path = if sim_file.is_relative() {
data_dir.join(sim_file)
} else {
sim_file
};

if sim_path.exists() {
Ok(sim_path)
} else {
log::info!("Simulation file '{}' does not exist.", sim_path.display());
select_sim_file(data_dir).await
}
}

async fn select_sim_file(data_dir: PathBuf) -> anyhow::Result<PathBuf> {
let sim_files = std::fs::read_dir(data_dir.clone())?
.filter_map(|f| {
f.ok().and_then(|f| {
if f.path().extension()?.to_str()? == "json" {
f.file_name().into_string().ok()
} else {
None
}
})
})
.collect::<Vec<_>>();

if sim_files.is_empty() {
anyhow::bail!(
"no simulation files found in {}.",
data_dir.canonicalize()?.display()
);
}

let selection = dialoguer::Select::new()
.with_prompt(format!(
"Select a simulation file. Found these in {}",
data_dir.canonicalize()?.display()
))
.items(&sim_files)
.default(0)
.interact()?;

Ok(data_dir.join(sim_files[selection].clone()))
}

async fn mkdir(dir: PathBuf) -> anyhow::Result<PathBuf> {
tokio::fs::create_dir_all(&dir).await?;
Ok(dir)
}
Loading

0 comments on commit 6a056fe

Please sign in to comment.