1use crate::{
75 e2s_types::{E2sError, Entry},
76 DecodeCompressed,
77};
78use alloy_consensus::{Block, BlockBody, Header};
79use alloy_primitives::{B256, U256};
80use alloy_rlp::{Decodable, Encodable};
81use snap::{read::FrameDecoder, write::FrameEncoder};
82use std::{
83 io::{Read, Write},
84 marker::PhantomData,
85};
86
87pub const COMPRESSED_HEADER: [u8; 2] = [0x03, 0x00];
90
91pub const COMPRESSED_BODY: [u8; 2] = [0x04, 0x00];
93
94pub const COMPRESSED_RECEIPTS: [u8; 2] = [0x05, 0x00];
96
97pub const TOTAL_DIFFICULTY: [u8; 2] = [0x06, 0x00];
99
100pub const ACCUMULATOR: [u8; 2] = [0x07, 0x00];
102
103pub const MAX_BLOCKS_PER_ERA1: usize = 8192;
105
106#[derive(Debug, Clone, Default)]
108pub struct SnappyRlpCodec<T> {
109 _phantom: PhantomData<T>,
110}
111
112impl<T> SnappyRlpCodec<T> {
113 pub const fn new() -> Self {
115 Self { _phantom: PhantomData }
116 }
117}
118
119impl<T: Decodable> SnappyRlpCodec<T> {
120 pub fn decode(&self, compressed_data: &[u8]) -> Result<T, E2sError> {
122 let mut decoder = FrameDecoder::new(compressed_data);
123 let mut decompressed = Vec::new();
124 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
125 E2sError::SnappyDecompression(format!("Failed to decompress data: {e}"))
126 })?;
127
128 let mut slice = decompressed.as_slice();
129 T::decode(&mut slice).map_err(|e| E2sError::Rlp(format!("Failed to decode RLP data: {e}")))
130 }
131}
132
133impl<T: Encodable> SnappyRlpCodec<T> {
134 pub fn encode(&self, data: &T) -> Result<Vec<u8>, E2sError> {
136 let mut rlp_data = Vec::new();
137 data.encode(&mut rlp_data);
138
139 let mut compressed = Vec::new();
140 {
141 let mut encoder = FrameEncoder::new(&mut compressed);
142
143 Write::write_all(&mut encoder, &rlp_data).map_err(|e| {
144 E2sError::SnappyCompression(format!("Failed to compress data: {e}"))
145 })?;
146
147 encoder.flush().map_err(|e| {
148 E2sError::SnappyCompression(format!("Failed to flush encoder: {e}"))
149 })?;
150 }
151
152 Ok(compressed)
153 }
154}
155
156#[derive(Debug, Clone)]
158pub struct CompressedHeader {
159 pub data: Vec<u8>,
161}
162
163impl CompressedHeader {
164 pub const fn new(data: Vec<u8>) -> Self {
166 Self { data }
167 }
168
169 pub fn from_rlp(rlp_data: &[u8]) -> Result<Self, E2sError> {
171 let mut compressed = Vec::new();
172 {
173 let mut encoder = FrameEncoder::new(&mut compressed);
174
175 Write::write_all(&mut encoder, rlp_data).map_err(|e| {
176 E2sError::SnappyCompression(format!("Failed to compress header: {e}"))
177 })?;
178
179 encoder.flush().map_err(|e| {
180 E2sError::SnappyCompression(format!("Failed to flush encoder: {e}"))
181 })?;
182 }
183 Ok(Self { data: compressed })
184 }
185
186 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
188 let mut decoder = FrameDecoder::new(self.data.as_slice());
189 let mut decompressed = Vec::new();
190 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
191 E2sError::SnappyDecompression(format!("Failed to decompress header: {e}"))
192 })?;
193
194 Ok(decompressed)
195 }
196
197 pub fn to_entry(&self) -> Entry {
199 Entry::new(COMPRESSED_HEADER, self.data.clone())
200 }
201
202 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
204 if entry.entry_type != COMPRESSED_HEADER {
205 return Err(E2sError::Ssz(format!(
206 "Invalid entry type for CompressedHeader: expected {:02x}{:02x}, got {:02x}{:02x}",
207 COMPRESSED_HEADER[0],
208 COMPRESSED_HEADER[1],
209 entry.entry_type[0],
210 entry.entry_type[1]
211 )));
212 }
213
214 Ok(Self { data: entry.data.clone() })
215 }
216
217 pub fn decode_header(&self) -> Result<Header, E2sError> {
219 self.decode()
220 }
221
222 pub fn from_header<H: Encodable>(header: &H) -> Result<Self, E2sError> {
224 let encoder = SnappyRlpCodec::new();
225 let compressed = encoder.encode(header)?;
226 Ok(Self::new(compressed))
227 }
228}
229
230impl DecodeCompressed for CompressedHeader {
231 fn decode<T: Decodable>(&self) -> Result<T, E2sError> {
232 let decoder = SnappyRlpCodec::<T>::new();
233 decoder.decode(&self.data)
234 }
235}
236
237#[derive(Debug, Clone)]
239pub struct CompressedBody {
240 pub data: Vec<u8>,
242}
243
244impl CompressedBody {
245 pub const fn new(data: Vec<u8>) -> Self {
247 Self { data }
248 }
249
250 pub fn from_rlp(rlp_data: &[u8]) -> Result<Self, E2sError> {
252 let mut compressed = Vec::new();
253 {
254 let mut encoder = FrameEncoder::new(&mut compressed);
255
256 Write::write_all(&mut encoder, rlp_data).map_err(|e| {
257 E2sError::SnappyCompression(format!("Failed to compress header: {e}"))
258 })?;
259
260 encoder.flush().map_err(|e| {
261 E2sError::SnappyCompression(format!("Failed to flush encoder: {e}"))
262 })?;
263 }
264 Ok(Self { data: compressed })
265 }
266
267 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
269 let mut decoder = FrameDecoder::new(self.data.as_slice());
270 let mut decompressed = Vec::new();
271 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
272 E2sError::SnappyDecompression(format!("Failed to decompress body: {e}"))
273 })?;
274
275 Ok(decompressed)
276 }
277
278 pub fn to_entry(&self) -> Entry {
280 Entry::new(COMPRESSED_BODY, self.data.clone())
281 }
282
283 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
285 if entry.entry_type != COMPRESSED_BODY {
286 return Err(E2sError::Ssz(format!(
287 "Invalid entry type for CompressedBody: expected {:02x}{:02x}, got {:02x}{:02x}",
288 COMPRESSED_BODY[0], COMPRESSED_BODY[1], entry.entry_type[0], entry.entry_type[1]
289 )));
290 }
291
292 Ok(Self { data: entry.data.clone() })
293 }
294
295 pub fn decode_body<T: Decodable, H: Decodable>(&self) -> Result<BlockBody<T, H>, E2sError> {
297 let decompressed = self.decompress()?;
298 Self::decode_body_from_decompressed(&decompressed)
299 }
300
301 pub fn decode_body_from_decompressed<T: Decodable, H: Decodable>(
303 data: &[u8],
304 ) -> Result<BlockBody<T, H>, E2sError> {
305 alloy_rlp::decode_exact::<BlockBody<T, H>>(data)
306 .map_err(|e| E2sError::Rlp(format!("Failed to decode RLP data: {e}")))
307 }
308
309 pub fn from_body<B: Encodable>(body: &B) -> Result<Self, E2sError> {
311 let encoder = SnappyRlpCodec::new();
312 let compressed = encoder.encode(body)?;
313 Ok(Self::new(compressed))
314 }
315}
316
317impl DecodeCompressed for CompressedBody {
318 fn decode<T: Decodable>(&self) -> Result<T, E2sError> {
319 let decoder = SnappyRlpCodec::<T>::new();
320 decoder.decode(&self.data)
321 }
322}
323
324#[derive(Debug, Clone)]
326pub struct CompressedReceipts {
327 pub data: Vec<u8>,
329}
330
331impl CompressedReceipts {
332 pub const fn new(data: Vec<u8>) -> Self {
334 Self { data }
335 }
336
337 pub fn from_rlp(rlp_data: &[u8]) -> Result<Self, E2sError> {
339 let mut compressed = Vec::new();
340 {
341 let mut encoder = FrameEncoder::new(&mut compressed);
342
343 Write::write_all(&mut encoder, rlp_data).map_err(|e| {
344 E2sError::SnappyCompression(format!("Failed to compress header: {e}"))
345 })?;
346
347 encoder.flush().map_err(|e| {
348 E2sError::SnappyCompression(format!("Failed to flush encoder: {e}"))
349 })?;
350 }
351 Ok(Self { data: compressed })
352 }
353 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
355 let mut decoder = FrameDecoder::new(self.data.as_slice());
356 let mut decompressed = Vec::new();
357 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
358 E2sError::SnappyDecompression(format!("Failed to decompress receipts: {e}"))
359 })?;
360
361 Ok(decompressed)
362 }
363
364 pub fn to_entry(&self) -> Entry {
366 Entry::new(COMPRESSED_RECEIPTS, self.data.clone())
367 }
368
369 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
371 if entry.entry_type != COMPRESSED_RECEIPTS {
372 return Err(E2sError::Ssz(format!(
373 "Invalid entry type for CompressedReceipts: expected {:02x}{:02x}, got {:02x}{:02x}",
374 COMPRESSED_RECEIPTS[0], COMPRESSED_RECEIPTS[1],
375 entry.entry_type[0], entry.entry_type[1]
376 )));
377 }
378
379 Ok(Self { data: entry.data.clone() })
380 }
381
382 pub fn decode<T: Decodable>(&self) -> Result<T, E2sError> {
384 let decoder = SnappyRlpCodec::<T>::new();
385 decoder.decode(&self.data)
386 }
387
388 pub fn from_encodable<T: Encodable>(data: &T) -> Result<Self, E2sError> {
390 let encoder = SnappyRlpCodec::<T>::new();
391 let compressed = encoder.encode(data)?;
392 Ok(Self::new(compressed))
393 }
394 pub fn encode_receipts_to_rlp<T: Encodable>(receipts: &[T]) -> Result<Vec<u8>, E2sError> {
396 let mut rlp_data = Vec::new();
397 alloy_rlp::encode_list(receipts, &mut rlp_data);
398 Ok(rlp_data)
399 }
400
401 pub fn from_encodable_list<T: Encodable>(receipts: &[T]) -> Result<Self, E2sError> {
403 let rlp_data = Self::encode_receipts_to_rlp(receipts)?;
404 Self::from_rlp(&rlp_data)
405 }
406}
407
408impl DecodeCompressed for CompressedReceipts {
409 fn decode<T: Decodable>(&self) -> Result<T, E2sError> {
410 let decoder = SnappyRlpCodec::<T>::new();
411 decoder.decode(&self.data)
412 }
413}
414
415#[derive(Debug, Clone)]
417pub struct TotalDifficulty {
418 pub value: U256,
420}
421
422impl TotalDifficulty {
423 pub const fn new(value: U256) -> Self {
425 Self { value }
426 }
427
428 pub fn to_entry(&self) -> Entry {
430 let mut data = [0u8; 32];
431
432 let be_bytes = self.value.to_be_bytes_vec();
433
434 if be_bytes.len() <= 32 {
435 data[32 - be_bytes.len()..].copy_from_slice(&be_bytes);
436 } else {
437 data.copy_from_slice(&be_bytes[be_bytes.len() - 32..]);
438 }
439
440 Entry::new(TOTAL_DIFFICULTY, data.to_vec())
441 }
442
443 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
445 if entry.entry_type != TOTAL_DIFFICULTY {
446 return Err(E2sError::Ssz(format!(
447 "Invalid entry type for TotalDifficulty: expected {:02x}{:02x}, got {:02x}{:02x}",
448 TOTAL_DIFFICULTY[0], TOTAL_DIFFICULTY[1], entry.entry_type[0], entry.entry_type[1]
449 )));
450 }
451
452 if entry.data.len() != 32 {
453 return Err(E2sError::Ssz(format!(
454 "Invalid data length for TotalDifficulty: expected 32, got {}",
455 entry.data.len()
456 )));
457 }
458
459 let value = U256::from_be_slice(&entry.data);
461
462 Ok(Self { value })
463 }
464}
465
466#[derive(Debug, Clone)]
469pub struct Accumulator {
470 pub root: B256,
472}
473
474impl Accumulator {
475 pub const fn new(root: B256) -> Self {
477 Self { root }
478 }
479
480 pub fn to_entry(&self) -> Entry {
482 Entry::new(ACCUMULATOR, self.root.to_vec())
483 }
484
485 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
487 if entry.entry_type != ACCUMULATOR {
488 return Err(E2sError::Ssz(format!(
489 "Invalid entry type for Accumulator: expected {:02x}{:02x}, got {:02x}{:02x}",
490 ACCUMULATOR[0], ACCUMULATOR[1], entry.entry_type[0], entry.entry_type[1]
491 )));
492 }
493
494 if entry.data.len() != 32 {
495 return Err(E2sError::Ssz(format!(
496 "Invalid data length for Accumulator: expected 32, got {}",
497 entry.data.len()
498 )));
499 }
500
501 let mut root = [0u8; 32];
502 root.copy_from_slice(&entry.data);
503
504 Ok(Self { root: B256::from(root) })
505 }
506}
507
508#[derive(Debug, Clone)]
510pub struct BlockTuple {
511 pub header: CompressedHeader,
513
514 pub body: CompressedBody,
516
517 pub receipts: CompressedReceipts,
519
520 pub total_difficulty: TotalDifficulty,
522}
523
524impl BlockTuple {
525 pub const fn new(
527 header: CompressedHeader,
528 body: CompressedBody,
529 receipts: CompressedReceipts,
530 total_difficulty: TotalDifficulty,
531 ) -> Self {
532 Self { header, body, receipts, total_difficulty }
533 }
534
535 pub fn to_alloy_block<T: Decodable>(&self) -> Result<Block<T>, E2sError> {
537 let header: Header = self.header.decode()?;
538 let body: BlockBody<T> = self.body.decode()?;
539
540 Ok(Block::new(header, body))
541 }
542
543 pub fn from_alloy_block<T: Encodable, R: Encodable>(
545 block: &Block<T>,
546 receipts: &R,
547 total_difficulty: U256,
548 ) -> Result<Self, E2sError> {
549 let header = CompressedHeader::from_header(&block.header)?;
550 let body = CompressedBody::from_body(&block.body)?;
551
552 let compressed_receipts = CompressedReceipts::from_encodable(receipts)?;
553
554 let difficulty = TotalDifficulty::new(total_difficulty);
555
556 Ok(Self::new(header, body, compressed_receipts, difficulty))
557 }
558}
559
560#[cfg(test)]
561mod tests {
562 use super::*;
563 use crate::test_utils::{create_header, create_test_receipt, create_test_receipts};
564 use alloy_eips::eip4895::Withdrawals;
565 use alloy_primitives::{Bytes, U256};
566 use reth_ethereum_primitives::{Receipt, TxType};
567
568 #[test]
569 fn test_header_conversion_roundtrip() {
570 let header = create_header();
571
572 let compressed_header = CompressedHeader::from_header(&header).unwrap();
573
574 let decoded_header = compressed_header.decode_header().unwrap();
575
576 assert_eq!(header.number, decoded_header.number);
577 assert_eq!(header.difficulty, decoded_header.difficulty);
578 assert_eq!(header.timestamp, decoded_header.timestamp);
579 assert_eq!(header.gas_used, decoded_header.gas_used);
580 assert_eq!(header.parent_hash, decoded_header.parent_hash);
581 assert_eq!(header.base_fee_per_gas, decoded_header.base_fee_per_gas);
582 }
583
584 #[test]
585 fn test_block_body_conversion() {
586 let block_body: BlockBody<Bytes> =
587 BlockBody { transactions: vec![], ommers: vec![], withdrawals: None };
588
589 let compressed_body = CompressedBody::from_body(&block_body).unwrap();
590
591 let decoded_body: BlockBody<Bytes> = compressed_body.decode_body().unwrap();
592
593 assert_eq!(decoded_body.transactions.len(), 0);
594 assert_eq!(decoded_body.ommers.len(), 0);
595 assert_eq!(decoded_body.withdrawals, None);
596 }
597
598 #[test]
599 fn test_total_difficulty_roundtrip() {
600 let value = U256::from(123456789u64);
601
602 let total_difficulty = TotalDifficulty::new(value);
603
604 let entry = total_difficulty.to_entry();
605
606 assert_eq!(entry.entry_type, TOTAL_DIFFICULTY);
607
608 let recovered = TotalDifficulty::from_entry(&entry).unwrap();
609
610 assert_eq!(recovered.value, value);
611 }
612
613 #[test]
614 fn test_compression_roundtrip() {
615 let rlp_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
616
617 let compressed_header = CompressedHeader::from_rlp(&rlp_data).unwrap();
619 let decompressed = compressed_header.decompress().unwrap();
620 assert_eq!(decompressed, rlp_data);
621
622 let compressed_body = CompressedBody::from_rlp(&rlp_data).unwrap();
624 let decompressed = compressed_body.decompress().unwrap();
625 assert_eq!(decompressed, rlp_data);
626
627 let compressed_receipts = CompressedReceipts::from_rlp(&rlp_data).unwrap();
629 let decompressed = compressed_receipts.decompress().unwrap();
630 assert_eq!(decompressed, rlp_data);
631 }
632
633 #[test]
634 fn test_block_tuple_with_data() {
635 let header = create_header();
637
638 let transactions = vec![Bytes::from(vec![1, 2, 3, 4]), Bytes::from(vec![5, 6, 7, 8])];
639
640 let withdrawals = Some(Withdrawals(vec![]));
641
642 let block_body = BlockBody { transactions, ommers: vec![], withdrawals };
643
644 let block = Block::new(header, block_body);
645
646 let receipts: Vec<u8> = Vec::new();
647
648 let block_tuple =
649 BlockTuple::from_alloy_block(&block, &receipts, U256::from(123456u64)).unwrap();
650
651 let decoded_block: Block<Bytes> = block_tuple.to_alloy_block().unwrap();
653
654 assert_eq!(decoded_block.header.number, 100);
656 assert_eq!(decoded_block.body.transactions.len(), 2);
657 assert_eq!(decoded_block.body.transactions[0], Bytes::from(vec![1, 2, 3, 4]));
658 assert_eq!(decoded_block.body.transactions[1], Bytes::from(vec![5, 6, 7, 8]));
659 assert!(decoded_block.body.withdrawals.is_some());
660 }
661
662 #[test]
663 fn test_single_receipt_compression_roundtrip() {
664 let test_receipt = create_test_receipt(TxType::Eip1559, true, 21000, 2);
665
666 let compressed_receipts =
668 CompressedReceipts::from_encodable(&test_receipt).expect("Failed to compress receipt");
669
670 assert!(!compressed_receipts.data.is_empty());
672
673 let decoded_receipt: Receipt =
675 compressed_receipts.decode().expect("Failed to decode compressed receipt");
676
677 assert_eq!(decoded_receipt.tx_type, test_receipt.tx_type);
679 assert_eq!(decoded_receipt.success, test_receipt.success);
680 assert_eq!(decoded_receipt.cumulative_gas_used, test_receipt.cumulative_gas_used);
681 assert_eq!(decoded_receipt.logs.len(), test_receipt.logs.len());
682
683 for (original_log, decoded_log) in test_receipt.logs.iter().zip(decoded_receipt.logs.iter())
685 {
686 assert_eq!(decoded_log.address, original_log.address);
687 assert_eq!(decoded_log.data.topics(), original_log.data.topics());
688 }
689 }
690
691 #[test]
692 fn test_receipt_list_compression() {
693 let receipts = create_test_receipts();
694
695 let compressed_receipts = CompressedReceipts::from_encodable_list(&receipts)
697 .expect("Failed to compress receipt list");
698
699 let decoded_receipts: Vec<Receipt> =
703 compressed_receipts.decode().expect("Failed to decode compressed receipt list");
704
705 assert_eq!(decoded_receipts.len(), receipts.len());
707
708 for (original, decoded) in receipts.iter().zip(decoded_receipts.iter()) {
709 assert_eq!(decoded.tx_type, original.tx_type);
710 assert_eq!(decoded.success, original.success);
711 assert_eq!(decoded.cumulative_gas_used, original.cumulative_gas_used);
712 assert_eq!(decoded.logs.len(), original.logs.len());
713
714 for (original_log, decoded_log) in original.logs.iter().zip(decoded.logs.iter()) {
715 assert_eq!(decoded_log.address, original_log.address);
716 assert_eq!(decoded_log.data.topics(), original_log.data.topics());
717 }
718 }
719 }
720}