foundry_primitives/network/
header.rs1use alloy_consensus::{BlockHeader, Header};
2use alloy_primitives::{Address, B64, B256, Bloom, Bytes, Sealable, U256};
3use alloy_rlp::{BufMut, Decodable, Encodable, Result};
4use std::ops::Deref;
5use tempo_primitives::TempoHeader;
6
7#[derive(Clone, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
13#[serde(untagged)]
14#[allow(clippy::large_enum_variant)]
15pub enum FoundryHeader {
16 Tempo(TempoHeader),
18 Ethereum(Header),
20}
21
22impl Default for FoundryHeader {
23 fn default() -> Self {
24 Self::Ethereum(Header::default())
25 }
26}
27
28impl FoundryHeader {
29 pub const fn tempo(inner: Header) -> Self {
31 Self::Tempo(TempoHeader {
32 general_gas_limit: inner.gas_limit,
33 shared_gas_limit: 0,
34 timestamp_millis_part: 0,
35 inner,
36 consensus_context: None,
37 })
38 }
39
40 pub const fn as_tempo(&self) -> Option<&TempoHeader> {
42 match self {
43 Self::Tempo(header) => Some(header),
44 Self::Ethereum(_) => None,
45 }
46 }
47
48 pub const fn inner(&self) -> &Header {
50 match self {
51 Self::Tempo(header) => &header.inner,
52 Self::Ethereum(header) => header,
53 }
54 }
55
56 const fn inner_mut(&mut self) -> &mut Header {
57 match self {
58 Self::Tempo(header) => &mut header.inner,
59 Self::Ethereum(header) => header,
60 }
61 }
62
63 pub const fn set_transactions_root(&mut self, transactions_root: B256) {
65 self.inner_mut().transactions_root = transactions_root;
66 }
67
68 pub const fn set_ommers_hash(&mut self, ommers_hash: B256) {
70 self.inner_mut().ommers_hash = ommers_hash;
71 }
72
73 pub fn into_inner(self) -> Header {
75 match self {
76 Self::Tempo(header) => header.inner,
77 Self::Ethereum(header) => header,
78 }
79 }
80
81 pub fn hash_slow(&self) -> B256 {
83 match self {
84 Self::Tempo(header) => header.hash_slow(),
85 Self::Ethereum(header) => header.hash_slow(),
86 }
87 }
88}
89
90impl From<Header> for FoundryHeader {
91 fn from(value: Header) -> Self {
92 Self::Ethereum(value)
93 }
94}
95
96impl From<TempoHeader> for FoundryHeader {
97 fn from(value: TempoHeader) -> Self {
98 Self::Tempo(value)
99 }
100}
101
102impl AsRef<Self> for FoundryHeader {
103 fn as_ref(&self) -> &Self {
104 self
105 }
106}
107
108impl Deref for FoundryHeader {
109 type Target = Header;
110
111 fn deref(&self) -> &Self::Target {
112 self.inner()
113 }
114}
115
116impl Encodable for FoundryHeader {
117 fn encode(&self, out: &mut dyn BufMut) {
118 match self {
119 Self::Tempo(header) => header.encode(out),
120 Self::Ethereum(header) => header.encode(out),
121 }
122 }
123
124 fn length(&self) -> usize {
125 match self {
126 Self::Tempo(header) => header.length(),
127 Self::Ethereum(header) => header.length(),
128 }
129 }
130}
131
132impl Decodable for FoundryHeader {
133 fn decode(buf: &mut &[u8]) -> Result<Self> {
134 let mut tempo_buf = *buf;
137 if let Ok(header) = TempoHeader::decode(&mut tempo_buf) {
138 *buf = tempo_buf;
139 return Ok(Self::Tempo(header));
140 }
141
142 Header::decode(buf).map(Self::Ethereum)
143 }
144}
145
146impl Sealable for FoundryHeader {
147 fn hash_slow(&self) -> B256 {
148 Self::hash_slow(self)
149 }
150}
151
152macro_rules! delegate_header_methods {
153 ($($method:ident -> $return_type:ty),+ $(,)?) => {
154 $(
155 fn $method(&self) -> $return_type {
156 self.inner().$method()
157 }
158 )+
159 };
160}
161
162impl BlockHeader for FoundryHeader {
163 delegate_header_methods! {
164 parent_hash -> B256,
165 ommers_hash -> B256,
166 beneficiary -> Address,
167 state_root -> B256,
168 transactions_root -> B256,
169 receipts_root -> B256,
170 withdrawals_root -> Option<B256>,
171 logs_bloom -> Bloom,
172 difficulty -> U256,
173 number -> u64,
174 gas_limit -> u64,
175 gas_used -> u64,
176 timestamp -> u64,
177 mix_hash -> Option<B256>,
178 nonce -> Option<B64>,
179 base_fee_per_gas -> Option<u64>,
180 blob_gas_used -> Option<u64>,
181 excess_blob_gas -> Option<u64>,
182 parent_beacon_block_root -> Option<B256>,
183 requests_hash -> Option<B256>,
184 block_access_list_hash -> Option<B256>,
185 slot_number -> Option<u64>,
186 }
187
188 fn extra_data(&self) -> &Bytes {
189 self.inner().extra_data()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn rlp_roundtrip_preserves_network_header() {
199 for header in [
200 Header { number: 1, ..Default::default() }.into(),
201 FoundryHeader::tempo(Header { number: 2, gas_limit: 30_000_000, ..Default::default() }),
202 ] {
203 let encoded = alloy_rlp::encode(&header);
204 let decoded = FoundryHeader::decode(&mut encoded.as_ref()).unwrap();
205
206 assert_eq!(decoded, header);
207 assert_eq!(decoded.hash_slow(), header.hash_slow());
208 if let Some(tempo) = header.as_tempo() {
209 assert_eq!(header.hash_slow(), tempo.hash_slow());
210 }
211 }
212 }
213
214 #[test]
215 fn serde_roundtrip_preserves_tempo_fields() {
216 let header =
217 FoundryHeader::tempo(Header { number: 1, gas_limit: 30_000_000, ..Default::default() });
218 let value = serde_json::to_value(&header).unwrap();
219
220 assert_eq!(value["mainBlockGeneralGasLimit"], "0x1c9c380");
221 assert_eq!(serde_json::from_value::<FoundryHeader>(value).unwrap(), header);
222 }
223}