@@ -494,6 +494,38 @@ impl BlobFactory {
494494// Blob file storage
495495// =====================
496496
497+ /// Sorts arrays of HashSet-backed fields that have non-deterministic iteration order.
498+ /// Object keys are already deterministic because serde_json::Value uses BTreeMap.
499+ fn normalize_set_arrays ( value : & mut serde_json:: Value ) {
500+ const SET_FIELDS : & [ & str ] =
501+ & [ "accessed_blocks" , "accessed_contract_addresses" , "accessed_storage_keys" ] ;
502+ match value {
503+ serde_json:: Value :: Object ( map) => {
504+ for ( key, val) in map. iter_mut ( ) {
505+ if SET_FIELDS . contains ( & key. as_str ( ) ) {
506+ if let serde_json:: Value :: Array ( arr) = val {
507+ arr. sort_by_key ( |a| a. to_string ( ) ) ;
508+ }
509+ } else {
510+ normalize_set_arrays ( val) ;
511+ }
512+ }
513+ }
514+ serde_json:: Value :: Array ( arr) => {
515+ for item in arr. iter_mut ( ) {
516+ normalize_set_arrays ( item) ;
517+ }
518+ }
519+ _ => { }
520+ }
521+ }
522+
523+ fn to_normalized_json ( value : & impl serde:: Serialize ) -> String {
524+ let mut json_value = serde_json:: to_value ( value) . unwrap ( ) ;
525+ normalize_set_arrays ( & mut json_value) ;
526+ format ! ( "{}\n " , serde_json:: to_string_pretty( & json_value) . unwrap( ) )
527+ }
528+
497529async fn gcs_client ( ) -> Client {
498530 Client :: new ( ClientConfig :: default ( ) . with_auth ( ) . await . expect (
499531 "Failed to create GCS client config. Did you run `gcloud auth application-default login`?" ,
@@ -534,8 +566,8 @@ async fn fetch_raw_blobs_at_generation(
534566}
535567
536568/// Pushes the blobs to GCS.
537- async fn bump_generation_and_store_blob_file ( blobs : & [ AerospikeBlob ] , client : & Client ) {
538- let blobs_json = serde_json :: to_string_pretty ( blobs) . unwrap ( ) ;
569+ async fn bump_generation_and_store_blob_file ( blobs : Vec < AerospikeBlob > , client : & Client ) {
570+ let blobs_json = to_normalized_json ( & blobs) ;
539571 let next_generation = find_next_available_blobs_generation ( client) . await ;
540572 client
541573 . upload_object (
@@ -570,13 +602,12 @@ async fn test_make_data() {
570602 // TODO(Dori): create txs.
571603 let ( blobs, preconfirmed_block) = blob_factory. finalize ( ) . await ;
572604 expect_file ! [ CHAIN_INFO_PATH ] . assert_eq ( & serde_json:: to_string_pretty ( & chain_info) . unwrap ( ) ) ;
573- expect_file ! [ PRECONFIRMED_BLOCK_PATH ]
574- . assert_eq ( & serde_json:: to_string_pretty ( & preconfirmed_block) . unwrap ( ) ) ;
605+ expect_file ! [ PRECONFIRMED_BLOCK_PATH ] . assert_eq ( & to_normalized_json ( & preconfirmed_block) ) ;
575606
576607 // Upload or download blobs depending on the fix mode.
577608 let client = gcs_client ( ) . await ;
578609 if env:: var ( "UPDATE_EXPECT" ) . is_ok ( ) {
579- bump_generation_and_store_blob_file ( & blobs, & client) . await ;
610+ bump_generation_and_store_blob_file ( blobs, & client) . await ;
580611 } else {
581612 let fetched_blobs = fetch_blob_file ( & client) . await ;
582613 assert_eq ! (
0 commit comments