blockchain_compression/presets/
solana.rs1use crate::core::traits::{CompressionStrategy, CompressionError, CompressionMetadata, CompressionStats};
7use serde::{Deserialize, Serialize};
8
9#[cfg(feature = "zstd")]
10use std::io::{Read, Write};
11
12#[cfg(feature = "solana")]
13use solana_sdk::{pubkey::Pubkey, signature::Signature};
14
15pub struct SolanaCompressor {
17 compression_level: i32,
19 preset: SolanaPreset,
21 dictionary: Option<Vec<u8>>,
23 stats: CompressionStats,
25}
26
27impl std::fmt::Debug for SolanaCompressor {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 f.debug_struct("SolanaCompressor")
30 .field("compression_level", &self.compression_level)
31 .field("preset", &self.preset)
32 .field("dictionary_size", &self.dictionary.as_ref().map(|d| d.len()))
33 .field("stats", &self.stats)
34 .finish()
35 }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub enum SolanaPreset {
41 Transactions,
43 Accounts,
45 Instructions,
47 Mixed,
49 MaxCompression,
51 FastCompression,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub enum SolanaPatternType {
58 PublicKey,
60 Signature,
62 ProgramId,
64 Amount,
66 Blockhash,
68 InstructionData,
70}
71
72const SOLANA_DICTIONARY_PATTERNS: &[&str] = &[
74 "11111111111111111111111111111112", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", "BPFLoaderUpgradeab1e11111111111111111111111", "Config1111111111111111111111111111111111111", "Vote111111111111111111111111111111111111111", "Stake11111111111111111111111111111111111111", "00000000", "01000000", "02000000", "03000000", "00e1f50500000000", "00ca9a3b00000000", "0010270000000000", "00e40b5402000000", "0100", "0200", "0300", ];
101
102impl SolanaCompressor {
103 pub fn new(preset: SolanaPreset) -> Self {
105 let compression_level = Self::preset_to_level(&preset);
106 let dictionary = Self::build_solana_dictionary();
107
108 Self {
109 compression_level,
110 preset,
111 dictionary: Some(dictionary),
112 stats: CompressionStats::default(),
113 }
114 }
115
116 fn preset_to_level(preset: &SolanaPreset) -> i32 {
118 match preset {
119 SolanaPreset::FastCompression => 3, SolanaPreset::Transactions => 3, SolanaPreset::Instructions => 6, SolanaPreset::Accounts => 6, SolanaPreset::Mixed => 6, SolanaPreset::MaxCompression => 19, }
126 }
127
128 fn build_solana_dictionary() -> Vec<u8> {
130 let mut dictionary_data = Vec::new();
131
132 for pattern in SOLANA_DICTIONARY_PATTERNS {
134 dictionary_data.extend_from_slice(pattern.as_bytes());
135 dictionary_data.push(0); }
137
138 dictionary_data.extend_from_slice(b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
140
141 dictionary_data
142 }
143
144 pub fn reset(&mut self) {
146 self.stats = CompressionStats::default();
147 }
148
149 #[cfg(feature = "solana")]
151 pub fn common_program_ids() -> Vec<Pubkey> {
152 vec![
153 solana_sdk::system_program::ID, spl_token::ID, spl_associated_token_account::ID, solana_sdk::sysvar::rent::ID, solana_sdk::sysvar::clock::ID, ]
160 }
161
162 #[cfg(feature = "solana")]
164 pub fn pre_populate_common_patterns(&mut self) -> Result<(), CompressionError> {
165 for program_id in Self::common_program_ids() {
167 self.add_pubkey_pattern(program_id)?;
168 }
169
170 for i in 0..=9 {
172 let amount = 10_u64.pow(i);
173 self.add_amount_pattern(amount)?;
174 }
175
176 Ok(())
177 }
178
179 #[cfg(feature = "solana")]
181 pub fn add_pubkey_pattern(&mut self, pubkey: Pubkey) -> Result<(), CompressionError> {
182 Ok(())
185 }
186
187 pub fn add_amount_pattern(&mut self, amount: u64) -> Result<(), CompressionError> {
189 Ok(())
192 }
193
194 pub fn optimize_for_transactions(&mut self) -> Result<(), CompressionError> {
196 Ok(())
198 }
199
200 pub fn solana_stats(&self) -> SolanaCompressionStats {
202 SolanaCompressionStats {
204 pubkey_patterns: 0,
205 signature_patterns: 0,
206 amount_patterns: 0,
207 total_solana_bytes_saved: 0,
208 }
209 }
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct SolanaCompressionStats {
215 pub signature_patterns: usize,
217 pub pubkey_patterns: usize,
219 pub amount_patterns: usize,
221 pub total_solana_bytes_saved: u64,
223}
224
225impl CompressionStrategy for SolanaCompressor {
226 type Error = CompressionError;
227
228 fn compress(&mut self, data: &[u8]) -> Result<Vec<u8>, Self::Error> {
229 if data.is_empty() {
230 return Ok(Vec::new());
231 }
232
233 let _start_time = std::time::Instant::now();
234
235 let compressed: Vec<u8> = match &self.dictionary {
236 Some(dict) => {
237 #[cfg(feature = "zstd")]
239 {
240 let mut encoder = zstd::stream::write::Encoder::with_dictionary(Vec::new(), self.compression_level, dict)
241 .map_err(|e| CompressionError::Internal {
242 message: format!("Failed to create zstd encoder with dictionary: {}", e),
243 })?;
244
245 encoder.write_all(data)
246 .map_err(|e| CompressionError::Internal {
247 message: format!("Failed to write data to zstd encoder: {}", e),
248 })?;
249
250 encoder.finish()
251 .map_err(|e| CompressionError::Internal {
252 message: format!("Failed to finish zstd compression: {}", e),
253 })?
254 }
255 #[cfg(not(feature = "zstd"))]
256 {
257 return Err(CompressionError::Internal {
258 message: "zstd feature not enabled".to_string(),
259 });
260 }
261 }
262 None => {
263 #[cfg(feature = "zstd")]
265 {
266 zstd::bulk::compress(data, self.compression_level)
267 .map_err(|e| CompressionError::Internal {
268 message: format!("zstd compression failed: {}", e),
269 })?
270 }
271 #[cfg(not(feature = "zstd"))]
272 {
273 return Err(CompressionError::Internal {
274 message: "zstd feature not enabled".to_string(),
275 });
276 }
277 }
278 };
279
280 self.stats.compressions += 1;
282 self.stats.total_input_bytes += data.len() as u64;
283 self.stats.total_output_bytes += compressed.len() as u64;
284
285 let ratio = data.len() as f64 / compressed.len() as f64;
286 if ratio > self.stats.best_ratio {
287 self.stats.best_ratio = ratio;
288 }
289
290 Ok(compressed)
291 }
292
293 fn decompress(&self, data: &[u8]) -> Result<Vec<u8>, Self::Error> {
294 if data.is_empty() {
295 return Ok(Vec::new());
296 }
297
298 let _start_time = std::time::Instant::now();
299
300 let decompressed: Vec<u8> = match &self.dictionary {
301 Some(dict) => {
302 #[cfg(feature = "zstd")]
304 {
305 let mut decoder = zstd::stream::read::Decoder::with_dictionary(data, dict)
306 .map_err(|e| CompressionError::Internal {
307 message: format!("Failed to create zstd decoder with dictionary: {}", e),
308 })?;
309
310 let mut decompressed = Vec::new();
311 decoder.read_to_end(&mut decompressed)
312 .map_err(|e| CompressionError::Internal {
313 message: format!("Failed to decompress with zstd: {}", e),
314 })?;
315
316 decompressed
317 }
318 #[cfg(not(feature = "zstd"))]
319 {
320 return Err(CompressionError::Internal {
321 message: "zstd feature not enabled".to_string(),
322 });
323 }
324 }
325 None => {
326 #[cfg(feature = "zstd")]
328 {
329 zstd::bulk::decompress(data, 1024 * 1024) .map_err(|e| CompressionError::Internal {
331 message: format!("zstd decompression failed: {}", e),
332 })?
333 }
334 #[cfg(not(feature = "zstd"))]
335 {
336 return Err(CompressionError::Internal {
337 message: "zstd feature not enabled".to_string(),
338 });
339 }
340 }
341 };
342
343 Ok(decompressed)
346 }
347
348 fn metadata(&self) -> CompressionMetadata {
349 CompressionMetadata {
350 name: "Solana Zstd Compressor".to_string(),
351 description: "Solana-optimized compression using Zstandard with custom dictionaries".to_string(),
352 version: "2.0.0".to_string(),
353 domains: vec!["Solana".to_string(), "Blockchain".to_string()],
354 deterministic: true,
355 memory_usage: (self.compression_level * 1024 * 1024) as usize, }
357 }
358
359 fn stats(&self) -> CompressionStats {
360 self.stats.clone()
361 }
362
363 fn reset(&mut self) {
364 self.stats = CompressionStats::default();
365 }
366}
367
368#[cfg(test)]
369mod tests {
370 use super::*;
371
372 #[test]
373 fn test_solana_compressor_creation() {
374 let compressor = SolanaCompressor::new(SolanaPreset::Mixed);
375 let metadata = compressor.metadata();
376 assert_eq!(metadata.name, "Solana Zstd Compressor");
377 }
378
379 #[test]
380 fn test_transaction_preset() {
381 let compressor = SolanaCompressor::new(SolanaPreset::Transactions);
382 let stats = compressor.stats();
383 assert_eq!(stats.compressions, 0);
384 }
385
386 #[test]
387 fn test_zstd_compression_with_solana_patterns() {
388 let mut compressor = SolanaCompressor::new(SolanaPreset::Transactions);
389
390 let mut test_data = Vec::new();
392
393 for _ in 0..20 {
395 test_data.extend_from_slice("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".as_bytes()); test_data.extend_from_slice("11111111111111111111111111111112".as_bytes()); }
398
399 println!("Original size: {} bytes", test_data.len());
400
401 let compressed = compressor.compress(&test_data).expect("Compression should work");
402 println!("Compressed size: {} bytes", compressed.len());
403
404 let decompressed = (&compressor as &dyn CompressionStrategy<Error = _>).decompress(&compressed)
406 .expect("Decompression should work");
407
408 assert_eq!(test_data, decompressed, "zstd must provide perfect data integrity");
410
411 let ratio = test_data.len() as f64 / compressed.len() as f64;
413 println!("Compression ratio: {:.2}:1", ratio);
414 assert!(ratio > 10.0, "Should achieve excellent compression on Solana patterns, got {:.2}:1", ratio);
415
416 let stats = compressor.stats();
418 assert_eq!(stats.compressions, 1);
419 assert!(stats.best_ratio > 10.0);
420 }
421
422 #[test]
423 fn test_preset_configurations() {
424 let presets = vec![
425 SolanaPreset::Transactions,
426 SolanaPreset::Accounts,
427 SolanaPreset::Instructions,
428 SolanaPreset::Mixed,
429 SolanaPreset::MaxCompression,
430 SolanaPreset::FastCompression,
431 ];
432
433 for preset in presets {
434 let compressor = SolanaCompressor::new(preset);
435 let metadata = compressor.metadata();
436 assert_eq!(metadata.name, "Solana Zstd Compressor");
437 }
438 }
439}