Skip to content

Commit 4198835

Browse files
authored
Ensure snapshot's bucket cache is not corrupted. (#2238)
1 parent a25bf0b commit 4198835

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

  • cmd/soroban-cli/src/commands/snapshot

cmd/soroban-cli/src/commands/snapshot/create.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,52 +105,78 @@ pub struct Cmd {
105105
pub 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+
620663
async 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

Comments
 (0)