11use clap:: { App , Arg , ArgMatches } ;
22use environment:: Environment ;
33use slashing_protection:: {
4- interchange:: Interchange , InterchangeImportOutcome , SlashingDatabase ,
4+ interchange:: Interchange , InterchangeError , InterchangeImportOutcome , SlashingDatabase ,
55 SLASHING_PROTECTION_FILENAME ,
66} ;
77use std:: fs:: File ;
@@ -15,6 +15,8 @@ pub const EXPORT_CMD: &str = "export";
1515pub const IMPORT_FILE_ARG : & str = "IMPORT-FILE" ;
1616pub const EXPORT_FILE_ARG : & str = "EXPORT-FILE" ;
1717
18+ pub const MINIFY_FLAG : & str = "minify" ;
19+
1820pub fn cli_app < ' a , ' b > ( ) -> App < ' a , ' b > {
1921 App :: new ( CMD )
2022 . about ( "Import or export slashing protection data to or from another client" )
@@ -26,6 +28,17 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
2628 . takes_value ( true )
2729 . value_name ( "FILE" )
2830 . help ( "The slashing protection interchange file to import (.json)" ) ,
31+ )
32+ . arg (
33+ Arg :: with_name ( MINIFY_FLAG )
34+ . long ( MINIFY_FLAG )
35+ . takes_value ( true )
36+ . default_value ( "true" )
37+ . possible_values ( & [ "false" , "true" ] )
38+ . help (
39+ "Minify the input file before processing. This is *much* faster, \
40+ but will not detect slashable data in the input.",
41+ ) ,
2942 ) ,
3043 )
3144 . subcommand (
@@ -36,6 +49,17 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
3649 . takes_value ( true )
3750 . value_name ( "FILE" )
3851 . help ( "The filename to export the interchange file to" ) ,
52+ )
53+ . arg (
54+ Arg :: with_name ( MINIFY_FLAG )
55+ . long ( MINIFY_FLAG )
56+ . takes_value ( true )
57+ . default_value ( "false" )
58+ . possible_values ( & [ "false" , "true" ] )
59+ . help (
60+ "Minify the output file. This will make it smaller and faster to \
61+ import, but not faster to generate.",
62+ ) ,
3963 ) ,
4064 )
4165}
@@ -64,6 +88,7 @@ pub fn cli_run<T: EthSpec>(
6488 match matches. subcommand ( ) {
6589 ( IMPORT_CMD , Some ( matches) ) => {
6690 let import_filename: PathBuf = clap_utils:: parse_required ( & matches, IMPORT_FILE_ARG ) ?;
91+ let minify: bool = clap_utils:: parse_required ( & matches, MINIFY_FLAG ) ?;
6792 let import_file = File :: open ( & import_filename) . map_err ( |e| {
6893 format ! (
6994 "Unable to open import file at {}: {:?}" ,
@@ -72,8 +97,18 @@ pub fn cli_run<T: EthSpec>(
7297 )
7398 } ) ?;
7499
75- let interchange = Interchange :: from_json_reader ( & import_file)
100+ eprint ! ( "Loading JSON file into memory & deserializing" ) ;
101+ let mut interchange = Interchange :: from_json_reader ( & import_file)
76102 . map_err ( |e| format ! ( "Error parsing file for import: {:?}" , e) ) ?;
103+ eprintln ! ( " [done]." ) ;
104+
105+ if minify {
106+ eprint ! ( "Minifying input file for faster loading" ) ;
107+ interchange = interchange
108+ . minify ( )
109+ . map_err ( |e| format ! ( "Minification failed: {:?}" , e) ) ?;
110+ eprintln ! ( " [done]." ) ;
111+ }
77112
78113 let slashing_protection_database =
79114 SlashingDatabase :: open_or_create ( & slashing_protection_db_path) . map_err ( |e| {
@@ -84,16 +119,6 @@ pub fn cli_run<T: EthSpec>(
84119 )
85120 } ) ?;
86121
87- let outcomes = slashing_protection_database
88- . import_interchange_info ( interchange, genesis_validators_root)
89- . map_err ( |e| {
90- format ! (
91- "Error during import: {:?}\n \
92- IT IS NOT SAFE TO START VALIDATING",
93- e
94- )
95- } ) ?;
96-
97122 let display_slot = |slot : Option < Slot > | {
98123 slot. map_or ( "none" . to_string ( ) , |slot| format ! ( "{}" , slot. as_u64( ) ) )
99124 } ;
@@ -105,48 +130,77 @@ pub fn cli_run<T: EthSpec>(
105130 ( source, target) => format ! ( "{}=>{}" , display_epoch( source) , display_epoch( target) ) ,
106131 } ;
107132
108- let mut num_failed = 0 ;
109-
110- for outcome in & outcomes {
111- match outcome {
112- InterchangeImportOutcome :: Success { pubkey, summary } => {
113- eprintln ! ( "- {:?} SUCCESS min block: {}, max block: {}, min attestation: {}, max attestation: {}" ,
114- pubkey,
115- display_slot( summary. min_block_slot) ,
116- display_slot( summary. max_block_slot) ,
117- display_attestation( summary. min_attestation_source, summary. min_attestation_target) ,
118- display_attestation( summary. max_attestation_source,
119- summary. max_attestation_target) ,
120- ) ;
133+ match slashing_protection_database
134+ . import_interchange_info ( interchange, genesis_validators_root)
135+ {
136+ Ok ( outcomes) => {
137+ eprintln ! ( "All records imported successfully:" ) ;
138+ for outcome in & outcomes {
139+ match outcome {
140+ InterchangeImportOutcome :: Success { pubkey, summary } => {
141+ eprintln ! ( "- {:?}" , pubkey) ;
142+ eprintln ! (
143+ " - min block: {}" ,
144+ display_slot( summary. min_block_slot)
145+ ) ;
146+ eprintln ! (
147+ " - min attestation: {}" ,
148+ display_attestation(
149+ summary. min_attestation_source,
150+ summary. min_attestation_target
151+ )
152+ ) ;
153+ eprintln ! (
154+ " - max attestation: {}" ,
155+ display_attestation(
156+ summary. max_attestation_source,
157+ summary. max_attestation_target
158+ )
159+ ) ;
160+ }
161+ InterchangeImportOutcome :: Failure { pubkey, error } => {
162+ panic ! (
163+ "import should be atomic, but key {:?} was imported despite error: {:?}" ,
164+ pubkey, error
165+ ) ;
166+ }
167+ }
121168 }
122- InterchangeImportOutcome :: Failure { pubkey, error } => {
123- eprintln ! ( "- {:?} ERROR: {:?}" , pubkey, error) ;
124- num_failed += 1 ;
169+ }
170+ Err ( InterchangeError :: AtomicBatchAborted ( outcomes) ) => {
171+ eprintln ! ( "ERROR, slashable data in input:" ) ;
172+ for outcome in & outcomes {
173+ if let InterchangeImportOutcome :: Failure { pubkey, error } = outcome {
174+ eprintln ! ( "- {:?}" , pubkey) ;
175+ eprintln ! ( " - error: {:?}" , error) ;
176+ }
125177 }
178+ return Err (
179+ "ERROR: import aborted due to slashable data, see above.\n \
180+ Please see https://lighthouse-book.sigmaprime.io/slashing-protection.html#slashable-data-in-import\n \
181+ IT IS NOT SAFE TO START VALIDATING". to_string ( )
182+ ) ;
183+ }
184+ Err ( e) => {
185+ return Err ( format ! (
186+ "Fatal error during import: {:?}\n \
187+ IT IS NOT SAFE TO START VALIDATING",
188+ e
189+ ) ) ;
126190 }
127191 }
128192
129- if num_failed == 0 {
130- eprintln ! ( "Import completed successfully." ) ;
131- eprintln ! (
132- "Please double-check that the minimum and maximum blocks and slots above \
133- match your expectations."
134- ) ;
135- } else {
136- eprintln ! (
137- "WARNING: history was NOT imported for {} of {} records" ,
138- num_failed,
139- outcomes. len( )
140- ) ;
141- eprintln ! ( "IT IS NOT SAFE TO START VALIDATING" ) ;
142- eprintln ! ( "Please see https://lighthouse-book.sigmaprime.io/slashing-protection.html#slashable-data-in-import" ) ;
143- return Err ( "Partial import" . to_string ( ) ) ;
144- }
193+ eprintln ! ( "Import completed successfully." ) ;
194+ eprintln ! (
195+ "Please double-check that the minimum and maximum blocks and attestations above \
196+ match your expectations."
197+ ) ;
145198
146199 Ok ( ( ) )
147200 }
148201 ( EXPORT_CMD , Some ( matches) ) => {
149202 let export_filename: PathBuf = clap_utils:: parse_required ( & matches, EXPORT_FILE_ARG ) ?;
203+ let minify: bool = clap_utils:: parse_required ( & matches, MINIFY_FLAG ) ?;
150204
151205 if !slashing_protection_db_path. exists ( ) {
152206 return Err ( format ! (
@@ -164,10 +218,17 @@ pub fn cli_run<T: EthSpec>(
164218 )
165219 } ) ?;
166220
167- let interchange = slashing_protection_database
221+ let mut interchange = slashing_protection_database
168222 . export_interchange_info ( genesis_validators_root)
169223 . map_err ( |e| format ! ( "Error during export: {:?}" , e) ) ?;
170224
225+ if minify {
226+ eprintln ! ( "Minifying output file" ) ;
227+ interchange = interchange
228+ . minify ( )
229+ . map_err ( |e| format ! ( "Unable to minify output: {:?}" , e) ) ?;
230+ }
231+
171232 let output_file = File :: create ( export_filename)
172233 . map_err ( |e| format ! ( "Error creating output file: {:?}" , e) ) ?;
173234
0 commit comments