Skip to main content

blockchain_compression/presets/
solana.rs

1//! Solana blockchain-specific compression presets and configurations
2//!
3//! This module provides optimized compression configurations specifically designed
4//! for Solana blockchain data structures and patterns using Zstandard compression.
5
6use 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
15/// Solana-optimized compression engine using Zstandard with custom dictionaries
16pub struct SolanaCompressor {
17    /// Compression level (1-22)
18    compression_level: i32,
19    /// Preset configuration
20    preset: SolanaPreset,
21    /// Custom dictionary for Solana patterns
22    dictionary: Option<Vec<u8>>,
23    /// Compression statistics
24    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/// Solana compression configuration presets
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub enum SolanaPreset {
41    /// Optimized for transaction data with high signature/account repetition
42    Transactions,
43    /// Optimized for account state data
44    Accounts,
45    /// Optimized for program instruction data
46    Instructions,
47    /// Balanced configuration for mixed workloads
48    Mixed,
49    /// Maximum compression (slower but best ratio)
50    MaxCompression,
51    /// Fast compression (lower ratio but faster)
52    FastCompression,
53}
54
55/// Solana-specific pattern types
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub enum SolanaPatternType {
58    /// Ed25519 public keys (32 bytes)
59    PublicKey,
60    /// Ed25519 signatures (64 bytes)
61    Signature,
62    /// Program IDs (32 bytes)
63    ProgramId,
64    /// Token amounts (8 bytes)
65    Amount,
66    /// Blockhashes (32 bytes)
67    Blockhash,
68    /// Instruction data patterns
69    InstructionData,
70}
71
72/// Common Solana program IDs and addresses for dictionary training
73const SOLANA_DICTIONARY_PATTERNS: &[&str] = &[
74    // System and core programs
75    "11111111111111111111111111111112",  // System Program
76    "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",  // Token Program
77    "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",  // Associated Token Program
78    "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",  // Serum DEX
79    "BPFLoaderUpgradeab1e11111111111111111111111",  // BPF Loader
80    "Config1111111111111111111111111111111111111", // Config Program
81    "Vote111111111111111111111111111111111111111",  // Vote Program
82    "Stake11111111111111111111111111111111111111",  // Stake Program
83
84    // Common instruction patterns
85    "00000000",  // Transfer instruction
86    "01000000",  // Initialize account
87    "02000000",  // Close account
88    "03000000",  // Approve
89
90    // Common amounts (as bytes)
91    "00e1f50500000000",  // 1 SOL in lamports
92    "00ca9a3b00000000",  // 0.1 SOL in lamports
93    "0010270000000000",  // 0.01 SOL in lamports
94    "00e40b5402000000",  // 10 SOL in lamports
95
96    // Common transaction structure markers
97    "0100",  // Single signature
98    "0200",  // Two signatures
99    "0300",  // Three signatures
100];
101
102impl SolanaCompressor {
103    /// Create a new Solana compressor with the specified preset
104    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    /// Convert preset to compression level
117    fn preset_to_level(preset: &SolanaPreset) -> i32 {
118        match preset {
119            SolanaPreset::FastCompression => 3,     // Fast compression
120            SolanaPreset::Transactions => 3,        // Fast for real-time processing
121            SolanaPreset::Instructions => 6,        // Balanced
122            SolanaPreset::Accounts => 6,            // Balanced
123            SolanaPreset::Mixed => 6,               // Balanced for general use
124            SolanaPreset::MaxCompression => 19,     // Maximum compression
125        }
126    }
127
128    /// Build a custom dictionary from common Solana patterns
129    fn build_solana_dictionary() -> Vec<u8> {
130        let mut dictionary_data = Vec::new();
131
132        // Add all common patterns to dictionary
133        for pattern in SOLANA_DICTIONARY_PATTERNS {
134            dictionary_data.extend_from_slice(pattern.as_bytes());
135            dictionary_data.push(0); // Null separator
136        }
137
138        // Add some common Base58 character sequences
139        dictionary_data.extend_from_slice(b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
140
141        dictionary_data
142    }
143
144    /// Reset internal compression state
145    pub fn reset(&mut self) {
146        self.stats = CompressionStats::default();
147    }
148
149    /// Get common Solana program IDs that appear frequently
150    #[cfg(feature = "solana")]
151    pub fn common_program_ids() -> Vec<Pubkey> {
152        vec![
153            solana_sdk::system_program::ID,              // System program
154            spl_token::ID,                                // Token program
155            spl_associated_token_account::ID,             // Associated token account
156            solana_sdk::sysvar::rent::ID,                 // Rent sysvar
157            solana_sdk::sysvar::clock::ID,                // Clock sysvar
158            // Add more common program IDs as needed
159        ]
160    }
161
162    /// Pre-populate the compressor with common Solana patterns
163    #[cfg(feature = "solana")]
164    pub fn pre_populate_common_patterns(&mut self) -> Result<(), CompressionError> {
165        // Add common program IDs
166        for program_id in Self::common_program_ids() {
167            self.add_pubkey_pattern(program_id)?;
168        }
169
170        // Add common amounts (powers of 10 in lamports)
171        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    /// Add a specific public key pattern
180    #[cfg(feature = "solana")]
181    pub fn add_pubkey_pattern(&mut self, pubkey: Pubkey) -> Result<(), CompressionError> {
182        // This would integrate with the pattern engine to add the pubkey
183        // Implementation would depend on the pattern engine's API
184        Ok(())
185    }
186
187    /// Add a specific amount pattern
188    pub fn add_amount_pattern(&mut self, amount: u64) -> Result<(), CompressionError> {
189        // This would integrate with the pattern engine to add the amount
190        // Implementation would depend on the pattern engine's API
191        Ok(())
192    }
193
194    /// Optimize specifically for Solana transaction patterns
195    pub fn optimize_for_transactions(&mut self) -> Result<(), CompressionError> {
196        // This could analyze usage patterns and optimize for transaction-specific patterns
197        Ok(())
198    }
199
200    /// Get Solana-specific compression statistics
201    pub fn solana_stats(&self) -> SolanaCompressionStats {
202        // Simplified for zstd implementation
203        SolanaCompressionStats {
204            pubkey_patterns: 0,
205            signature_patterns: 0,
206            amount_patterns: 0,
207            total_solana_bytes_saved: 0,
208        }
209    }
210}
211
212/// Solana-specific compression statistics
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct SolanaCompressionStats {
215    /// Number of signature patterns
216    pub signature_patterns: usize,
217    /// Number of public key patterns
218    pub pubkey_patterns: usize,
219    /// Number of amount patterns
220    pub amount_patterns: usize,
221    /// Total bytes saved by Solana-specific patterns
222    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                // Use custom dictionary for optimal Solana compression
238                #[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                // Fallback to standard zstd compression
264                #[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        // Update statistics
281        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                // Use custom dictionary for decompression
303                #[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                // Fallback to standard zstd decompression
327                #[cfg(feature = "zstd")]
328                {
329                    zstd::bulk::decompress(data, 1024 * 1024) // 1MB limit for safety
330                        .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        // Note: Can't update stats here due to &self constraint from trait
344
345        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, // Rough estimate
356        }
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        // Create test data with repetitive Solana patterns that should compress well
391        let mut test_data = Vec::new();
392
393        // Add common Solana program IDs (from dictionary)
394        for _ in 0..20 {
395            test_data.extend_from_slice("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".as_bytes()); // Token Program
396            test_data.extend_from_slice("11111111111111111111111111111112".as_bytes()); // System Program
397        }
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        // Decompress using the trait method
405        let decompressed = (&compressor as &dyn CompressionStrategy<Error = _>).decompress(&compressed)
406            .expect("Decompression should work");
407
408        // Verify perfect data integrity
409        assert_eq!(test_data, decompressed, "zstd must provide perfect data integrity");
410
411        // Should achieve excellent compression on repetitive Solana patterns
412        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        // Verify stats
417        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}