1use alloy_consensus::constants::EMPTY_OMMER_ROOT_HASH;
2use alloy_consensus::constants::EMPTY_ROOT_HASH;
3use alloy_primitives::B64;
4use alloy_primitives::B256;
5use alloy_primitives::FixedBytes;
6use alloy_primitives::U256;
7use alloy_rpc_types_eth::Block as AlloyBlock;
8use alloy_rpc_types_eth::BlockTransactions;
9use display_json::DebugAsJson;
10use fake::Dummy;
11use fake::Fake;
12use fake::Faker;
13use hex_literal::hex;
14use jsonrpsee::SubscriptionMessage;
15
16use crate::alias::AlloyAddress;
17use crate::alias::AlloyBlockVoid;
18use crate::alias::AlloyBloom;
19use crate::alias::AlloyBytes;
20use crate::alias::AlloyConsensusHeader;
21use crate::alias::AlloyHeader;
22use crate::eth::primitives::Address;
23use crate::eth::primitives::BlockNumber;
24use crate::eth::primitives::Bytes;
25use crate::eth::primitives::Difficulty;
26use crate::eth::primitives::ExternalBlock;
27use crate::eth::primitives::Gas;
28use crate::eth::primitives::Hash;
29use crate::eth::primitives::MinerNonce;
30use crate::eth::primitives::Size;
31use crate::eth::primitives::UnixTime;
32use crate::eth::primitives::logs_bloom::LogsBloom;
33use crate::ext::InfallibleExt;
34
35const HASH_EMPTY_UNCLES: Hash = Hash::new(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"));
37
38const HASH_EMPTY_TRIE: Hash = Hash::new(hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"));
40
41#[derive(DebugAsJson, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
42pub struct BlockHeader {
43 pub number: BlockNumber,
44 pub hash: Hash,
45 pub transactions_root: Hash,
46 pub gas_used: Gas,
47 pub gas_limit: Gas,
48 pub bloom: LogsBloom,
49 pub timestamp: UnixTime,
50 pub parent_hash: Hash,
51 pub author: Address,
52 pub extra_data: Bytes,
53 pub miner: Address,
54 pub difficulty: Difficulty, pub receipts_root: Hash,
56 pub uncle_hash: Hash, pub size: Size,
58 pub state_root: Hash,
59 pub total_difficulty: Difficulty, pub nonce: MinerNonce, }
62
63impl BlockHeader {
64 pub fn new(number: BlockNumber, timestamp: UnixTime) -> Self {
66 Self {
67 number,
68 hash: number.hash(),
69 transactions_root: HASH_EMPTY_TRIE,
70 gas_used: Gas::ZERO,
71 gas_limit: Gas::ZERO,
72 bloom: LogsBloom::default(),
73 timestamp,
74 parent_hash: number.prev().map(|n| n.hash()).unwrap_or(Hash::ZERO),
75 author: Address::default(),
76 extra_data: Bytes::default(),
77 miner: Address::default(),
78 difficulty: Difficulty::default(),
79 receipts_root: HASH_EMPTY_TRIE,
80 uncle_hash: HASH_EMPTY_UNCLES,
81 size: Size::default(),
82 state_root: HASH_EMPTY_TRIE,
83 total_difficulty: Difficulty::default(),
84 nonce: MinerNonce::default(),
85 }
86 }
87}
88
89impl Dummy<Faker> for BlockHeader {
90 fn dummy_with_rng<R: rand::Rng + ?Sized>(faker: &Faker, rng: &mut R) -> Self {
91 Self {
92 number: faker.fake_with_rng(rng),
93 hash: faker.fake_with_rng(rng),
94 transactions_root: faker.fake_with_rng(rng),
95 gas_used: faker.fake_with_rng(rng),
96 bloom: LogsBloom::default(),
97 timestamp: faker.fake_with_rng(rng),
98 parent_hash: faker.fake_with_rng(rng),
99 gas_limit: faker.fake_with_rng(rng),
100 author: faker.fake_with_rng(rng),
101 extra_data: faker.fake_with_rng(rng),
102 miner: faker.fake_with_rng(rng),
103 difficulty: faker.fake_with_rng(rng),
104 receipts_root: faker.fake_with_rng(rng),
105 uncle_hash: faker.fake_with_rng(rng),
106 size: faker.fake_with_rng(rng),
107 state_root: faker.fake_with_rng(rng),
108 total_difficulty: faker.fake_with_rng(rng),
109 nonce: faker.fake_with_rng(rng),
110 }
111 }
112}
113
114impl<T> From<BlockHeader> for AlloyBlock<T> {
119 fn from(header: BlockHeader) -> Self {
120 let inner = AlloyConsensusHeader {
121 number: header.number.as_u64(),
123 mix_hash: FixedBytes::default(),
124
125 ommers_hash: EMPTY_OMMER_ROOT_HASH,
127 parent_hash: B256::from(header.parent_hash),
128 parent_beacon_block_root: None,
129
130 timestamp: *header.timestamp,
132 beneficiary: AlloyAddress::from(header.author),
133
134 difficulty: U256::ZERO,
136 nonce: B64::ZERO,
137
138 gas_limit: header.gas_limit.as_u64(),
140 gas_used: header.gas_used.as_u64(),
141 base_fee_per_gas: Some(0u64),
142 blob_gas_used: None,
143 excess_blob_gas: None,
144
145 transactions_root: B256::from(header.transactions_root),
147 receipts_root: EMPTY_ROOT_HASH,
148 withdrawals_root: None,
149
150 logs_bloom: AlloyBloom::from(header.bloom),
152 extra_data: AlloyBytes::default(),
153 state_root: B256::from(header.state_root),
154 requests_hash: None,
155 };
156
157 let rpc_header = AlloyHeader {
158 hash: header.hash.into(),
159 inner,
160 total_difficulty: Some(U256::ZERO),
161 size: Some(header.size.into()),
162 };
163
164 Self {
165 header: rpc_header,
166 uncles: Vec::new(),
167 transactions: BlockTransactions::default(),
168 withdrawals: None,
169 }
170 }
171}
172
173impl TryFrom<&ExternalBlock> for BlockHeader {
178 type Error = anyhow::Error;
179 fn try_from(value: &ExternalBlock) -> Result<Self, Self::Error> {
180 Ok(Self {
181 number: BlockNumber::from(value.0.header.inner.number),
182 hash: Hash::from(value.0.header.hash),
183 transactions_root: Hash::from(value.0.header.inner.transactions_root),
184 gas_used: Gas::from(value.0.header.inner.gas_used),
185 gas_limit: Gas::from(value.0.header.inner.gas_limit),
186 bloom: LogsBloom::from(value.0.header.inner.logs_bloom),
187 timestamp: UnixTime::from(value.0.header.inner.timestamp),
188 parent_hash: Hash::from(value.0.header.inner.parent_hash),
189 author: Address::from(value.0.header.inner.beneficiary),
190 extra_data: Bytes::from(value.0.header.inner.extra_data.clone()),
191 miner: Address::from(value.0.header.inner.beneficiary),
192 difficulty: Difficulty::from(value.0.header.inner.difficulty),
193 receipts_root: Hash::from(value.0.header.inner.receipts_root),
194 uncle_hash: Hash::from(value.0.header.inner.ommers_hash),
195 size: Size::try_from(value.0.header.size.unwrap_or_default())?,
196 state_root: Hash::from(value.0.header.inner.state_root),
197 total_difficulty: Difficulty::from(value.0.header.total_difficulty.unwrap_or_default()),
198 nonce: MinerNonce::from(value.0.header.inner.nonce.0),
199 })
200 }
201}
202
203impl From<BlockHeader> for SubscriptionMessage {
204 fn from(value: BlockHeader) -> Self {
205 serde_json::value::RawValue::from_string(serde_json::to_string(&AlloyBlockVoid::from(value)).expect_infallible())
206 .expect_infallible()
207 .into()
208 }
209}
210
211#[cfg(test)]
215mod tests {
216 use crate::eth::primitives::BlockHeader;
217 use crate::eth::primitives::BlockNumber;
218 use crate::eth::primitives::Hash;
219 use crate::eth::primitives::UnixTime;
220
221 #[test]
222 fn block_header_hash_calculation() {
223 let header = BlockHeader::new(BlockNumber::ZERO, UnixTime::from(1234567890));
224 assert_eq!(header.hash.to_string(), "0x011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce");
225 }
226
227 #[test]
228 fn block_header_parent_hash() {
229 let header = BlockHeader::new(BlockNumber::ONE, UnixTime::from(1234567891));
230 assert_eq!(
231 header.parent_hash.to_string(),
232 "0x011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce"
233 );
234 }
235
236 #[test]
237 fn block_header_genesis_parent_hash() {
238 let header = BlockHeader::new(BlockNumber::ZERO, UnixTime::from(1234567890));
239 assert_eq!(header.parent_hash, Hash::ZERO);
240 }
241}