1use eth_valkyoth_codec::{
2 DecodeError, DecodeLimits, RlpInteger, RlpItem, RlpScalar, decode_rlp_list,
3};
4use eth_valkyoth_hash::{Keccak256, hash_one};
5use eth_valkyoth_primitives::{Address, B256, BlockNumber, Gas, UnixTimestamp, Wei};
6
7mod error;
8pub use error::{BlockHeaderDecodeError, BlockHeaderDecodeErrorCategory, BlockHeaderField};
9
10const ADDRESS_BYTES: usize = 20;
11const B256_BYTES: usize = 32;
12const BLOOM_BYTES: usize = 256;
13const NONCE_BYTES: usize = 8;
14
15pub const LEGACY_HEADER_FIELD_COUNT: usize = 15;
17pub const LONDON_HEADER_FIELD_COUNT: usize = 16;
19pub const SHANGHAI_HEADER_FIELD_COUNT: usize = 17;
21pub const CANCUN_HEADER_FIELD_COUNT: usize = 20;
24pub const PRAGUE_HEADER_FIELD_COUNT: usize = 21;
26
27#[non_exhaustive]
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub enum HeaderFieldSet {
31 Legacy,
33 London,
35 Shanghai,
37 Cancun,
39 Prague,
41}
42
43impl HeaderFieldSet {
44 #[must_use]
46 pub const fn field_count(self) -> usize {
47 match self {
48 Self::Legacy => LEGACY_HEADER_FIELD_COUNT,
49 Self::London => LONDON_HEADER_FIELD_COUNT,
50 Self::Shanghai => SHANGHAI_HEADER_FIELD_COUNT,
51 Self::Cancun => CANCUN_HEADER_FIELD_COUNT,
52 Self::Prague => PRAGUE_HEADER_FIELD_COUNT,
53 }
54 }
55
56 const fn has_base_fee(self) -> bool {
57 !matches!(self, Self::Legacy)
58 }
59
60 const fn has_withdrawals_root(self) -> bool {
61 matches!(self, Self::Shanghai | Self::Cancun | Self::Prague)
62 }
63
64 const fn has_cancun_fields(self) -> bool {
65 matches!(self, Self::Cancun | Self::Prague)
66 }
67
68 const fn has_requests_hash(self) -> bool {
69 matches!(self, Self::Prague)
70 }
71}
72
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
75pub struct BlockHash(B256);
76
77impl BlockHash {
78 #[must_use]
80 pub const fn from_b256(value: B256) -> Self {
81 Self(value)
82 }
83
84 #[must_use]
86 pub const fn to_b256(self) -> B256 {
87 self.0
88 }
89}
90
91impl From<BlockHash> for B256 {
92 fn from(value: BlockHash) -> Self {
93 value.to_b256()
94 }
95}
96
97#[derive(Clone, Copy, Debug, Eq, PartialEq)]
106pub struct UnvalidatedBlockHeader<'a> {
107 encoded_rlp: &'a [u8],
108 pub field_set: HeaderFieldSet,
110 pub parent_hash: B256,
112 pub ommers_hash: B256,
114 pub beneficiary: Address,
116 pub state_root: B256,
118 pub transactions_root: B256,
120 pub receipts_root: B256,
122 pub logs_bloom: LogsBloom,
124 pub difficulty: [u8; 32],
126 pub number: BlockNumber,
128 pub gas_limit: Gas,
130 pub gas_used: Gas,
132 pub timestamp: UnixTimestamp,
134 pub extra_data: &'a [u8],
139 pub mix_hash: B256,
141 pub nonce: [u8; 8],
143 pub base_fee_per_gas: Option<Wei>,
145 pub withdrawals_root: Option<B256>,
147 pub blob_gas_used: Option<Gas>,
149 pub excess_blob_gas: Option<Gas>,
151 pub parent_beacon_block_root: Option<B256>,
153 pub requests_hash: Option<B256>,
155}
156
157impl UnvalidatedBlockHeader<'_> {
158 #[must_use]
160 pub const fn encoded_rlp(&self) -> &[u8] {
161 self.encoded_rlp
162 }
163
164 #[must_use]
167 pub fn hash_with<H>(&self, hasher: H) -> BlockHash
168 where
169 H: Keccak256,
170 {
171 BlockHash::from_b256(hash_one(hasher, self.encoded_rlp))
172 }
173}
174
175#[derive(Clone, Copy, Debug, Eq, PartialEq)]
177pub struct LogsBloom([u8; BLOOM_BYTES]);
178
179impl LogsBloom {
180 #[must_use]
182 pub const fn from_bytes(bytes: [u8; BLOOM_BYTES]) -> Self {
183 Self(bytes)
184 }
185
186 #[must_use]
188 pub const fn to_bytes(self) -> [u8; BLOOM_BYTES] {
189 self.0
190 }
191}
192
193pub fn decode_block_header<'a>(
200 input: &'a [u8],
201 field_set: HeaderFieldSet,
202 limits: DecodeLimits,
203) -> Result<UnvalidatedBlockHeader<'a>, BlockHeaderDecodeError> {
204 let list = decode_rlp_list(input, limits).map_err(BlockHeaderDecodeError::Decode)?;
205 let expected = field_set.field_count();
206 let found = list.item_count();
207 if found != expected {
208 return Err(BlockHeaderDecodeError::WrongFieldCount { expected, found });
209 }
210
211 let mut fields = list.items();
212 let header = UnvalidatedBlockHeader {
213 encoded_rlp: input,
214 field_set,
215 parent_hash: decode_b256(&mut fields, BlockHeaderField::ParentHash)?,
216 ommers_hash: decode_b256(&mut fields, BlockHeaderField::OmmersHash)?,
217 beneficiary: decode_address(&mut fields, BlockHeaderField::Beneficiary)?,
218 state_root: decode_b256(&mut fields, BlockHeaderField::StateRoot)?,
219 transactions_root: decode_b256(&mut fields, BlockHeaderField::TransactionsRoot)?,
220 receipts_root: decode_b256(&mut fields, BlockHeaderField::ReceiptsRoot)?,
221 logs_bloom: decode_logs_bloom(&mut fields)?,
222 difficulty: decode_u256(&mut fields, BlockHeaderField::Difficulty)?,
223 number: BlockNumber::new(decode_u64(&mut fields, BlockHeaderField::Number)?),
224 gas_limit: Gas::new(decode_u64(&mut fields, BlockHeaderField::GasLimit)?),
225 gas_used: Gas::new(decode_u64(&mut fields, BlockHeaderField::GasUsed)?),
226 timestamp: UnixTimestamp::new(decode_u64(&mut fields, BlockHeaderField::Timestamp)?),
227 extra_data: next_scalar(&mut fields, BlockHeaderField::ExtraData)?.payload(),
228 mix_hash: decode_b256(&mut fields, BlockHeaderField::MixHash)?,
229 nonce: decode_fixed::<NONCE_BYTES>(&mut fields, BlockHeaderField::Nonce)?,
230 base_fee_per_gas: decode_optional_wei(
231 &mut fields,
232 field_set.has_base_fee(),
233 BlockHeaderField::BaseFeePerGas,
234 )?,
235 withdrawals_root: decode_optional_b256(
236 &mut fields,
237 field_set.has_withdrawals_root(),
238 BlockHeaderField::WithdrawalsRoot,
239 )?,
240 blob_gas_used: decode_optional_gas(
241 &mut fields,
242 field_set.has_cancun_fields(),
243 BlockHeaderField::BlobGasUsed,
244 )?,
245 excess_blob_gas: decode_optional_gas(
246 &mut fields,
247 field_set.has_cancun_fields(),
248 BlockHeaderField::ExcessBlobGas,
249 )?,
250 parent_beacon_block_root: decode_optional_b256(
251 &mut fields,
252 field_set.has_cancun_fields(),
253 BlockHeaderField::ParentBeaconBlockRoot,
254 )?,
255 requests_hash: decode_optional_b256(
256 &mut fields,
257 field_set.has_requests_hash(),
258 BlockHeaderField::RequestsHash,
259 )?,
260 };
261 Ok(header)
262}
263
264fn next_scalar<'a>(
265 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
266 field: BlockHeaderField,
267) -> Result<RlpScalar<'a>, BlockHeaderDecodeError> {
268 let item = fields
269 .next()
270 .ok_or(BlockHeaderDecodeError::FieldDecode {
271 field,
272 source: DecodeError::Malformed,
273 })?
274 .map_err(|source| BlockHeaderDecodeError::FieldDecode { field, source })?;
275 match item {
276 RlpItem::Scalar(scalar) => Ok(scalar),
277 RlpItem::List(_) => Err(BlockHeaderDecodeError::FieldDecode {
278 field,
279 source: DecodeError::UnexpectedList,
280 }),
281 }
282}
283
284fn decode_u64<'a>(
285 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
286 field: BlockHeaderField,
287) -> Result<u64, BlockHeaderDecodeError> {
288 RlpInteger::try_from_scalar(next_scalar(fields, field)?)
289 .and_then(RlpInteger::to_u64)
290 .map_err(|source| BlockHeaderDecodeError::FieldDecode { field, source })
291}
292
293fn decode_u256<'a>(
294 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
295 field: BlockHeaderField,
296) -> Result<[u8; 32], BlockHeaderDecodeError> {
297 RlpInteger::try_from_scalar(next_scalar(fields, field)?)
298 .and_then(RlpInteger::to_be_bytes32)
299 .map_err(|source| BlockHeaderDecodeError::FieldDecode { field, source })
300}
301
302fn decode_wei<'a>(
303 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
304 field: BlockHeaderField,
305) -> Result<Wei, BlockHeaderDecodeError> {
306 decode_u256(fields, field).map(Wei::from_be_bytes)
307}
308
309fn decode_fixed<'a, const N: usize>(
310 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
311 field: BlockHeaderField,
312) -> Result<[u8; N], BlockHeaderDecodeError> {
313 let scalar = next_scalar(fields, field)?;
314 scalar
315 .payload()
316 .try_into()
317 .map_err(|_| BlockHeaderDecodeError::InvalidFieldLength {
318 field,
319 expected: N,
320 found: scalar.payload().len(),
321 })
322}
323
324fn decode_b256<'a>(
325 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
326 field: BlockHeaderField,
327) -> Result<B256, BlockHeaderDecodeError> {
328 decode_fixed::<B256_BYTES>(fields, field).map(B256::from_bytes)
329}
330
331fn decode_address<'a>(
332 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
333 field: BlockHeaderField,
334) -> Result<Address, BlockHeaderDecodeError> {
335 decode_fixed::<ADDRESS_BYTES>(fields, field).map(Address::from_bytes)
336}
337
338fn decode_logs_bloom<'a>(
339 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
340) -> Result<LogsBloom, BlockHeaderDecodeError> {
341 decode_fixed::<BLOOM_BYTES>(fields, BlockHeaderField::LogsBloom).map(LogsBloom::from_bytes)
342}
343
344fn decode_optional_b256<'a>(
345 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
346 present: bool,
347 field: BlockHeaderField,
348) -> Result<Option<B256>, BlockHeaderDecodeError> {
349 if present {
350 decode_b256(fields, field).map(Some)
351 } else {
352 Ok(None)
353 }
354}
355
356fn decode_optional_gas<'a>(
357 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
358 present: bool,
359 field: BlockHeaderField,
360) -> Result<Option<Gas>, BlockHeaderDecodeError> {
361 if present {
362 decode_u64(fields, field).map(Gas::new).map(Some)
363 } else {
364 Ok(None)
365 }
366}
367
368fn decode_optional_wei<'a>(
369 fields: &mut impl Iterator<Item = Result<RlpItem<'a>, DecodeError>>,
370 present: bool,
371 field: BlockHeaderField,
372) -> Result<Option<Wei>, BlockHeaderDecodeError> {
373 if present {
374 decode_wei(fields, field).map(Some)
375 } else {
376 Ok(None)
377 }
378}
379
380#[cfg(test)]
381#[path = "header_tests.rs"]
382mod tests;