reth_era/
consensus_types.rs1use crate::{
4 e2s_types::{E2sError, Entry},
5 DecodeCompressedSsz,
6};
7use snap::{read::FrameDecoder, write::FrameEncoder};
8use ssz::Decode;
9use std::io::{Read, Write};
10
11pub const COMPRESSED_SIGNED_BEACON_BLOCK: [u8; 2] = [0x01, 0x00];
13
14pub const COMPRESSED_BEACON_STATE: [u8; 2] = [0x02, 0x00];
16
17#[derive(Debug, Clone)]
21pub struct CompressedSignedBeaconBlock {
22 pub data: Vec<u8>,
24}
25
26impl CompressedSignedBeaconBlock {
27 pub const fn new(data: Vec<u8>) -> Self {
29 Self { data }
30 }
31
32 pub fn from_ssz(ssz_data: &[u8]) -> Result<Self, E2sError> {
34 let mut compressed = Vec::new();
35 {
36 let mut encoder = FrameEncoder::new(&mut compressed);
37
38 Write::write_all(&mut encoder, ssz_data).map_err(|e| {
39 E2sError::SnappyCompression(format!("Failed to compress signed beacon block: {e}"))
40 })?;
41
42 encoder.flush().map_err(|e| {
43 E2sError::SnappyCompression(format!("Failed to flush encoder: {e}"))
44 })?;
45 }
46 Ok(Self { data: compressed })
47 }
48
49 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
51 let mut decoder = FrameDecoder::new(self.data.as_slice());
52 let mut decompressed = Vec::new();
53 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
54 E2sError::SnappyDecompression(format!("Failed to decompress signed beacon block: {e}"))
55 })?;
56
57 Ok(decompressed)
58 }
59
60 pub fn to_entry(&self) -> Entry {
62 Entry::new(COMPRESSED_SIGNED_BEACON_BLOCK, self.data.clone())
63 }
64
65 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
67 if entry.entry_type != COMPRESSED_SIGNED_BEACON_BLOCK {
68 return Err(E2sError::Ssz(format!(
69 "Invalid entry type for CompressedSignedBeaconBlock: expected {:02x}{:02x}, got {:02x}{:02x}",
70 COMPRESSED_SIGNED_BEACON_BLOCK[0],
71 COMPRESSED_SIGNED_BEACON_BLOCK[1],
72 entry.entry_type[0],
73 entry.entry_type[1]
74 )));
75 }
76
77 Ok(Self { data: entry.data.clone() })
78 }
79
80 pub fn decode_to_ssz(&self) -> Result<Vec<u8>, E2sError> {
82 self.decompress()
83 }
84}
85
86impl DecodeCompressedSsz for CompressedSignedBeaconBlock {
87 fn decode<T: Decode>(&self) -> Result<T, E2sError> {
88 let ssz_bytes = self.decompress()?;
89 T::from_ssz_bytes(&ssz_bytes).map_err(|e| {
90 E2sError::Ssz(format!("Failed to decode SSZ data into target type: {e:?}"))
91 })
92 }
93}
94
95#[derive(Debug, Clone)]
99pub struct CompressedBeaconState {
100 pub data: Vec<u8>,
102}
103
104impl CompressedBeaconState {
105 pub const fn new(data: Vec<u8>) -> Self {
107 Self { data }
108 }
109
110 pub fn from_ssz(ssz_data: &[u8]) -> Result<Self, E2sError> {
112 let mut compressed = Vec::new();
113 {
114 let mut encoder = FrameEncoder::new(&mut compressed);
115
116 Write::write_all(&mut encoder, ssz_data).map_err(|e| {
117 E2sError::SnappyCompression(format!("Failed to compress beacon state: {e}"))
118 })?;
119
120 encoder.flush().map_err(|e| {
121 E2sError::SnappyCompression(format!("Failed to flush encoder: {e}"))
122 })?;
123 }
124 Ok(Self { data: compressed })
125 }
126
127 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
129 let mut decoder = FrameDecoder::new(self.data.as_slice());
130 let mut decompressed = Vec::new();
131 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
132 E2sError::SnappyDecompression(format!("Failed to decompress beacon state: {e}"))
133 })?;
134
135 Ok(decompressed)
136 }
137
138 pub fn to_entry(&self) -> Entry {
140 Entry::new(COMPRESSED_BEACON_STATE, self.data.clone())
141 }
142
143 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
145 if entry.entry_type != COMPRESSED_BEACON_STATE {
146 return Err(E2sError::Ssz(format!(
147 "Invalid entry type for CompressedBeaconState: expected {:02x}{:02x}, got {:02x}{:02x}",
148 COMPRESSED_BEACON_STATE[0],
149 COMPRESSED_BEACON_STATE[1],
150 entry.entry_type[0],
151 entry.entry_type[1]
152 )));
153 }
154
155 Ok(Self { data: entry.data.clone() })
156 }
157
158 pub fn decode_to_ssz(&self) -> Result<Vec<u8>, E2sError> {
160 self.decompress()
161 }
162}
163
164impl DecodeCompressedSsz for CompressedBeaconState {
165 fn decode<T: Decode>(&self) -> Result<T, E2sError> {
166 let ssz_bytes = self.decompress()?;
167 T::from_ssz_bytes(&ssz_bytes).map_err(|e| {
168 E2sError::Ssz(format!("Failed to decode SSZ data into target type: {e:?}"))
169 })
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn test_signed_beacon_block_compression_roundtrip() {
179 let ssz_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
180
181 let compressed_block = CompressedSignedBeaconBlock::from_ssz(&ssz_data).unwrap();
182 let decompressed = compressed_block.decompress().unwrap();
183
184 assert_eq!(decompressed, ssz_data);
185 }
186
187 #[test]
188 fn test_beacon_state_compression_roundtrip() {
189 let ssz_data = vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
190
191 let compressed_state = CompressedBeaconState::from_ssz(&ssz_data).unwrap();
192 let decompressed = compressed_state.decompress().unwrap();
193
194 assert_eq!(decompressed, ssz_data);
195 }
196
197 #[test]
198 fn test_entry_conversion_signed_beacon_block() {
199 let ssz_data = vec![1, 2, 3, 4, 5];
200 let compressed_block = CompressedSignedBeaconBlock::from_ssz(&ssz_data).unwrap();
201
202 let entry = compressed_block.to_entry();
203 assert_eq!(entry.entry_type, COMPRESSED_SIGNED_BEACON_BLOCK);
204
205 let recovered = CompressedSignedBeaconBlock::from_entry(&entry).unwrap();
206 let recovered_ssz = recovered.decode_to_ssz().unwrap();
207
208 assert_eq!(recovered_ssz, ssz_data);
209 }
210
211 #[test]
212 fn test_entry_conversion_beacon_state() {
213 let ssz_data = vec![5, 4, 3, 2, 1];
214 let compressed_state = CompressedBeaconState::from_ssz(&ssz_data).unwrap();
215
216 let entry = compressed_state.to_entry();
217 assert_eq!(entry.entry_type, COMPRESSED_BEACON_STATE);
218
219 let recovered = CompressedBeaconState::from_entry(&entry).unwrap();
220 let recovered_ssz = recovered.decode_to_ssz().unwrap();
221
222 assert_eq!(recovered_ssz, ssz_data);
223 }
224
225 #[test]
226 fn test_invalid_entry_type() {
227 let invalid_entry = Entry::new([0xFF, 0xFF], vec![1, 2, 3]);
228
229 let result = CompressedSignedBeaconBlock::from_entry(&invalid_entry);
230 assert!(result.is_err());
231
232 let result = CompressedBeaconState::from_entry(&invalid_entry);
233 assert!(result.is_err());
234 }
235}