use {
super::{
decompress::decompress_all_streams,
history::{HistoricalIdlVersion, IdlHistorySource},
sessions::group_chunks_into_sessions,
DecompressedSessions, IdlFetcher, RpcConfirmedTransactionStatusWithSignature,
SessionChunks, SlotChunk, PROGRESS_TICK_INTERVAL_MS,
},
anyhow::{anyhow, Result},
indicatif::{ProgressBar, ProgressStyle},
std::time::Duration,
};
fn combine_chunks(chunks: &[SlotChunk]) -> Vec<u8> {
chunks
.iter()
.flat_map(|(_, _, chunk)| chunk.iter())
.copied()
.collect()
}
fn collect_and_process_chunks(
fetcher: &IdlFetcher,
signatures: &[RpcConfirmedTransactionStatusWithSignature],
pb: &ProgressBar,
) -> Vec<SlotChunk> {
let mut all_chunks = fetcher.collect_chunks_owned(signatures, pb);
all_chunks.sort_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1.cmp(&b.1)));
all_chunks
}
fn decompress_sessions(
sessions: &[SessionChunks],
pb: &ProgressBar,
) -> Result<DecompressedSessions> {
let mut failed = 0usize;
let mut extracted = Vec::new();
for session in sessions {
pb.inc(1);
let combined_data = combine_chunks(session);
let streams = decompress_all_streams(&combined_data);
if streams.is_empty() {
failed += 1;
continue;
}
let (session_slot, session_sig) = session
.first()
.map(|(slot, signature, _)| (*slot, signature.clone()))
.ok_or_else(|| {
anyhow!("could not reconstruct an IDL upload from the fetched transactions")
})?;
extracted.extend(streams.into_iter().map(|idl_data| HistoricalIdlVersion {
slot: session_slot,
signature: session_sig.clone(),
source: IdlHistorySource::Legacy,
idl_data,
}));
}
Ok(DecompressedSessions {
extracted_idls: extracted,
skipped_sessions: failed,
})
}
pub(super) fn fetch_legacy_historical_idls(
fetcher: &IdlFetcher,
signatures: &[RpcConfirmedTransactionStatusWithSignature],
verbose: bool,
) -> Result<Vec<HistoricalIdlVersion>> {
if signatures.is_empty() {
return Ok(Vec::new());
}
if verbose {
println!(
"Processing {} transactions on the legacy IDL account...",
signatures.len()
);
}
let pb = ProgressBar::new(signatures.len() as u64);
pb.enable_steady_tick(Duration::from_millis(PROGRESS_TICK_INTERVAL_MS));
pb.set_style(
ProgressStyle::default_bar()
.template(
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} \
transactions ({eta})",
)
.unwrap()
.progress_chars("#>-"),
);
pb.set_message("Extracting legacy IDL chunks from transactions...");
let all_chunks = collect_and_process_chunks(fetcher, signatures, &pb);
pb.finish_with_message("Legacy transaction processing complete");
if all_chunks.is_empty() {
return Ok(Vec::new());
}
if verbose {
println!(
"Grouping {} legacy chunks into sessions...",
all_chunks.len()
);
}
let sessions = group_chunks_into_sessions(&all_chunks);
let decompress_pb = ProgressBar::new(sessions.len() as u64);
decompress_pb.enable_steady_tick(Duration::from_millis(PROGRESS_TICK_INTERVAL_MS));
decompress_pb.set_style(
ProgressStyle::default_bar()
.template(
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} sessions",
)
.unwrap()
.progress_chars("#>-"),
);
decompress_pb.set_message("Decompressing reconstructed legacy upload sessions...");
let decompressed = decompress_sessions(&sessions, &decompress_pb)?;
decompress_pb.finish_with_message("Legacy decompression complete");
if decompressed.skipped_sessions > 0 {
println!(
"Skipped {}/{} legacy session(s): no valid zlib streams found (partial uploads)",
decompressed.skipped_sessions,
sessions.len()
);
}
Ok(decompressed.extracted_idls)
}