1use crate::e2s_types::{E2sError, Entry};
14use alloy_consensus::{Block, BlockBody, Header};
15use alloy_primitives::{B256, U256};
16use alloy_rlp::{Decodable, Encodable};
17use snap::{read::FrameDecoder, write::FrameEncoder};
18use std::{
19 io::{Read, Write},
20 marker::PhantomData,
21};
22
23pub const COMPRESSED_HEADER: [u8; 2] = [0x03, 0x00];
26
27pub const COMPRESSED_BODY: [u8; 2] = [0x04, 0x00];
29
30pub const COMPRESSED_RECEIPTS: [u8; 2] = [0x05, 0x00];
32
33pub const TOTAL_DIFFICULTY: [u8; 2] = [0x06, 0x00];
35
36pub const ACCUMULATOR: [u8; 2] = [0x07, 0x00];
38
39pub const MAX_BLOCKS_PER_ERA1: usize = 8192;
41
42#[derive(Debug, Clone, Default)]
44pub struct SnappyRlpCodec<T> {
45 _phantom: PhantomData<T>,
46}
47
48impl<T> SnappyRlpCodec<T> {
49 pub fn new() -> Self {
51 Self { _phantom: PhantomData }
52 }
53}
54
55impl<T: Decodable> SnappyRlpCodec<T> {
56 pub fn decode(&self, compressed_data: &[u8]) -> Result<T, E2sError> {
58 let mut decoder = FrameDecoder::new(compressed_data);
59 let mut decompressed = Vec::new();
60 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
61 E2sError::SnappyDecompression(format!("Failed to decompress data: {}", e))
62 })?;
63
64 let mut slice = decompressed.as_slice();
65 T::decode(&mut slice)
66 .map_err(|e| E2sError::Rlp(format!("Failed to decode RLP data: {}", e)))
67 }
68}
69
70impl<T: Encodable> SnappyRlpCodec<T> {
71 pub fn encode(&self, data: &T) -> Result<Vec<u8>, E2sError> {
73 let mut rlp_data = Vec::new();
74 data.encode(&mut rlp_data);
75
76 let mut compressed = Vec::new();
77 {
78 let mut encoder = FrameEncoder::new(&mut compressed);
79
80 Write::write_all(&mut encoder, &rlp_data).map_err(|e| {
81 E2sError::SnappyCompression(format!("Failed to compress data: {}", e))
82 })?;
83
84 encoder.flush().map_err(|e| {
85 E2sError::SnappyCompression(format!("Failed to flush encoder: {}", e))
86 })?;
87 }
88
89 Ok(compressed)
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct CompressedHeader {
96 pub data: Vec<u8>,
98}
99
100pub trait DecodeCompressed {
102 fn decode<T: Decodable>(&self) -> Result<T, E2sError>;
104}
105
106impl CompressedHeader {
107 pub fn new(data: Vec<u8>) -> Self {
109 Self { data }
110 }
111
112 pub fn from_rlp(rlp_data: &[u8]) -> Result<Self, E2sError> {
114 let mut compressed = Vec::new();
115 {
116 let mut encoder = FrameEncoder::new(&mut compressed);
117
118 Write::write_all(&mut encoder, rlp_data).map_err(|e| {
119 E2sError::SnappyCompression(format!("Failed to compress header: {}", e))
120 })?;
121
122 encoder.flush().map_err(|e| {
123 E2sError::SnappyCompression(format!("Failed to flush encoder: {}", e))
124 })?;
125 }
126 Ok(Self { data: compressed })
127 }
128
129 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
131 let mut decoder = FrameDecoder::new(self.data.as_slice());
132 let mut decompressed = Vec::new();
133 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
134 E2sError::SnappyDecompression(format!("Failed to decompress header: {}", e))
135 })?;
136
137 Ok(decompressed)
138 }
139
140 pub fn to_entry(&self) -> Entry {
142 Entry::new(COMPRESSED_HEADER, self.data.clone())
143 }
144
145 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
147 if entry.entry_type != COMPRESSED_HEADER {
148 return Err(E2sError::Ssz(format!(
149 "Invalid entry type for CompressedHeader: expected {:02x}{:02x}, got {:02x}{:02x}",
150 COMPRESSED_HEADER[0],
151 COMPRESSED_HEADER[1],
152 entry.entry_type[0],
153 entry.entry_type[1]
154 )));
155 }
156
157 Ok(Self { data: entry.data.clone() })
158 }
159
160 pub fn decode_header(&self) -> Result<Header, E2sError> {
162 self.decode()
163 }
164
165 pub fn from_header(header: &Header) -> Result<Self, E2sError> {
167 let encoder = SnappyRlpCodec::<Header>::new();
168 let compressed = encoder.encode(header)?;
169 Ok(Self::new(compressed))
170 }
171}
172
173impl DecodeCompressed for CompressedHeader {
174 fn decode<T: Decodable>(&self) -> Result<T, E2sError> {
175 let decoder = SnappyRlpCodec::<T>::new();
176 decoder.decode(&self.data)
177 }
178}
179
180#[derive(Debug, Clone)]
182pub struct CompressedBody {
183 pub data: Vec<u8>,
185}
186
187impl CompressedBody {
188 pub fn new(data: Vec<u8>) -> Self {
190 Self { data }
191 }
192
193 pub fn from_rlp(rlp_data: &[u8]) -> Result<Self, E2sError> {
195 let mut compressed = Vec::new();
196 {
197 let mut encoder = FrameEncoder::new(&mut compressed);
198
199 Write::write_all(&mut encoder, rlp_data).map_err(|e| {
200 E2sError::SnappyCompression(format!("Failed to compress header: {}", e))
201 })?;
202
203 encoder.flush().map_err(|e| {
204 E2sError::SnappyCompression(format!("Failed to flush encoder: {}", e))
205 })?;
206 }
207 Ok(Self { data: compressed })
208 }
209
210 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
212 let mut decoder = FrameDecoder::new(self.data.as_slice());
213 let mut decompressed = Vec::new();
214 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
215 E2sError::SnappyDecompression(format!("Failed to decompress body: {}", e))
216 })?;
217
218 Ok(decompressed)
219 }
220
221 pub fn to_entry(&self) -> Entry {
223 Entry::new(COMPRESSED_BODY, self.data.clone())
224 }
225
226 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
228 if entry.entry_type != COMPRESSED_BODY {
229 return Err(E2sError::Ssz(format!(
230 "Invalid entry type for CompressedBody: expected {:02x}{:02x}, got {:02x}{:02x}",
231 COMPRESSED_BODY[0], COMPRESSED_BODY[1], entry.entry_type[0], entry.entry_type[1]
232 )));
233 }
234
235 Ok(Self { data: entry.data.clone() })
236 }
237
238 pub fn decode_body<T: Decodable, H: Decodable>(&self) -> Result<BlockBody<T, H>, E2sError> {
240 self.decode()
241 }
242
243 pub fn from_body<T: Encodable, H: Encodable>(body: &BlockBody<T, H>) -> Result<Self, E2sError> {
245 let encoder = SnappyRlpCodec::<BlockBody<T, H>>::new();
246 let compressed = encoder.encode(body)?;
247 Ok(Self::new(compressed))
248 }
249}
250
251impl DecodeCompressed for CompressedBody {
252 fn decode<T: Decodable>(&self) -> Result<T, E2sError> {
253 let decoder = SnappyRlpCodec::<T>::new();
254 decoder.decode(&self.data)
255 }
256}
257
258#[derive(Debug, Clone)]
260pub struct CompressedReceipts {
261 pub data: Vec<u8>,
263}
264
265impl CompressedReceipts {
266 pub fn new(data: Vec<u8>) -> Self {
268 Self { data }
269 }
270
271 pub fn from_rlp(rlp_data: &[u8]) -> Result<Self, E2sError> {
273 let mut compressed = Vec::new();
274 {
275 let mut encoder = FrameEncoder::new(&mut compressed);
276
277 Write::write_all(&mut encoder, rlp_data).map_err(|e| {
278 E2sError::SnappyCompression(format!("Failed to compress header: {}", e))
279 })?;
280
281 encoder.flush().map_err(|e| {
282 E2sError::SnappyCompression(format!("Failed to flush encoder: {}", e))
283 })?;
284 }
285 Ok(Self { data: compressed })
286 }
287 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
289 let mut decoder = FrameDecoder::new(self.data.as_slice());
290 let mut decompressed = Vec::new();
291 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
292 E2sError::SnappyDecompression(format!("Failed to decompress receipts: {}", e))
293 })?;
294
295 Ok(decompressed)
296 }
297
298 pub fn to_entry(&self) -> Entry {
300 Entry::new(COMPRESSED_RECEIPTS, self.data.clone())
301 }
302
303 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
305 if entry.entry_type != COMPRESSED_RECEIPTS {
306 return Err(E2sError::Ssz(format!(
307 "Invalid entry type for CompressedReceipts: expected {:02x}{:02x}, got {:02x}{:02x}",
308 COMPRESSED_RECEIPTS[0], COMPRESSED_RECEIPTS[1],
309 entry.entry_type[0], entry.entry_type[1]
310 )));
311 }
312
313 Ok(Self { data: entry.data.clone() })
314 }
315
316 pub fn decode<T: Decodable>(&self) -> Result<T, E2sError> {
318 let decoder = SnappyRlpCodec::<T>::new();
319 decoder.decode(&self.data)
320 }
321
322 pub fn from_encodable<T: Encodable>(data: &T) -> Result<Self, E2sError> {
324 let encoder = SnappyRlpCodec::<T>::new();
325 let compressed = encoder.encode(data)?;
326 Ok(Self::new(compressed))
327 }
328}
329
330impl DecodeCompressed for CompressedReceipts {
331 fn decode<T: Decodable>(&self) -> Result<T, E2sError> {
332 let decoder = SnappyRlpCodec::<T>::new();
333 decoder.decode(&self.data)
334 }
335}
336
337#[derive(Debug, Clone)]
339pub struct TotalDifficulty {
340 pub value: U256,
342}
343
344impl TotalDifficulty {
345 pub fn new(value: U256) -> Self {
347 Self { value }
348 }
349
350 pub fn to_entry(&self) -> Entry {
352 let mut data = [0u8; 32];
353
354 let be_bytes = self.value.to_be_bytes_vec();
355
356 if be_bytes.len() <= 32 {
357 data[32 - be_bytes.len()..].copy_from_slice(&be_bytes);
358 } else {
359 data.copy_from_slice(&be_bytes[be_bytes.len() - 32..]);
360 }
361
362 Entry::new(TOTAL_DIFFICULTY, data.to_vec())
363 }
364
365 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
367 if entry.entry_type != TOTAL_DIFFICULTY {
368 return Err(E2sError::Ssz(format!(
369 "Invalid entry type for TotalDifficulty: expected {:02x}{:02x}, got {:02x}{:02x}",
370 TOTAL_DIFFICULTY[0], TOTAL_DIFFICULTY[1], entry.entry_type[0], entry.entry_type[1]
371 )));
372 }
373
374 if entry.data.len() != 32 {
375 return Err(E2sError::Ssz(format!(
376 "Invalid data length for TotalDifficulty: expected 32, got {}",
377 entry.data.len()
378 )));
379 }
380
381 let value = U256::from_be_slice(&entry.data);
383
384 Ok(Self { value })
385 }
386}
387
388#[derive(Debug, Clone)]
391pub struct Accumulator {
392 pub root: B256,
394}
395
396impl Accumulator {
397 pub fn new(root: B256) -> Self {
399 Self { root }
400 }
401
402 pub fn to_entry(&self) -> Entry {
404 Entry::new(ACCUMULATOR, self.root.to_vec())
405 }
406
407 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
409 if entry.entry_type != ACCUMULATOR {
410 return Err(E2sError::Ssz(format!(
411 "Invalid entry type for Accumulator: expected {:02x}{:02x}, got {:02x}{:02x}",
412 ACCUMULATOR[0], ACCUMULATOR[1], entry.entry_type[0], entry.entry_type[1]
413 )));
414 }
415
416 if entry.data.len() != 32 {
417 return Err(E2sError::Ssz(format!(
418 "Invalid data length for Accumulator: expected 32, got {}",
419 entry.data.len()
420 )));
421 }
422
423 let mut root = [0u8; 32];
424 root.copy_from_slice(&entry.data);
425
426 Ok(Self { root: B256::from(root) })
427 }
428}
429
430#[derive(Debug, Clone)]
432pub struct BlockTuple {
433 pub header: CompressedHeader,
435
436 pub body: CompressedBody,
438
439 pub receipts: CompressedReceipts,
441
442 pub total_difficulty: TotalDifficulty,
444}
445
446impl BlockTuple {
447 pub fn new(
449 header: CompressedHeader,
450 body: CompressedBody,
451 receipts: CompressedReceipts,
452 total_difficulty: TotalDifficulty,
453 ) -> Self {
454 Self { header, body, receipts, total_difficulty }
455 }
456
457 pub fn to_alloy_block<T: Decodable>(&self) -> Result<Block<T>, E2sError> {
459 let header: Header = self.header.decode()?;
460 let body: BlockBody<T> = self.body.decode()?;
461
462 Ok(Block::new(header, body))
463 }
464
465 pub fn from_alloy_block<T: Encodable, R: Encodable>(
467 block: &Block<T>,
468 receipts: &R,
469 total_difficulty: U256,
470 ) -> Result<Self, E2sError> {
471 let header = CompressedHeader::from_header(&block.header)?;
472 let body = CompressedBody::from_body(&block.body)?;
473
474 let compressed_receipts = CompressedReceipts::from_encodable(receipts)?;
475
476 let difficulty = TotalDifficulty::new(total_difficulty);
477
478 Ok(Self::new(header, body, compressed_receipts, difficulty))
479 }
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485 use alloy_eips::eip4895::Withdrawals;
486 use alloy_primitives::{Address, Bytes, B64};
487
488 #[test]
489 fn test_header_conversion_roundtrip() {
490 let header = Header {
491 parent_hash: B256::default(),
492 ommers_hash: B256::default(),
493 beneficiary: Address::default(),
494 state_root: B256::default(),
495 transactions_root: B256::default(),
496 receipts_root: B256::default(),
497 logs_bloom: Default::default(),
498 difficulty: U256::from(123456u64),
499 number: 100,
500 gas_limit: 5000000,
501 gas_used: 21000,
502 timestamp: 1609459200,
503 extra_data: Bytes::default(),
504 mix_hash: B256::default(),
505 nonce: B64::default(),
506 base_fee_per_gas: Some(10),
507 withdrawals_root: None,
508 blob_gas_used: None,
509 excess_blob_gas: None,
510 parent_beacon_block_root: None,
511 requests_hash: None,
512 };
513
514 let compressed_header = CompressedHeader::from_header(&header).unwrap();
515
516 let decoded_header = compressed_header.decode_header().unwrap();
517
518 assert_eq!(header.number, decoded_header.number);
519 assert_eq!(header.difficulty, decoded_header.difficulty);
520 assert_eq!(header.timestamp, decoded_header.timestamp);
521 assert_eq!(header.gas_used, decoded_header.gas_used);
522 assert_eq!(header.parent_hash, decoded_header.parent_hash);
523 assert_eq!(header.base_fee_per_gas, decoded_header.base_fee_per_gas);
524 }
525
526 #[test]
527 fn test_block_body_conversion() {
528 let block_body: BlockBody<Bytes> =
529 BlockBody { transactions: vec![], ommers: vec![], withdrawals: None };
530
531 let compressed_body = CompressedBody::from_body(&block_body).unwrap();
532
533 let decoded_body: BlockBody<Bytes> = compressed_body.decode_body().unwrap();
534
535 assert_eq!(decoded_body.transactions.len(), 0);
536 assert_eq!(decoded_body.ommers.len(), 0);
537 assert_eq!(decoded_body.withdrawals, None);
538 }
539
540 #[test]
541 fn test_total_difficulty_roundtrip() {
542 let value = U256::from(123456789u64);
543
544 let total_difficulty = TotalDifficulty::new(value);
545
546 let entry = total_difficulty.to_entry();
547
548 assert_eq!(entry.entry_type, TOTAL_DIFFICULTY);
549
550 let recovered = TotalDifficulty::from_entry(&entry).unwrap();
551
552 assert_eq!(recovered.value, value);
553 }
554
555 #[test]
556 fn test_compression_roundtrip() {
557 let rlp_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
558
559 let compressed_header = CompressedHeader::from_rlp(&rlp_data).unwrap();
561 let decompressed = compressed_header.decompress().unwrap();
562 assert_eq!(decompressed, rlp_data);
563
564 let compressed_body = CompressedBody::from_rlp(&rlp_data).unwrap();
566 let decompressed = compressed_body.decompress().unwrap();
567 assert_eq!(decompressed, rlp_data);
568
569 let compressed_receipts = CompressedReceipts::from_rlp(&rlp_data).unwrap();
571 let decompressed = compressed_receipts.decompress().unwrap();
572 assert_eq!(decompressed, rlp_data);
573 }
574
575 #[test]
576 fn test_block_tuple_with_data() {
577 let header = Header {
579 parent_hash: B256::default(),
580 ommers_hash: B256::default(),
581 beneficiary: Address::default(),
582 state_root: B256::default(),
583 transactions_root: B256::default(),
584 receipts_root: B256::default(),
585 logs_bloom: Default::default(),
586 difficulty: U256::from(123456u64),
587 number: 100,
588 gas_limit: 5000000,
589 gas_used: 21000,
590 timestamp: 1609459200,
591 extra_data: Bytes::default(),
592 mix_hash: B256::default(),
593 nonce: B64::default(),
594 base_fee_per_gas: Some(10),
595 withdrawals_root: Some(B256::default()),
596 blob_gas_used: None,
597 excess_blob_gas: None,
598 parent_beacon_block_root: None,
599 requests_hash: None,
600 };
601
602 let transactions = vec![Bytes::from(vec![1, 2, 3, 4]), Bytes::from(vec![5, 6, 7, 8])];
603
604 let withdrawals = Some(Withdrawals(vec![]));
605
606 let block_body = BlockBody { transactions, ommers: vec![], withdrawals };
607
608 let block = Block::new(header, block_body);
609
610 let receipts: Vec<u8> = Vec::new();
611
612 let block_tuple =
613 BlockTuple::from_alloy_block(&block, &receipts, U256::from(123456u64)).unwrap();
614
615 let decoded_block: Block<Bytes> = block_tuple.to_alloy_block().unwrap();
617
618 assert_eq!(decoded_block.header.number, 100);
620 assert_eq!(decoded_block.body.transactions.len(), 2);
621 assert_eq!(decoded_block.body.transactions[0], Bytes::from(vec![1, 2, 3, 4]));
622 assert_eq!(decoded_block.body.transactions[1], Bytes::from(vec![5, 6, 7, 8]));
623 assert!(decoded_block.body.withdrawals.is_some());
624 }
625}