Skip to content

Commit 02b9448

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

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,
@@ -88,6 +118,19 @@ impl SnapshotConverterCommand {
88118
)
89119
})?;
90120

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

@@ -128,6 +171,120 @@ impl SnapshotConverterCommand {
128171

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

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

0 commit comments

Comments
 (0)