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