@@ -491,6 +491,38 @@ impl BlobFactory {
491491// Blob file storage
492492// =====================
493493
494+ /// Sorts arrays of HashSet-backed fields that have non-deterministic iteration order.
495+ /// Object keys are already deterministic because serde_json::Value uses BTreeMap.
496+ fn normalize_set_arrays ( value : & mut serde_json:: Value ) {
497+ const SET_FIELDS : & [ & str ] =
498+ & [ "accessed_blocks" , "accessed_contract_addresses" , "accessed_storage_keys" ] ;
499+ match value {
500+ serde_json:: Value :: Object ( map) => {
501+ for ( key, val) in map. iter_mut ( ) {
502+ if SET_FIELDS . contains ( & key. as_str ( ) ) {
503+ if let serde_json:: Value :: Array ( arr) = val {
504+ arr. sort_by_key ( |a| a. to_string ( ) ) ;
505+ }
506+ } else {
507+ normalize_set_arrays ( val) ;
508+ }
509+ }
510+ }
511+ serde_json:: Value :: Array ( arr) => {
512+ for item in arr. iter_mut ( ) {
513+ normalize_set_arrays ( item) ;
514+ }
515+ }
516+ _ => { }
517+ }
518+ }
519+
520+ fn to_normalized_json ( value : & impl serde:: Serialize ) -> String {
521+ let mut json_value = serde_json:: to_value ( value) . unwrap ( ) ;
522+ normalize_set_arrays ( & mut json_value) ;
523+ format ! ( "{}\n " , serde_json:: to_string_pretty( & json_value) . unwrap( ) )
524+ }
525+
494526async fn gcs_client ( ) -> Client {
495527 Client :: new ( ClientConfig :: default ( ) . with_auth ( ) . await . expect (
496528 "Failed to create GCS client config. Did you run `gcloud auth application-default login`?" ,
@@ -531,8 +563,8 @@ async fn fetch_raw_blobs_at_generation(
531563}
532564
533565/// Pushes the blobs to GCS.
534- async fn bump_generation_and_store_blob_file ( blobs : & [ AerospikeBlob ] , client : & Client ) {
535- let blobs_json = serde_json :: to_string_pretty ( blobs) . unwrap ( ) ;
566+ async fn bump_generation_and_store_blob_file ( blobs : Vec < AerospikeBlob > , client : & Client ) {
567+ let blobs_json = to_normalized_json ( & blobs) ;
536568 let next_generation = find_next_available_blobs_generation ( client) . await ;
537569 client
538570 . upload_object (
@@ -567,13 +599,12 @@ async fn test_make_data() {
567599 // TODO(Dori): create txs.
568600 let ( blobs, preconfirmed_block) = blob_factory. finalize ( ) . await ;
569601 expect_file ! [ CHAIN_INFO_PATH ] . assert_eq ( & serde_json:: to_string_pretty ( & chain_info) . unwrap ( ) ) ;
570- expect_file ! [ PRECONFIRMED_BLOCK_PATH ]
571- . assert_eq ( & serde_json:: to_string_pretty ( & preconfirmed_block) . unwrap ( ) ) ;
602+ expect_file ! [ PRECONFIRMED_BLOCK_PATH ] . assert_eq ( & to_normalized_json ( & preconfirmed_block) ) ;
572603
573604 // Upload or download blobs depending on the fix mode.
574605 let client = gcs_client ( ) . await ;
575606 if env:: var ( "UPDATE_EXPECT" ) . is_ok ( ) {
576- bump_generation_and_store_blob_file ( & blobs, & client) . await ;
607+ bump_generation_and_store_blob_file ( blobs, & client) . await ;
577608 } else {
578609 let fetched_blobs = fetch_blob_file ( & client) . await ;
579610 assert_eq ! (
0 commit comments