22// - Add logs. 
33// - Remove the temporary directory in all cases (error or success). 
44use  std:: path:: Path ; 
5+ use  std:: process:: Command ; 
56use  std:: { env,  fmt,  path:: PathBuf } ; 
67
78use  anyhow:: { anyhow,  Context } ; 
@@ -21,6 +22,14 @@ const PRERELEASE_DISTRIBUTION_TAG: &str = "prerelease";
2122
2223const  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 ) ]  
2534enum  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 ) ]  
4369pub  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