2
2
// - Add logs.
3
3
// - Remove the temporary directory in all cases (error or success).
4
4
use std:: path:: Path ;
5
+ use std:: process:: Command ;
5
6
use std:: { env, fmt, path:: PathBuf } ;
6
7
7
8
use anyhow:: { anyhow, Context } ;
@@ -23,6 +24,14 @@ const PRERELEASE_DISTRIBUTION_TAG: &str = "prerelease";
23
24
24
25
const CARDANO_DISTRIBUTION_TEMP_DIR : & str = "cardano-node-distribution-tmp" ;
25
26
27
+ const SNAPSHOT_CONVERTER_BIN_DIR : & str = "bin" ;
28
+ const SNAPSHOT_CONVERTER_BIN_NAME_UNIX : & str = "snapshot-converter" ;
29
+ const SNAPSHOT_CONVERTER_BIN_NAME_WINDOWS : & str = "snapshot-converter.exe" ;
30
+ const SNAPSHOT_CONVERTER_CONFIG_DIR : & str = "share" ;
31
+ const SNAPSHOT_CONVERTER_CONFIG_FILE : & str = "config.json" ;
32
+
33
+ const LEDGER_DIR : & str = "ledger" ;
34
+
26
35
#[ derive( Debug , Clone , ValueEnum ) ]
27
36
enum UTxOHDFlavor {
28
37
#[ clap( name = "Legacy" ) ]
@@ -40,6 +49,23 @@ impl fmt::Display for UTxOHDFlavor {
40
49
}
41
50
}
42
51
52
+ #[ derive( Debug , Clone , ValueEnum ) ]
53
+ enum CardanoNetwork {
54
+ Preview ,
55
+ Preprod ,
56
+ Mainnet ,
57
+ }
58
+
59
+ impl fmt:: Display for CardanoNetwork {
60
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
61
+ match self {
62
+ Self :: Preview => write ! ( f, "preview" ) ,
63
+ Self :: Preprod => write ! ( f, "preprod" ) ,
64
+ Self :: Mainnet => write ! ( f, "mainnet" ) ,
65
+ }
66
+ }
67
+ }
68
+
43
69
/// Clap command to convert a restored `InMemory` Mithril snapshot to another flavor.
44
70
#[ derive( Parser , Debug , Clone ) ]
45
71
pub struct SnapshotConverterCommand {
@@ -53,6 +79,10 @@ pub struct SnapshotConverterCommand {
53
79
#[ clap( long) ]
54
80
cardano_node_version : String ,
55
81
82
+ /// Cardano network.
83
+ #[ clap( long) ]
84
+ cardano_network : CardanoNetwork ,
85
+
56
86
/// UTxO-HD flavor to convert the ledger snapshot to.
57
87
#[ clap( long) ]
58
88
utxo_hd_flavor : UTxOHDFlavor ,
@@ -88,6 +118,19 @@ impl SnapshotConverterCommand {
88
118
)
89
119
} ) ?;
90
120
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
+
91
134
Ok ( ( ) )
92
135
}
93
136
@@ -141,6 +184,120 @@ impl SnapshotConverterCommand {
141
184
142
185
Ok ( ( ) )
143
186
}
187
+
188
+ // 1. Find the `snapshot-converter` binary
189
+ // 2. Find the configuration file
190
+ // 3. Find the less recent ledger snapshot in the db directory
191
+ // 4. Copy the ledger snapshot to the distribution directory (backup)
192
+ // 5. Run the `snapshot-converter` command with the appropriate arguments
193
+ // - Legacy: snapshot-converter Mem <PATH-IN> Legacy <PATH-OUT> cardano --config <CONFIG>
194
+ // - LMDB: snapshot-converter Mem <PATH-IN> LMDB <PATH-OUT> cardano --config <CONFIG>
195
+ fn convert_ledger_snapshot (
196
+ db_dir : & Path ,
197
+ cardano_network : & CardanoNetwork ,
198
+ distribution_dir : & Path ,
199
+ utxo_hd_flavor : & UTxOHDFlavor ,
200
+ ) -> MithrilResult < ( ) > {
201
+ let snapshot_converter_bin_path =
202
+ Self :: get_snapshot_converter_binary_path ( distribution_dir, env:: consts:: OS ) ?;
203
+ // TODO: check if this configuration file is enough to convert the snapshot.
204
+ let config_path =
205
+ Self :: get_snapshot_converter_config_path ( distribution_dir, cardano_network) ;
206
+
207
+ let ( slot_number, ledger_snapshot_path) = Self :: find_less_recent_ledger_snapshot ( db_dir) ?;
208
+ let snapshot_backup_path = distribution_dir. join ( ledger_snapshot_path. file_name ( ) . unwrap ( ) ) ;
209
+ std:: fs:: copy ( ledger_snapshot_path. clone ( ) , snapshot_backup_path. clone ( ) ) ?;
210
+
211
+ let snapshot_converted_output_path = distribution_dir
212
+ . join ( slot_number. to_string ( ) )
213
+ . join ( utxo_hd_flavor. to_string ( ) . to_lowercase ( ) ) ;
214
+
215
+ // TODO: verify if the paths are correct, the command needs relative path.
216
+ Command :: new ( snapshot_converter_bin_path. clone ( ) )
217
+ . arg ( "Mem" )
218
+ . arg ( snapshot_backup_path)
219
+ . arg ( utxo_hd_flavor. to_string ( ) )
220
+ . arg ( snapshot_converted_output_path)
221
+ . arg ( "cardano" )
222
+ . arg ( "--config" )
223
+ . arg ( config_path)
224
+ . status ( )
225
+ . with_context ( || {
226
+ format ! (
227
+ "Failed to get help of snapshot-converter: {}" ,
228
+ snapshot_converter_bin_path. display( )
229
+ )
230
+ } ) ?;
231
+
232
+ println ! (
233
+ "Snapshot converter executed successfully. The ledger snapshot has been converted to {} flavor in {}." ,
234
+ utxo_hd_flavor,
235
+ distribution_dir. display( )
236
+ ) ;
237
+ Ok ( ( ) )
238
+ }
239
+
240
+ fn get_snapshot_converter_binary_path (
241
+ distribution_dir : & Path ,
242
+ target_os : & str ,
243
+ ) -> MithrilResult < PathBuf > {
244
+ let base_path = distribution_dir. join ( SNAPSHOT_CONVERTER_BIN_DIR ) ;
245
+
246
+ let binary_name = match target_os {
247
+ "linux" | "macos" => SNAPSHOT_CONVERTER_BIN_NAME_UNIX ,
248
+ "windows" => SNAPSHOT_CONVERTER_BIN_NAME_WINDOWS ,
249
+ _ => return Err ( anyhow ! ( "Unsupported platform: {}" , target_os) ) ,
250
+ } ;
251
+
252
+ Ok ( base_path. join ( binary_name) )
253
+ }
254
+
255
+ fn get_snapshot_converter_config_path (
256
+ distribution_dir : & Path ,
257
+ network : & CardanoNetwork ,
258
+ ) -> PathBuf {
259
+ distribution_dir
260
+ . join ( SNAPSHOT_CONVERTER_CONFIG_DIR )
261
+ . join ( network. to_string ( ) )
262
+ . join ( SNAPSHOT_CONVERTER_CONFIG_FILE )
263
+ }
264
+
265
+ // TODO: quick dirty code to go further in `convert_ledger_snapshot` function, must be enhanced and tested.
266
+ fn find_less_recent_ledger_snapshot ( db_dir : & Path ) -> MithrilResult < ( u64 , PathBuf ) > {
267
+ let ledger_dir = db_dir. join ( LEDGER_DIR ) ;
268
+
269
+ let entries = std:: fs:: read_dir ( & ledger_dir) . with_context ( || {
270
+ format ! ( "Failed to read ledger directory: {}" , ledger_dir. display( ) )
271
+ } ) ?;
272
+
273
+ let mut min_slot: Option < ( u64 , PathBuf ) > = None ;
274
+
275
+ for entry in entries {
276
+ let entry = entry?;
277
+ let file_name = entry. file_name ( ) ;
278
+ let file_name_str = file_name. to_str ( ) . unwrap ( ) ;
279
+
280
+ let slot = match file_name_str. parse :: < u64 > ( ) {
281
+ Ok ( n) => n,
282
+ Err ( _) => continue ,
283
+ } ;
284
+
285
+ let path = entry. path ( ) ;
286
+ if path. is_dir ( ) {
287
+ match & min_slot {
288
+ Some ( ( current_min, _) ) if * current_min <= slot => { }
289
+ _ => min_slot = Some ( ( slot, path) ) ,
290
+ }
291
+ }
292
+ }
293
+
294
+ min_slot. ok_or_else ( || {
295
+ anyhow ! (
296
+ "No valid ledger snapshot found in: {}" ,
297
+ ledger_dir. display( )
298
+ )
299
+ } )
300
+ }
144
301
}
145
302
146
303
#[ cfg( test) ]
@@ -257,4 +414,115 @@ mod tests {
257
414
. await
258
415
. unwrap ( ) ;
259
416
}
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
+ }
260
528
}
0 commit comments