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 header: {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 header: {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 mut data = [0u8; 32];
429
430 let be_bytes = self.value.to_be_bytes_vec();
431
432 if be_bytes.len() <= 32 {
433 data[32 - be_bytes.len()..].copy_from_slice(&be_bytes);
434 } else {
435 data.copy_from_slice(&be_bytes[be_bytes.len() - 32..]);
436 }
437
438 Entry::new(TOTAL_DIFFICULTY, data.to_vec())
439 }
440
441 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
443 if entry.entry_type != TOTAL_DIFFICULTY {
444 return Err(E2sError::Ssz(format!(
445 "Invalid entry type for TotalDifficulty: expected {:02x}{:02x}, got {:02x}{:02x}",
446 TOTAL_DIFFICULTY[0], TOTAL_DIFFICULTY[1], entry.entry_type[0], entry.entry_type[1]
447 )));
448 }
449
450 if entry.data.len() != 32 {
451 return Err(E2sError::Ssz(format!(
452 "Invalid data length for TotalDifficulty: expected 32, got {}",
453 entry.data.len()
454 )));
455 }
456
457 let value = U256::from_be_slice(&entry.data);
459
460 Ok(Self { value })
461 }
462}
463
464#[derive(Debug, Clone)]
467pub struct Accumulator {
468 pub root: B256,
470}
471
472impl Accumulator {
473 pub const fn new(root: B256) -> Self {
475 Self { root }
476 }
477
478 pub fn to_entry(&self) -> Entry {
480 Entry::new(ACCUMULATOR, self.root.to_vec())
481 }
482
483 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
485 if entry.entry_type != ACCUMULATOR {
486 return Err(E2sError::Ssz(format!(
487 "Invalid entry type for Accumulator: expected {:02x}{:02x}, got {:02x}{:02x}",
488 ACCUMULATOR[0], ACCUMULATOR[1], entry.entry_type[0], entry.entry_type[1]
489 )));
490 }
491
492 if entry.data.len() != 32 {
493 return Err(E2sError::Ssz(format!(
494 "Invalid data length for Accumulator: expected 32, got {}",
495 entry.data.len()
496 )));
497 }
498
499 let mut root = [0u8; 32];
500 root.copy_from_slice(&entry.data);
501
502 Ok(Self { root: B256::from(root) })
503 }
504}
505
506#[derive(Debug, Clone)]
508pub struct BlockTuple {
509 pub header: CompressedHeader,
511
512 pub body: CompressedBody,
514
515 pub receipts: CompressedReceipts,
517
518 pub total_difficulty: TotalDifficulty,
520}
521
522impl BlockTuple {
523 pub const fn new(
525 header: CompressedHeader,
526 body: CompressedBody,
527 receipts: CompressedReceipts,
528 total_difficulty: TotalDifficulty,
529 ) -> Self {
530 Self { header, body, receipts, total_difficulty }
531 }
532
533 pub fn to_alloy_block<T: Decodable>(&self) -> Result<Block<T>, E2sError> {
535 let header: Header = self.header.decode()?;
536 let body: BlockBody<T> = self.body.decode()?;
537
538 Ok(Block::new(header, body))
539 }
540
541 pub fn from_alloy_block<T: Encodable, R: Encodable>(
543 block: &Block<T>,
544 receipts: &R,
545 total_difficulty: U256,
546 ) -> Result<Self, E2sError> {
547 let header = CompressedHeader::from_header(&block.header)?;
548 let body = CompressedBody::from_body(&block.body)?;
549
550 let compressed_receipts = CompressedReceipts::from_encodable(receipts)?;
551
552 let difficulty = TotalDifficulty::new(total_difficulty);
553
554 Ok(Self::new(header, body, compressed_receipts, difficulty))
555 }
556}
557
558#[cfg(test)]
559mod tests {
560 use super::*;
561 use crate::test_utils::{create_header, create_test_receipt, create_test_receipts};
562 use alloy_eips::eip4895::Withdrawals;
563 use alloy_primitives::{Bytes, U256};
564 use reth_ethereum_primitives::{Receipt, TxType};
565
566 #[test]
567 fn test_header_conversion_roundtrip() {
568 let header = create_header();
569
570 let compressed_header = CompressedHeader::from_header(&header).unwrap();
571
572 let decoded_header = compressed_header.decode_header().unwrap();
573
574 assert_eq!(header.number, decoded_header.number);
575 assert_eq!(header.difficulty, decoded_header.difficulty);
576 assert_eq!(header.timestamp, decoded_header.timestamp);
577 assert_eq!(header.gas_used, decoded_header.gas_used);
578 assert_eq!(header.parent_hash, decoded_header.parent_hash);
579 assert_eq!(header.base_fee_per_gas, decoded_header.base_fee_per_gas);
580 }
581
582 #[test]
583 fn test_block_body_conversion() {
584 let block_body: BlockBody<Bytes> =
585 BlockBody { transactions: vec![], ommers: vec![], withdrawals: None };
586
587 let compressed_body = CompressedBody::from_body(&block_body).unwrap();
588
589 let decoded_body: BlockBody<Bytes> = compressed_body.decode_body().unwrap();
590
591 assert_eq!(decoded_body.transactions.len(), 0);
592 assert_eq!(decoded_body.ommers.len(), 0);
593 assert_eq!(decoded_body.withdrawals, None);
594 }
595
596 #[test]
597 fn test_total_difficulty_roundtrip() {
598 let value = U256::from(123456789u64);
599
600 let total_difficulty = TotalDifficulty::new(value);
601
602 let entry = total_difficulty.to_entry();
603
604 assert_eq!(entry.entry_type, TOTAL_DIFFICULTY);
605
606 let recovered = TotalDifficulty::from_entry(&entry).unwrap();
607
608 assert_eq!(recovered.value, value);
609 }
610
611 #[test]
612 fn test_compression_roundtrip() {
613 let rlp_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
614
615 let compressed_header = CompressedHeader::from_rlp(&rlp_data).unwrap();
617 let decompressed = compressed_header.decompress().unwrap();
618 assert_eq!(decompressed, rlp_data);
619
620 let compressed_body = CompressedBody::from_rlp(&rlp_data).unwrap();
622 let decompressed = compressed_body.decompress().unwrap();
623 assert_eq!(decompressed, rlp_data);
624
625 let compressed_receipts = CompressedReceipts::from_rlp(&rlp_data).unwrap();
627 let decompressed = compressed_receipts.decompress().unwrap();
628 assert_eq!(decompressed, rlp_data);
629 }
630
631 #[test]
632 fn test_block_tuple_with_data() {
633 let header = create_header();
635
636 let transactions = vec![Bytes::from(vec![1, 2, 3, 4]), Bytes::from(vec![5, 6, 7, 8])];
637
638 let withdrawals = Some(Withdrawals(vec![]));
639
640 let block_body = BlockBody { transactions, ommers: vec![], withdrawals };
641
642 let block = Block::new(header, block_body);
643
644 let receipts: Vec<u8> = Vec::new();
645
646 let block_tuple =
647 BlockTuple::from_alloy_block(&block, &receipts, U256::from(123456u64)).unwrap();
648
649 let decoded_block: Block<Bytes> = block_tuple.to_alloy_block().unwrap();
651
652 assert_eq!(decoded_block.header.number, 100);
654 assert_eq!(decoded_block.body.transactions.len(), 2);
655 assert_eq!(decoded_block.body.transactions[0], Bytes::from(vec![1, 2, 3, 4]));
656 assert_eq!(decoded_block.body.transactions[1], Bytes::from(vec![5, 6, 7, 8]));
657 assert!(decoded_block.body.withdrawals.is_some());
658 }
659
660 #[test]
661 fn test_single_receipt_compression_roundtrip() {
662 let test_receipt = create_test_receipt(TxType::Eip1559, true, 21000, 2);
663
664 let compressed_receipts =
666 CompressedReceipts::from_encodable(&test_receipt).expect("Failed to compress receipt");
667
668 assert!(!compressed_receipts.data.is_empty());
670
671 let decoded_receipt: Receipt =
673 compressed_receipts.decode().expect("Failed to decode compressed receipt");
674
675 assert_eq!(decoded_receipt.tx_type, test_receipt.tx_type);
677 assert_eq!(decoded_receipt.success, test_receipt.success);
678 assert_eq!(decoded_receipt.cumulative_gas_used, test_receipt.cumulative_gas_used);
679 assert_eq!(decoded_receipt.logs.len(), test_receipt.logs.len());
680
681 for (original_log, decoded_log) in test_receipt.logs.iter().zip(decoded_receipt.logs.iter())
683 {
684 assert_eq!(decoded_log.address, original_log.address);
685 assert_eq!(decoded_log.data.topics(), original_log.data.topics());
686 }
687 }
688
689 #[test]
690 fn test_receipt_list_compression() {
691 let receipts = create_test_receipts();
692
693 let compressed_receipts = CompressedReceipts::from_encodable_list(&receipts)
695 .expect("Failed to compress receipt list");
696
697 let decoded_receipts: Vec<Receipt> =
701 compressed_receipts.decode().expect("Failed to decode compressed receipt list");
702
703 assert_eq!(decoded_receipts.len(), receipts.len());
705
706 for (original, decoded) in receipts.iter().zip(decoded_receipts.iter()) {
707 assert_eq!(decoded.tx_type, original.tx_type);
708 assert_eq!(decoded.success, original.success);
709 assert_eq!(decoded.cumulative_gas_used, original.cumulative_gas_used);
710 assert_eq!(decoded.logs.len(), original.logs.len());
711
712 for (original_log, decoded_log) in original.logs.iter().zip(decoded.logs.iter()) {
713 assert_eq!(decoded_log.address, original_log.address);
714 assert_eq!(decoded_log.data.topics(), original_log.data.topics());
715 }
716 }
717 }
718}