@@ -105,52 +105,78 @@ pub struct Cmd {
105105pub enum Error {
106106 #[ error( "wasm hash invalid: {0}" ) ]
107107 WasmHashInvalid ( String ) ,
108+
108109 #[ error( "downloading history: {0}" ) ]
109110 DownloadingHistory ( reqwest:: Error ) ,
111+
110112 #[ error( "downloading history: got status code {0}" ) ]
111113 DownloadingHistoryGotStatusCode ( reqwest:: StatusCode ) ,
114+
112115 #[ error( "json decoding history: {0}" ) ]
113116 JsonDecodingHistory ( serde_json:: Error ) ,
117+
114118 #[ error( "opening cached bucket to read: {0}" ) ]
115119 ReadOpeningCachedBucket ( io:: Error ) ,
120+
116121 #[ error( "parsing bucket url: {0}" ) ]
117122 ParsingBucketUrl ( url:: ParseError ) ,
123+
118124 #[ error( "getting bucket: {0}" ) ]
119125 GettingBucket ( reqwest:: Error ) ,
126+
120127 #[ error( "getting bucket: got status code {0}" ) ]
121128 GettingBucketGotStatusCode ( reqwest:: StatusCode ) ,
129+
122130 #[ error( "opening cached bucket to write: {0}" ) ]
123131 WriteOpeningCachedBucket ( io:: Error ) ,
132+
124133 #[ error( "streaming bucket: {0}" ) ]
125134 StreamingBucket ( io:: Error ) ,
135+
126136 #[ error( "read XDR frame bucket entry: {0}" ) ]
127137 ReadXdrFrameBucketEntry ( xdr:: Error ) ,
138+
128139 #[ error( "renaming temporary downloaded file to final destination: {0}" ) ]
129140 RenameDownloadFile ( io:: Error ) ,
141+
130142 #[ error( "getting bucket directory: {0}" ) ]
131143 GetBucketDir ( data:: Error ) ,
144+
132145 #[ error( "reading history http stream: {0}" ) ]
133146 ReadHistoryHttpStream ( reqwest:: Error ) ,
147+
134148 #[ error( "writing ledger snapshot: {0}" ) ]
135149 WriteLedgerSnapshot ( soroban_ledger_snapshot:: Error ) ,
150+
136151 #[ error( transparent) ]
137152 Join ( #[ from] tokio:: task:: JoinError ) ,
153+
138154 #[ error( transparent) ]
139155 Network ( #[ from] config:: network:: Error ) ,
156+
140157 #[ error( transparent) ]
141158 Locator ( #[ from] locator:: Error ) ,
159+
142160 #[ error( transparent) ]
143161 Config ( #[ from] config:: Error ) ,
162+
144163 #[ error( "archive url not configured" ) ]
145164 ArchiveUrlNotConfigured ,
165+
146166 #[ error( "parsing asset name: {0}" ) ]
147167 ParseAssetName ( String ) ,
168+
148169 #[ error( transparent) ]
149170 Asset ( #[ from] builder:: asset:: Error ) ,
171+
150172 #[ error( "ledger not found in archive" ) ]
151173 LedgerNotFound ,
174+
152175 #[ error( "xdr parsing error: {0}" ) ]
153176 Xdr ( #[ from] xdr:: Error ) ,
177+
178+ #[ error( "corrupted bucket file: expected hash {expected}, got {actual}" ) ]
179+ CorruptedBucket { expected : String , actual : String } ,
154180}
155181
156182/// Checkpoint frequency is usually 64 ledgers, but in local test nets it'll
@@ -617,6 +643,23 @@ async fn get_ledger_metadata_from_archive(
617643 Err ( Error :: LedgerNotFound )
618644}
619645
646+ fn validate_bucket_hash ( cache_path : & PathBuf , expected_hash : & str ) -> Result < ( ) , Error > {
647+ let file = std:: fs:: File :: open ( cache_path) . map_err ( Error :: ReadOpeningCachedBucket ) ?;
648+ let mut hasher = Sha256 :: new ( ) ;
649+ std:: io:: copy ( & mut std:: io:: BufReader :: new ( file) , & mut hasher)
650+ . map_err ( Error :: ReadOpeningCachedBucket ) ?;
651+ let actual_hash = hex:: encode ( hasher. finalize ( ) ) ;
652+
653+ if actual_hash != expected_hash {
654+ return Err ( Error :: CorruptedBucket {
655+ expected : expected_hash. to_string ( ) ,
656+ actual : actual_hash,
657+ } ) ;
658+ }
659+
660+ Ok ( ( ) )
661+ }
662+
620663async fn cache_bucket (
621664 print : & print:: Print ,
622665 archive_url : & Url ,
@@ -625,6 +668,19 @@ async fn cache_bucket(
625668) -> Result < PathBuf , Error > {
626669 let bucket_dir = data:: bucket_dir ( ) . map_err ( Error :: GetBucketDir ) ?;
627670 let cache_path = bucket_dir. join ( format ! ( "bucket-{bucket}.xdr" ) ) ;
671+
672+ // Validate cached bucket if it exists
673+ if cache_path. exists ( ) {
674+ if validate_bucket_hash ( & cache_path, bucket) . is_err ( ) {
675+ print. warnln ( format ! (
676+ "Cached bucket {bucket} is corrupted, re-downloading"
677+ ) ) ;
678+ std:: fs:: remove_file ( & cache_path) . ok ( ) ;
679+ } else {
680+ return Ok ( cache_path) ;
681+ }
682+ }
683+
628684 if !cache_path. exists ( ) {
629685 let bucket_0 = & bucket[ 0 ..=1 ] ;
630686 let bucket_1 = & bucket[ 2 ..=3 ] ;
0 commit comments