reth_era/era/types/
consensus.rs1use crate::e2s::{error::E2sError, types::Entry};
64use snap::{read::FrameDecoder, write::FrameEncoder};
65use std::io::{Read, Write};
66
67pub const COMPRESSED_SIGNED_BEACON_BLOCK: [u8; 2] = [0x01, 0x00];
69
70pub const COMPRESSED_BEACON_STATE: [u8; 2] = [0x02, 0x00];
72
73#[derive(Debug, Clone)]
77pub struct CompressedSignedBeaconBlock {
78 pub data: Vec<u8>,
80}
81
82impl CompressedSignedBeaconBlock {
83 pub const fn new(data: Vec<u8>) -> Self {
85 Self { data }
86 }
87
88 pub fn from_ssz(ssz_data: &[u8]) -> Result<Self, E2sError> {
90 let mut compressed = Vec::new();
91 {
92 let mut encoder = FrameEncoder::new(&mut compressed);
93
94 Write::write_all(&mut encoder, ssz_data).map_err(|e| {
95 E2sError::SnappyCompression(format!("Failed to compress signed beacon block: {e}"))
96 })?;
97
98 encoder.flush().map_err(|e| {
99 E2sError::SnappyCompression(format!("Failed to flush encoder: {e}"))
100 })?;
101 }
102 Ok(Self { data: compressed })
103 }
104
105 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
107 let mut decoder = FrameDecoder::new(self.data.as_slice());
108 let mut decompressed = Vec::new();
109 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
110 E2sError::SnappyDecompression(format!("Failed to decompress signed beacon block: {e}"))
111 })?;
112
113 Ok(decompressed)
114 }
115
116 pub fn to_entry(&self) -> Entry {
118 Entry::new(COMPRESSED_SIGNED_BEACON_BLOCK, self.data.clone())
119 }
120
121 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
123 if entry.entry_type != COMPRESSED_SIGNED_BEACON_BLOCK {
124 return Err(E2sError::Ssz(format!(
125 "Invalid entry type for CompressedSignedBeaconBlock: expected {:02x}{:02x}, got {:02x}{:02x}",
126 COMPRESSED_SIGNED_BEACON_BLOCK[0],
127 COMPRESSED_SIGNED_BEACON_BLOCK[1],
128 entry.entry_type[0],
129 entry.entry_type[1]
130 )));
131 }
132
133 Ok(Self { data: entry.data.clone() })
134 }
135}
136
137#[derive(Debug, Clone)]
141pub struct CompressedBeaconState {
142 pub data: Vec<u8>,
144}
145
146impl CompressedBeaconState {
147 pub const fn new(data: Vec<u8>) -> Self {
149 Self { data }
150 }
151
152 pub fn from_ssz(ssz_data: &[u8]) -> Result<Self, E2sError> {
154 let mut compressed = Vec::new();
155 {
156 let mut encoder = FrameEncoder::new(&mut compressed);
157
158 Write::write_all(&mut encoder, ssz_data).map_err(|e| {
159 E2sError::SnappyCompression(format!("Failed to compress beacon state: {e}"))
160 })?;
161
162 encoder.flush().map_err(|e| {
163 E2sError::SnappyCompression(format!("Failed to flush encoder: {e}"))
164 })?;
165 }
166 Ok(Self { data: compressed })
167 }
168
169 pub fn decompress(&self) -> Result<Vec<u8>, E2sError> {
171 let mut decoder = FrameDecoder::new(self.data.as_slice());
172 let mut decompressed = Vec::new();
173 Read::read_to_end(&mut decoder, &mut decompressed).map_err(|e| {
174 E2sError::SnappyDecompression(format!("Failed to decompress beacon state: {e}"))
175 })?;
176
177 Ok(decompressed)
178 }
179
180 pub fn to_entry(&self) -> Entry {
182 Entry::new(COMPRESSED_BEACON_STATE, self.data.clone())
183 }
184
185 pub fn from_entry(entry: &Entry) -> Result<Self, E2sError> {
187 if entry.entry_type != COMPRESSED_BEACON_STATE {
188 return Err(E2sError::Ssz(format!(
189 "Invalid entry type for CompressedBeaconState: expected {:02x}{:02x}, got {:02x}{:02x}",
190 COMPRESSED_BEACON_STATE[0],
191 COMPRESSED_BEACON_STATE[1],
192 entry.entry_type[0],
193 entry.entry_type[1]
194 )));
195 }
196
197 Ok(Self { data: entry.data.clone() })
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_signed_beacon_block_compression_roundtrip() {
207 let ssz_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
208
209 let compressed_block = CompressedSignedBeaconBlock::from_ssz(&ssz_data).unwrap();
210 let decompressed = compressed_block.decompress().unwrap();
211
212 assert_eq!(decompressed, ssz_data);
213 }
214
215 #[test]
216 fn test_beacon_state_compression_roundtrip() {
217 let ssz_data = vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
218
219 let compressed_state = CompressedBeaconState::from_ssz(&ssz_data).unwrap();
220 let decompressed = compressed_state.decompress().unwrap();
221
222 assert_eq!(decompressed, ssz_data);
223 }
224
225 #[test]
226 fn test_entry_conversion_signed_beacon_block() {
227 let ssz_data = vec![1, 2, 3, 4, 5];
228 let compressed_block = CompressedSignedBeaconBlock::from_ssz(&ssz_data).unwrap();
229
230 let entry = compressed_block.to_entry();
231 assert_eq!(entry.entry_type, COMPRESSED_SIGNED_BEACON_BLOCK);
232
233 let recovered = CompressedSignedBeaconBlock::from_entry(&entry).unwrap();
234 let recovered_ssz = recovered.decompress().unwrap();
235
236 assert_eq!(recovered_ssz, ssz_data);
237 }
238
239 #[test]
240 fn test_entry_conversion_beacon_state() {
241 let ssz_data = vec![5, 4, 3, 2, 1];
242 let compressed_state = CompressedBeaconState::from_ssz(&ssz_data).unwrap();
243
244 let entry = compressed_state.to_entry();
245 assert_eq!(entry.entry_type, COMPRESSED_BEACON_STATE);
246
247 let recovered = CompressedBeaconState::from_entry(&entry).unwrap();
248 let recovered_ssz = recovered.decompress().unwrap();
249
250 assert_eq!(recovered_ssz, ssz_data);
251 }
252
253 #[test]
254 fn test_invalid_entry_type() {
255 let invalid_entry = Entry::new([0xFF, 0xFF], vec![1, 2, 3]);
256
257 let result = CompressedSignedBeaconBlock::from_entry(&invalid_entry);
258 assert!(result.is_err());
259
260 let result = CompressedBeaconState::from_entry(&invalid_entry);
261 assert!(result.is_err());
262 }
263}