11use bech32:: { self , FromBase32 } ;
2- use chain_addr:: { Address , Kind } ;
2+ use chain_addr:: { Address , AddressReadable , Kind } ;
33use chain_crypto:: { Ed25519 , PublicKey } ;
44use chain_impl_mockchain:: account;
55
@@ -10,15 +10,21 @@ use chain_impl_mockchain::{
1010 block:: Block , chaintypes:: HeaderId , fragment:: Fragment , transaction:: InputEnum ,
1111} ;
1212
13- use tracing:: error;
13+ use tracing:: { error, info } ;
1414
1515use jormungandr_lib:: interfaces:: AccountIdentifier ;
1616
1717const MAIN_TAG : & str = "HEAD" ;
1818
19- use std:: path:: Path ;
19+ use std:: {
20+ collections:: { HashMap , HashSet } ,
21+ error,
22+ fs:: { read_to_string, File } ,
23+ io:: BufWriter ,
24+ path:: { Path , PathBuf } ,
25+ } ;
2026
21- use crate :: offline:: Vote ;
27+ use crate :: offline:: { extract_fragments_from_storage , Vote } ;
2228
2329#[ derive( Debug , thiserror:: Error ) ]
2430pub enum Error {
@@ -177,15 +183,147 @@ pub fn find_vote(jormungandr_database: &Path, voting_key: String) -> Result<Vec<
177183 Ok ( votes)
178184}
179185
186+ /// Collect all voting keys in ca and 0x format and write to files
187+ pub fn all_voters (
188+ jormungandr_database : & Path ,
189+ ) -> Result < ( HashSet < std:: string:: String > , HashSet < std:: string:: String > ) , Box < dyn error:: Error > > {
190+ let fragments = extract_fragments_from_storage ( jormungandr_database) ?;
191+
192+ let mut unique_voters_ca = HashSet :: new ( ) ;
193+ let mut unique_voters_0x = HashSet :: new ( ) ;
194+
195+ for fragment in fragments {
196+ if let Fragment :: VoteCast ( tx) = fragment. clone ( ) {
197+ let input = tx. as_slice ( ) . inputs ( ) . iter ( ) . next ( ) . unwrap ( ) . to_enum ( ) ;
198+ let caster = if let InputEnum :: AccountInput ( account_id, _value) = input {
199+ AccountIdentifier :: from ( account_id) . into_address ( Discrimination :: Production , "ca" )
200+ } else {
201+ error ! ( "Corrupted fragment {:?}" , fragment) ;
202+ continue ;
203+ } ;
204+
205+ unique_voters_ca. insert ( caster. to_string ( ) . clone ( ) ) ;
206+
207+ let voting_key_61824_format = AddressReadable :: from_string ( "ca" , & caster. to_string ( ) )
208+ . expect ( "infallible" )
209+ . to_address ( ) ;
210+
211+ let voting_key = voting_key_61824_format
212+ . public_key ( )
213+ . expect ( "infallible" )
214+ . to_string ( ) ;
215+ unique_voters_0x. insert ( voting_key) ;
216+ }
217+ }
218+
219+ info ! ( "unique voters ca {:?}" , unique_voters_ca. len( ) ) ;
220+ info ! ( "unique voters 0x {:?}" , unique_voters_0x. len( ) ) ;
221+
222+ Ok ( ( unique_voters_ca, unique_voters_0x) )
223+ }
224+
225+ /// convert keys from ca to 0x and vice versa
226+ pub fn convert_key_formats ( voting_key : String ) -> Result < String , Box < dyn error:: Error > > {
227+ if voting_key. starts_with ( "ca" ) {
228+ let voting_key_61824_format = AddressReadable :: from_string ( "ca" , & voting_key) ?. to_address ( ) ;
229+
230+ let voting_key = voting_key_61824_format
231+ . public_key ( )
232+ . expect ( "addr to pub key is infallible" )
233+ . to_string ( ) ;
234+
235+ Ok ( voting_key)
236+ } else {
237+ // we need to convert this to our internal key representation
238+ let decoded_voting_key = hex:: decode ( voting_key) ?;
239+ let voting_key: PublicKey < Ed25519 > = PublicKey :: from_binary ( & decoded_voting_key) ?;
240+ let addr = Address ( Discrimination :: Production , Kind :: Single ( voting_key. clone ( ) ) ) ;
241+ let addr_readable = AddressReadable :: from_address ( "ca" , & addr) ;
242+
243+ Ok ( addr_readable. to_string ( ) )
244+ }
245+ }
246+
247+ /// read voter keys from file
248+ pub fn read_lines ( filename : & str ) -> Result < Vec < String > , Box < dyn error:: Error > > {
249+ let mut result = Vec :: new ( ) ;
250+
251+ for line in read_to_string ( filename) ?. lines ( ) {
252+ result. push ( line. to_string ( ) )
253+ }
254+
255+ Ok ( result)
256+ }
257+
258+ /// check key history of multiple keys and write metadata to file
259+ pub fn batch_key_check (
260+ jormungandr_database : & Path ,
261+ key_file : String ,
262+ ) -> Result < ( ) , Box < dyn error:: Error > > {
263+ let mut flagged_keys = HashMap :: new ( ) ;
264+
265+ let keys = read_lines ( & key_file) ?;
266+
267+ for key in keys {
268+ let voting_key_61824_format = AddressReadable :: from_string ( "ca" , & key)
269+ . expect ( "infallible" )
270+ . to_address ( ) ;
271+
272+ let voting_key = voting_key_61824_format
273+ . public_key ( )
274+ . expect ( "infallible" )
275+ . to_string ( ) ;
276+
277+ let votes = find_vote ( jormungandr_database, voting_key) ?;
278+
279+ flagged_keys. insert ( key. clone ( ) , votes. clone ( ) ) ;
280+
281+ info ! ( "Inserted: key: {} vote: {:?}" , key, votes) ;
282+ }
283+
284+ let flagged_file = PathBuf :: from ( "/tmp/inspect" ) . with_extension ( "flag_keys.json" ) ;
285+
286+ let file = File :: options ( )
287+ . write ( true )
288+ . create ( true )
289+ . truncate ( true )
290+ . open ( flagged_file. clone ( ) ) ?;
291+ let writer = BufWriter :: new ( file) ;
292+
293+ serde_json:: to_writer_pretty ( writer, & flagged_keys) ?;
294+
295+ info ! ( "flagged keys and metadata saved here {:?}" , flagged_file) ;
296+
297+ Ok ( ( ) )
298+ }
299+
180300#[ cfg( test) ]
181301mod tests {
302+
182303 use std:: path:: PathBuf ;
183304
184305 use chain_addr:: { Address , AddressReadable , Discrimination , Kind } ;
185306 use chain_crypto:: { Ed25519 , PublicKey } ;
186307
187308 use crate :: find:: find_vote;
188309
310+ use super :: convert_key_formats;
311+
312+ #[ test]
313+ fn test_key_conversion ( ) {
314+ let voting_key_0x =
315+ "f895a6a7f44dd15f7700c60456c93793b1241fdd1c77bbb6cd3fc8a4d24c8c1b" . to_string ( ) ;
316+
317+ let converted_key = convert_key_formats ( voting_key_0x. clone ( ) ) . unwrap ( ) ;
318+
319+ let voting_key_ca =
320+ "ca1q0uftf4873xazhmhqrrqg4kfx7fmzfqlm5w80wake5lu3fxjfjxpk6wv3f7" . to_string ( ) ;
321+
322+ assert_eq ! ( converted_key, voting_key_ca, ) ;
323+
324+ assert_eq ! ( convert_key_formats( voting_key_ca) . unwrap( ) , voting_key_0x) ;
325+ }
326+
189327 #[ test]
190328 #[ ignore]
191329 fn test_account_parser ( ) {
0 commit comments