1use alloy_rlp::{Decodable, Encodable};
4use bytes::BufMut;
5use reth_codecs_derive::add_arbitrary_tests;
6
7#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
20#[add_arbitrary_tests(rlp)]
21pub enum HeadersDirection {
22 Falling,
24 #[default]
26 Rising,
27}
28
29impl HeadersDirection {
30 pub const fn is_rising(&self) -> bool {
32 matches!(self, Self::Rising)
33 }
34
35 pub const fn is_falling(&self) -> bool {
37 matches!(self, Self::Falling)
38 }
39
40 pub const fn new(reverse: bool) -> Self {
47 if reverse {
48 Self::Falling
49 } else {
50 Self::Rising
51 }
52 }
53}
54
55impl Encodable for HeadersDirection {
56 fn encode(&self, out: &mut dyn BufMut) {
57 bool::from(*self).encode(out)
58 }
59
60 fn length(&self) -> usize {
61 bool::from(*self).length()
62 }
63}
64
65impl Decodable for HeadersDirection {
66 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
67 let value: bool = Decodable::decode(buf)?;
68 Ok(value.into())
69 }
70}
71
72impl From<bool> for HeadersDirection {
73 fn from(reverse: bool) -> Self {
74 Self::new(reverse)
75 }
76}
77
78impl From<HeadersDirection> for bool {
79 fn from(value: HeadersDirection) -> Self {
80 match value {
81 HeadersDirection::Rising => false,
82 HeadersDirection::Falling => true,
83 }
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use alloy_consensus::{Header, EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH};
91 use alloy_primitives::{address, b256, bloom, bytes, hex, Bytes, B256, U256};
92 use alloy_rlp::{Decodable, Encodable};
93 use std::str::FromStr;
94
95 #[test]
97 fn test_encode_block_header() {
98 let expected = hex!(
99 "f901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"
100 );
101 let header = Header {
102 difficulty: U256::from(0x8ae_u64),
103 number: 0xd05_u64,
104 gas_limit: 0x115c,
105 gas_used: 0x15b3,
106 timestamp: 0x1a0a_u64,
107 extra_data: Bytes::from_str("7788").unwrap(),
108 ommers_hash: B256::ZERO,
109 state_root: B256::ZERO,
110 transactions_root: B256::ZERO,
111 receipts_root: B256::ZERO,
112 ..Default::default()
113 };
114 let mut data = vec![];
115 header.encode(&mut data);
116 assert_eq!(hex::encode(&data), hex::encode(expected));
117 assert_eq!(header.length(), data.len());
118 }
119
120 #[test]
122 fn test_eip1559_block_header_hash() {
123 let expected_hash =
124 b256!("0x6a251c7c3c5dca7b42407a3752ff48f3bbca1fab7f9868371d9918daf1988d1f");
125 let header = Header {
126 parent_hash: b256!("0xe0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2a"),
127 ommers_hash: EMPTY_OMMER_ROOT_HASH,
128 beneficiary: address!("0xba5e000000000000000000000000000000000000"),
129 state_root: b256!(
130 "0xec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7"
131 ),
132 transactions_root: b256!(
133 "0x50f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accf"
134 ),
135 receipts_root: b256!(
136 "0x29b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9"
137 ),
138 logs_bloom: bloom!(
139 "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
140 ),
141 difficulty: U256::from(0x020000),
142 number: 0x01_u64,
143 gas_limit: 0x016345785d8a0000,
144 gas_used: 0x015534,
145 timestamp: 0x079e,
146 extra_data: bytes!("42"),
147 mix_hash: b256!("0x0000000000000000000000000000000000000000000000000000000000000000"),
148 nonce: 0u64.into() ,
149 base_fee_per_gas: Some(0x036b),
150 withdrawals_root: None,
151 blob_gas_used: None,
152 excess_blob_gas: None,
153 parent_beacon_block_root: None,
154 requests_hash: None,
155 block_access_list_hash: None,
156 slot_number: None,
157 };
158 assert_eq!(header.hash_slow(), expected_hash);
159 }
160
161 #[test]
163 fn test_decode_block_header() {
164 let data = hex!(
165 "f901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"
166 );
167 let expected = Header {
168 difficulty: U256::from(0x8aeu64),
169 number: 0xd05u64,
170 gas_limit: 0x115c,
171 gas_used: 0x15b3,
172 timestamp: 0x1a0au64,
173 extra_data: Bytes::from_str("7788").unwrap(),
174 ommers_hash: B256::ZERO,
175 state_root: B256::ZERO,
176 transactions_root: B256::ZERO,
177 receipts_root: B256::ZERO,
178 ..Default::default()
179 };
180 let header = <Header as Decodable>::decode(&mut data.as_slice()).unwrap();
181 assert_eq!(header, expected);
182
183 let expected_hash =
185 b256!("0x8c2f2af15b7b563b6ab1e09bed0e9caade7ed730aec98b70a993597a797579a9");
186 assert_eq!(header.hash_slow(), expected_hash);
187 }
188
189 #[test]
191 fn test_decode_block_header_with_withdrawals() {
192 let data = hex!(
193 "f9021ca018db39e19931515b30b16b3a92c292398039e31d6c267111529c3f2ba0a26c17a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa095efce3d6972874ca8b531b233b7a1d1ff0a56f08b20c8f1b89bef1b001194a5a071e515dd89e8a7973402c2e11646081b4e2209b2d3a1550df5095289dabcb3fba0ed9c51ea52c968e552e370a77a41dac98606e98b915092fb5f949d6452fce1c4b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff830125b882079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a027f166f1d7c789251299535cb176ba34116e44894476a7886fe5d73d9be5c973"
194 );
195 let expected = Header {
196 parent_hash: B256::from_str(
197 "18db39e19931515b30b16b3a92c292398039e31d6c267111529c3f2ba0a26c17",
198 )
199 .unwrap(),
200 beneficiary: address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
201 state_root: B256::from_str(
202 "95efce3d6972874ca8b531b233b7a1d1ff0a56f08b20c8f1b89bef1b001194a5",
203 )
204 .unwrap(),
205 transactions_root: B256::from_str(
206 "71e515dd89e8a7973402c2e11646081b4e2209b2d3a1550df5095289dabcb3fb",
207 )
208 .unwrap(),
209 receipts_root: B256::from_str(
210 "ed9c51ea52c968e552e370a77a41dac98606e98b915092fb5f949d6452fce1c4",
211 )
212 .unwrap(),
213 number: 0x01,
214 gas_limit: 0x7fffffffffffffff,
215 gas_used: 0x0125b8,
216 timestamp: 0x079e,
217 extra_data: Bytes::from_str("42").unwrap(),
218 mix_hash: EMPTY_ROOT_HASH,
219 base_fee_per_gas: Some(0x09),
220 withdrawals_root: Some(b256!(
221 "0x27f166f1d7c789251299535cb176ba34116e44894476a7886fe5d73d9be5c973"
222 )),
223 ..Default::default()
224 };
225 let header = <Header as Decodable>::decode(&mut data.as_slice()).unwrap();
226 assert_eq!(header, expected);
227
228 let expected_hash =
229 b256!("0x85fdec94c534fa0a1534720f167b899d1fc268925c71c0cbf5aaa213483f5a69");
230 assert_eq!(header.hash_slow(), expected_hash);
231 }
232
233 #[test]
235 fn test_decode_block_header_with_blob_fields_ef_tests() {
236 let data = hex!(
237 "f90221a03a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa03c837fc158e3e93eafcaf2e658a02f5d8f99abc9f1c4c66cdea96c0ca26406aea04409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9cea046cab26abf1047b5b119ecc2dda1296b071766c8b1307e1381fcecc90d513d86b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff8302a86582079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218302000080"
238 );
239 let expected = Header {
240 parent_hash: B256::from_str(
241 "3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6",
242 )
243 .unwrap(),
244 ommers_hash: EMPTY_OMMER_ROOT_HASH,
245 beneficiary: address!("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
246 state_root: B256::from_str(
247 "3c837fc158e3e93eafcaf2e658a02f5d8f99abc9f1c4c66cdea96c0ca26406ae",
248 )
249 .unwrap(),
250 transactions_root: B256::from_str(
251 "4409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9ce",
252 )
253 .unwrap(),
254 receipts_root: B256::from_str(
255 "46cab26abf1047b5b119ecc2dda1296b071766c8b1307e1381fcecc90d513d86",
256 )
257 .unwrap(),
258 logs_bloom: Default::default(),
259 difficulty: U256::from(0),
260 number: 0x1,
261 gas_limit: 0x7fffffffffffffff,
262 gas_used: 0x02a865,
263 timestamp: 0x079e,
264 extra_data: Bytes::from(vec![0x42]),
265 mix_hash: EMPTY_ROOT_HASH,
266 nonce: 0u64.into(),
267 base_fee_per_gas: Some(9),
268 withdrawals_root: Some(EMPTY_ROOT_HASH),
269 blob_gas_used: Some(0x020000),
270 excess_blob_gas: Some(0),
271 parent_beacon_block_root: None,
272 requests_hash: None,
273 block_access_list_hash: None,
274 slot_number: None,
275 };
276
277 let header = Header::decode(&mut data.as_slice()).unwrap();
278 assert_eq!(header, expected);
279
280 let expected_hash =
281 B256::from_str("0x10aca3ebb4cf6ddd9e945a5db19385f9c105ede7374380c50d56384c3d233785")
282 .unwrap();
283 assert_eq!(header.hash_slow(), expected_hash);
284 }
285
286 #[test]
287 fn test_decode_block_header_with_blob_fields() {
288 let data = hex!(
290 "f90239a013a7ec98912f917b3e804654e37c9866092043c13eb8eab94eb64818e886cff5a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794f97e180c050e5ab072211ad2c213eb5aee4df134a0ec229dbe85b0d3643ad0f471e6ec1a36bbc87deffbbd970762d22a53b35d068aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080830305988401c9c380808464c40d5499d883010c01846765746888676f312e32302e35856c696e7578a070ccadc40b16e2094954b1064749cc6fbac783c1712f1b271a8aac3eda2f232588000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421808401600000"
291 );
292 let expected = Header {
293 parent_hash: B256::from_str(
294 "13a7ec98912f917b3e804654e37c9866092043c13eb8eab94eb64818e886cff5",
295 )
296 .unwrap(),
297 ommers_hash: EMPTY_OMMER_ROOT_HASH,
298 beneficiary: address!("0xf97e180c050e5ab072211ad2c213eb5aee4df134"),
299 state_root: b256!("0xec229dbe85b0d3643ad0f471e6ec1a36bbc87deffbbd970762d22a53b35d068a"),
300 transactions_root: EMPTY_ROOT_HASH,
301 receipts_root: EMPTY_ROOT_HASH,
302 logs_bloom: Default::default(),
303 difficulty: U256::from(0),
304 number: 0x30598,
305 gas_limit: 0x1c9c380,
306 gas_used: 0,
307 timestamp: 0x64c40d54,
308 extra_data: bytes!("d883010c01846765746888676f312e32302e35856c696e7578"),
309 mix_hash: b256!("0x70ccadc40b16e2094954b1064749cc6fbac783c1712f1b271a8aac3eda2f2325"),
310 nonce: 0u64.into(),
311 base_fee_per_gas: Some(7),
312 withdrawals_root: Some(EMPTY_ROOT_HASH),
313 parent_beacon_block_root: None,
314 blob_gas_used: Some(0),
315 excess_blob_gas: Some(0x1600000),
316 requests_hash: None,
317 block_access_list_hash: None,
318 slot_number: None,
319 };
320
321 let header = Header::decode(&mut data.as_slice()).unwrap();
322 assert_eq!(header, expected);
323
324 let expected_hash =
325 b256!("0x539c9ea0a3ca49808799d3964b8b6607037227de26bc51073c6926963127087b");
326 assert_eq!(header.hash_slow(), expected_hash);
327 }
328
329 #[test]
330 fn sanity_direction() {
331 let reverse = true;
332 assert_eq!(HeadersDirection::Falling, reverse.into());
333 assert_eq!(reverse, bool::from(HeadersDirection::Falling));
334
335 let reverse = false;
336 assert_eq!(HeadersDirection::Rising, reverse.into());
337 assert_eq!(reverse, bool::from(HeadersDirection::Rising));
338
339 let mut buf = Vec::new();
340 let direction = HeadersDirection::Falling;
341 direction.encode(&mut buf);
342 assert_eq!(direction, HeadersDirection::decode(&mut buf.as_slice()).unwrap());
343
344 let mut buf = Vec::new();
345 let direction = HeadersDirection::Rising;
346 direction.encode(&mut buf);
347 assert_eq!(direction, HeadersDirection::decode(&mut buf.as_slice()).unwrap());
348 }
349}