@@ -498,6 +498,38 @@ impl BlobFactory {
498498// Blob file storage
499499// =====================
500500
501+ /// Sorts arrays of HashSet-backed fields that have non-deterministic iteration order.
502+ /// Object keys are already deterministic because serde_json::Value uses BTreeMap.
503+ fn normalize_set_arrays ( value : & mut serde_json:: Value ) {
504+ const SET_FIELDS : & [ & str ] =
505+ & [ "accessed_blocks" , "accessed_contract_addresses" , "accessed_storage_keys" ] ;
506+ match value {
507+ serde_json:: Value :: Object ( map) => {
508+ for ( key, val) in map. iter_mut ( ) {
509+ if SET_FIELDS . contains ( & key. as_str ( ) ) {
510+ if let serde_json:: Value :: Array ( arr) = val {
511+ arr. sort_by_key ( |a| a. to_string ( ) ) ;
512+ }
513+ } else {
514+ normalize_set_arrays ( val) ;
515+ }
516+ }
517+ }
518+ serde_json:: Value :: Array ( arr) => {
519+ for item in arr. iter_mut ( ) {
520+ normalize_set_arrays ( item) ;
521+ }
522+ }
523+ _ => { }
524+ }
525+ }
526+
527+ fn to_normalized_json ( value : & impl serde:: Serialize ) -> String {
528+ let mut json_value = serde_json:: to_value ( value) . unwrap ( ) ;
529+ normalize_set_arrays ( & mut json_value) ;
530+ format ! ( "{}\n " , serde_json:: to_string_pretty( & json_value) . unwrap( ) )
531+ }
532+
501533async fn gcs_client ( ) -> Client {
502534 Client :: new ( ClientConfig :: default ( ) . with_auth ( ) . await . expect (
503535 "Failed to create GCS client config. Did you run `gcloud auth application-default login`?" ,
@@ -538,8 +570,8 @@ async fn fetch_raw_blobs_at_generation(
538570}
539571
540572/// Pushes the blobs to GCS.
541- async fn bump_generation_and_store_blob_file ( blobs : & [ AerospikeBlob ] , client : & Client ) {
542- let blobs_json = serde_json :: to_string_pretty ( blobs) . unwrap ( ) ;
573+ async fn bump_generation_and_store_blob_file ( blobs : Vec < AerospikeBlob > , client : & Client ) {
574+ let blobs_json = to_normalized_json ( & blobs) ;
543575 let next_generation = find_next_available_blobs_generation ( client) . await ;
544576 client
545577 . upload_object (
@@ -574,13 +606,12 @@ async fn test_make_data() {
574606 // TODO(Dori): create txs.
575607 let ( blobs, preconfirmed_block) = blob_factory. finalize ( ) . await ;
576608 expect_file ! [ CHAIN_INFO_PATH ] . assert_eq ( & serde_json:: to_string_pretty ( & chain_info) . unwrap ( ) ) ;
577- expect_file ! [ PRECONFIRMED_BLOCK_PATH ]
578- . assert_eq ( & serde_json:: to_string_pretty ( & preconfirmed_block) . unwrap ( ) ) ;
609+ expect_file ! [ PRECONFIRMED_BLOCK_PATH ] . assert_eq ( & to_normalized_json ( & preconfirmed_block) ) ;
579610
580611 // Upload or download blobs depending on the fix mode.
581612 let client = gcs_client ( ) . await ;
582613 if env:: var ( "UPDATE_EXPECT" ) . is_ok ( ) {
583- bump_generation_and_store_blob_file ( & blobs, & client) . await ;
614+ bump_generation_and_store_blob_file ( blobs, & client) . await ;
584615 } else {
585616 let fetched_blobs = fetch_blob_file ( & client) . await ;
586617 assert_eq ! (
0 commit comments