Skip to content

Commit ff07c4f

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

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};
@@ -22,6 +23,14 @@ const PRERELEASE_DISTRIBUTION_TAG: &str = "prerelease";
2223

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

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

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

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

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

@@ -140,6 +183,120 @@ impl SnapshotConverterCommand {
140183

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

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

0 commit comments

Comments
 (0)