Skip to content

Commit 1cb0c25

Browse files
committed
[WIP]feat(client-cli): execute the snapshot-converter binary
1 parent 56b95d7 commit 1cb0c25

File tree

1 file changed

+268
-0
lines changed

1 file changed

+268
-0
lines changed

mithril-client-cli/src/commands/tools/snapshot_converter.rs

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// - Add logs.
33
// - Remove the temporary directory in all cases (error or success).
44
use std::path::Path;
5+
use std::process::Command;
56
use std::{env, fmt, path::PathBuf};
67

78
use anyhow::{anyhow, Context};
@@ -21,6 +22,14 @@ const PRERELEASE_DISTRIBUTION_TAG: &str = "prerelease";
2122

2223
const CARDANO_DISTRIBUTION_TEMP_DIR: &str = "cardano-node-distribution-tmp";
2324

25+
const SNAPSHOT_CONVERTER_BIN_DIR: &str = "bin";
26+
const SNAPSHOT_CONVERTER_BIN_NAME_UNIX: &str = "snapshot-converter";
27+
const SNAPSHOT_CONVERTER_BIN_NAME_WINDOWS: &str = "snapshot-converter.exe";
28+
const SNAPSHOT_CONVERTER_CONFIG_DIR: &str = "share";
29+
const SNAPSHOT_CONVERTER_CONFIG_FILE: &str = "config.json";
30+
31+
const LEDGER_DIR: &str = "ledger";
32+
2433
#[derive(Debug, Clone, ValueEnum)]
2534
enum UTxOHDFlavor {
2635
#[clap(name = "Legacy")]
@@ -38,6 +47,23 @@ impl fmt::Display for UTxOHDFlavor {
3847
}
3948
}
4049

50+
#[derive(Debug, Clone, ValueEnum)]
51+
enum CardanoNetwork {
52+
Preview,
53+
Preprod,
54+
Mainnet,
55+
}
56+
57+
impl fmt::Display for CardanoNetwork {
58+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59+
match self {
60+
Self::Preview => write!(f, "preview"),
61+
Self::Preprod => write!(f, "preprod"),
62+
Self::Mainnet => write!(f, "mainnet"),
63+
}
64+
}
65+
}
66+
4167
/// Clap command to convert a restored `InMemory` Mithril snapshot to another flavor.
4268
#[derive(Parser, Debug, Clone)]
4369
pub struct SnapshotConverterCommand {
@@ -51,6 +77,10 @@ pub struct SnapshotConverterCommand {
5177
#[clap(long)]
5278
cardano_node_version: String,
5379

80+
/// Cardano network.
81+
#[clap(long)]
82+
cardano_network: CardanoNetwork,
83+
5484
/// UTxO-HD flavor to convert the ledger snapshot to.
5585
#[clap(long)]
5686
utxo_hd_flavor: UTxOHDFlavor,
@@ -86,6 +116,19 @@ impl SnapshotConverterCommand {
86116
)
87117
})?;
88118

119+
Self::convert_ledger_snapshot(
120+
&self.db_directory,
121+
&self.cardano_network,
122+
&distribution_temp_dir,
123+
&self.utxo_hd_flavor,
124+
)
125+
.with_context(|| {
126+
format!(
127+
"Failed to convert ledger snapshot to flavor: {}",
128+
self.utxo_hd_flavor
129+
)
130+
})?;
131+
89132
Ok(())
90133
}
91134

@@ -138,6 +181,120 @@ impl SnapshotConverterCommand {
138181

139182
Ok(())
140183
}
184+
185+
// 1. Find the `snapshot-converter` binary
186+
// 2. Find the configuration file
187+
// 3. Find the less recent ledger snapshot in the db directory
188+
// 4. Copy the ledger snapshot to the distribution directory (backup)
189+
// 5. Run the `snapshot-converter` command with the appropriate arguments
190+
// - Legacy: snapshot-converter Mem <PATH-IN> Legacy <PATH-OUT> cardano --config <CONFIG>
191+
// - LMDB: snapshot-converter Mem <PATH-IN> LMDB <PATH-OUT> cardano --config <CONFIG>
192+
fn convert_ledger_snapshot(
193+
db_dir: &Path,
194+
cardano_network: &CardanoNetwork,
195+
distribution_dir: &Path,
196+
utxo_hd_flavor: &UTxOHDFlavor,
197+
) -> MithrilResult<()> {
198+
let snapshot_converter_bin_path =
199+
Self::get_snapshot_converter_binary_path(distribution_dir, env::consts::OS)?;
200+
// TODO: check if this configuration file is enough to convert the snapshot.
201+
let config_path =
202+
Self::get_snapshot_converter_config_path(distribution_dir, cardano_network);
203+
204+
let (slot_number, ledger_snapshot_path) = Self::find_less_recent_ledger_snapshot(db_dir)?;
205+
let snapshot_backup_path = distribution_dir.join(ledger_snapshot_path.file_name().unwrap());
206+
std::fs::copy(ledger_snapshot_path.clone(), snapshot_backup_path.clone())?;
207+
208+
let snapshot_converted_output_path = distribution_dir
209+
.join(slot_number.to_string())
210+
.join(utxo_hd_flavor.to_string().to_lowercase());
211+
212+
// TODO: verify if the paths are correct, the command needs relative path.
213+
Command::new(snapshot_converter_bin_path.clone())
214+
.arg("Mem")
215+
.arg(snapshot_backup_path)
216+
.arg(utxo_hd_flavor.to_string())
217+
.arg(snapshot_converted_output_path)
218+
.arg("cardano")
219+
.arg("--config")
220+
.arg(config_path)
221+
.status()
222+
.with_context(|| {
223+
format!(
224+
"Failed to get help of snapshot-converter: {}",
225+
snapshot_converter_bin_path.display()
226+
)
227+
})?;
228+
229+
println!(
230+
"Snapshot converter executed successfully. The ledger snapshot has been converted to {} flavor in {}.",
231+
utxo_hd_flavor,
232+
distribution_dir.display()
233+
);
234+
Ok(())
235+
}
236+
237+
fn get_snapshot_converter_binary_path(
238+
distribution_dir: &Path,
239+
target_os: &str,
240+
) -> MithrilResult<PathBuf> {
241+
let base_path = distribution_dir.join(SNAPSHOT_CONVERTER_BIN_DIR);
242+
243+
let binary_name = match target_os {
244+
"linux" | "macos" => SNAPSHOT_CONVERTER_BIN_NAME_UNIX,
245+
"windows" => SNAPSHOT_CONVERTER_BIN_NAME_WINDOWS,
246+
_ => return Err(anyhow!("Unsupported platform: {}", target_os)),
247+
};
248+
249+
Ok(base_path.join(binary_name))
250+
}
251+
252+
fn get_snapshot_converter_config_path(
253+
distribution_dir: &Path,
254+
network: &CardanoNetwork,
255+
) -> PathBuf {
256+
distribution_dir
257+
.join(SNAPSHOT_CONVERTER_CONFIG_DIR)
258+
.join(network.to_string())
259+
.join(SNAPSHOT_CONVERTER_CONFIG_FILE)
260+
}
261+
262+
// TODO: quick dirty code to go further in `convert_ledger_snapshot` function, must be enhanced and tested.
263+
fn find_less_recent_ledger_snapshot(db_dir: &Path) -> MithrilResult<(u64, PathBuf)> {
264+
let ledger_dir = db_dir.join(LEDGER_DIR);
265+
266+
let entries = std::fs::read_dir(&ledger_dir).with_context(|| {
267+
format!("Failed to read ledger directory: {}", ledger_dir.display())
268+
})?;
269+
270+
let mut min_slot: Option<(u64, PathBuf)> = None;
271+
272+
for entry in entries {
273+
let entry = entry?;
274+
let file_name = entry.file_name();
275+
let file_name_str = file_name.to_str().unwrap();
276+
277+
let slot = match file_name_str.parse::<u64>() {
278+
Ok(n) => n,
279+
Err(_) => continue,
280+
};
281+
282+
let path = entry.path();
283+
if path.is_dir() {
284+
match &min_slot {
285+
Some((current_min, _)) if *current_min <= slot => {}
286+
_ => min_slot = Some((slot, path)),
287+
}
288+
}
289+
}
290+
291+
min_slot.ok_or_else(|| {
292+
anyhow!(
293+
"No valid ledger snapshot found in: {}",
294+
ledger_dir.display()
295+
)
296+
})
297+
}
141298
}
142299

143300
#[cfg(test)]
@@ -257,4 +414,115 @@ mod tests {
257414
.await
258415
.unwrap();
259416
}
417+
418+
#[test]
419+
fn get_snapshot_converter_binary_path_linux() {
420+
let distribution_dir = PathBuf::from("/path/to/distribution");
421+
422+
let binary_path = SnapshotConverterCommand::get_snapshot_converter_binary_path(
423+
&distribution_dir,
424+
"linux",
425+
)
426+
.unwrap();
427+
428+
assert_eq!(
429+
binary_path,
430+
distribution_dir
431+
.join(SNAPSHOT_CONVERTER_BIN_DIR)
432+
.join(SNAPSHOT_CONVERTER_BIN_NAME_UNIX)
433+
);
434+
}
435+
436+
#[test]
437+
fn get_snapshot_converter_binary_path_macos() {
438+
let distribution_dir = PathBuf::from("/path/to/distribution");
439+
440+
let binary_path = SnapshotConverterCommand::get_snapshot_converter_binary_path(
441+
&distribution_dir,
442+
"macos",
443+
)
444+
.unwrap();
445+
446+
assert_eq!(
447+
binary_path,
448+
distribution_dir
449+
.join(SNAPSHOT_CONVERTER_BIN_DIR)
450+
.join(SNAPSHOT_CONVERTER_BIN_NAME_UNIX)
451+
);
452+
}
453+
454+
#[test]
455+
fn get_snapshot_converter_binary_path_windows() {
456+
let distribution_dir = PathBuf::from("/path/to/distribution");
457+
458+
let binary_path = SnapshotConverterCommand::get_snapshot_converter_binary_path(
459+
&distribution_dir,
460+
"windows",
461+
)
462+
.unwrap();
463+
464+
assert_eq!(
465+
binary_path,
466+
distribution_dir
467+
.join(SNAPSHOT_CONVERTER_BIN_DIR)
468+
.join(SNAPSHOT_CONVERTER_BIN_NAME_WINDOWS)
469+
);
470+
}
471+
472+
#[test]
473+
fn get_snapshot_converter_config_path_mainnet() {
474+
let distribution_dir = PathBuf::from("/path/to/distribution");
475+
let network = CardanoNetwork::Mainnet;
476+
477+
let config_path = SnapshotConverterCommand::get_snapshot_converter_config_path(
478+
&distribution_dir,
479+
&network,
480+
);
481+
482+
assert_eq!(
483+
config_path,
484+
distribution_dir
485+
.join(SNAPSHOT_CONVERTER_CONFIG_DIR)
486+
.join(network.to_string())
487+
.join(SNAPSHOT_CONVERTER_CONFIG_FILE)
488+
);
489+
}
490+
491+
#[test]
492+
fn get_snapshot_converter_config_path_preprod() {
493+
let distribution_dir = PathBuf::from("/path/to/distribution");
494+
let network = CardanoNetwork::Preprod;
495+
496+
let config_path = SnapshotConverterCommand::get_snapshot_converter_config_path(
497+
&distribution_dir,
498+
&network,
499+
);
500+
501+
assert_eq!(
502+
config_path,
503+
distribution_dir
504+
.join(SNAPSHOT_CONVERTER_CONFIG_DIR)
505+
.join(network.to_string())
506+
.join(SNAPSHOT_CONVERTER_CONFIG_FILE)
507+
);
508+
}
509+
510+
#[test]
511+
fn get_snapshot_converter_config_path_preview() {
512+
let distribution_dir = PathBuf::from("/path/to/distribution");
513+
let network = CardanoNetwork::Preview;
514+
515+
let config_path = SnapshotConverterCommand::get_snapshot_converter_config_path(
516+
&distribution_dir,
517+
&network,
518+
);
519+
520+
assert_eq!(
521+
config_path,
522+
distribution_dir
523+
.join(SNAPSHOT_CONVERTER_CONFIG_DIR)
524+
.join(network.to_string())
525+
.join(SNAPSHOT_CONVERTER_CONFIG_FILE)
526+
);
527+
}
260528
}

0 commit comments

Comments
 (0)