hdp_primitives/task/datalake/block_sampled/
rlp_fields.rs

1//! This module defines the fields that can be queried from a block or account.
2//! The fields are defined as enums, and can be converted to and from their string representation.
3//! It is meant to be used in the `BlockSampled` struct, which is used to query fields from a block or account.
4
5use std::{fmt::Display, str::FromStr};
6
7use alloy::primitives::U256;
8use anyhow::{bail, Result};
9use serde::{Deserialize, Serialize};
10
11use crate::{
12    block::{account::Account, header::Header},
13    task::datalake::DatalakeField,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(try_from = "String")]
18pub enum HeaderField {
19    ParentHash,
20    OmmerHash,
21    Beneficiary,
22    StateRoot,
23    TransactionsRoot,
24    ReceiptsRoot,
25    LogsBloom,
26    Difficulty,
27    Number,
28    GasLimit,
29    GasUsed,
30    Timestamp,
31    ExtraData,
32    MixHash,
33    Nonce,
34    BaseFeePerGas,
35    WithdrawalsRoot,
36    BlobGasUsed,
37    ExcessBlobGas,
38    ParentBeaconBlockRoot,
39}
40
41impl HeaderField {
42    pub fn variants() -> Vec<String> {
43        vec![
44            "PARENT_HASH".to_string(),
45            "OMMERS_HASH".to_string(),
46            "BENEFICIARY".to_string(),
47            "STATE_ROOT".to_string(),
48            "TRANSACTIONS_ROOT".to_string(),
49            "RECEIPTS_ROOT".to_string(),
50            "LOGS_BLOOM".to_string(),
51            "DIFFICULTY".to_string(),
52            "NUMBER".to_string(),
53            "GAS_LIMIT".to_string(),
54            "GAS_USED".to_string(),
55            "TIMESTAMP".to_string(),
56            "EXTRA_DATA".to_string(),
57            "MIX_HASH".to_string(),
58            "NONCE".to_string(),
59            "BASE_FEE_PER_GAS".to_string(),
60            "WITHDRAWALS_ROOT".to_string(),
61            "BLOB_GAS_USED".to_string(),
62            "EXCESS_BLOB_GAS".to_string(),
63            "PARENT_BEACON_BLOCK_ROOT".to_string(),
64        ]
65    }
66
67    pub fn integer_variants_index(index: u8) -> Self {
68        match index {
69            0 => HeaderField::Difficulty,
70            1 => HeaderField::Number,
71            2 => HeaderField::GasLimit,
72            3 => HeaderField::GasUsed,
73            4 => HeaderField::Timestamp,
74            5 => HeaderField::Nonce,
75            6 => HeaderField::BaseFeePerGas,
76            7 => HeaderField::BlobGasUsed,
77            8 => HeaderField::ExcessBlobGas,
78            _ => unreachable!(),
79        }
80    }
81}
82
83impl DatalakeField for HeaderField {
84    fn from_index(index: u8) -> Result<Self> {
85        match index {
86            0 => Ok(HeaderField::ParentHash),
87            1 => Ok(HeaderField::OmmerHash),
88            2 => Ok(HeaderField::Beneficiary),
89            3 => Ok(HeaderField::StateRoot),
90            4 => Ok(HeaderField::TransactionsRoot),
91            5 => Ok(HeaderField::ReceiptsRoot),
92            6 => Ok(HeaderField::LogsBloom),
93            7 => Ok(HeaderField::Difficulty),
94            8 => Ok(HeaderField::Number),
95            9 => Ok(HeaderField::GasLimit),
96            10 => Ok(HeaderField::GasUsed),
97            11 => Ok(HeaderField::Timestamp),
98            12 => Ok(HeaderField::ExtraData),
99            13 => Ok(HeaderField::MixHash),
100            14 => Ok(HeaderField::Nonce),
101            15 => Ok(HeaderField::BaseFeePerGas),
102            16 => Ok(HeaderField::WithdrawalsRoot),
103            17 => Ok(HeaderField::BlobGasUsed),
104            18 => Ok(HeaderField::ExcessBlobGas),
105            19 => Ok(HeaderField::ParentBeaconBlockRoot),
106            _ => bail!("Unknown header field"),
107        }
108    }
109
110    fn to_index(&self) -> u8 {
111        match self {
112            HeaderField::ParentHash => 0,
113            HeaderField::OmmerHash => 1,
114            HeaderField::Beneficiary => 2,
115            HeaderField::StateRoot => 3,
116            HeaderField::TransactionsRoot => 4,
117            HeaderField::ReceiptsRoot => 5,
118            HeaderField::LogsBloom => 6,
119            HeaderField::Difficulty => 7,
120            HeaderField::Number => 8,
121            HeaderField::GasLimit => 9,
122            HeaderField::GasUsed => 10,
123            HeaderField::Timestamp => 11,
124            HeaderField::ExtraData => 12,
125            HeaderField::MixHash => 13,
126            HeaderField::Nonce => 14,
127            HeaderField::BaseFeePerGas => 15,
128            HeaderField::WithdrawalsRoot => 16,
129            HeaderField::BlobGasUsed => 17,
130            HeaderField::ExcessBlobGas => 18,
131            HeaderField::ParentBeaconBlockRoot => 19,
132        }
133    }
134
135    fn decode_field_from_rlp(&self, header_rlp: &[u8]) -> U256 {
136        let decoded = <Header>::rlp_decode(header_rlp);
137
138        match self {
139            HeaderField::ParentHash => decoded.parent_hash.into(),
140            HeaderField::OmmerHash => decoded.ommers_hash.into(),
141            HeaderField::Beneficiary => {
142                U256::from_str_radix(&decoded.beneficiary.to_string(), 16).unwrap()
143            }
144            HeaderField::StateRoot => decoded.state_root.into(),
145            HeaderField::TransactionsRoot => decoded.transactions_root.into(),
146            HeaderField::ReceiptsRoot => decoded.receipts_root.into(),
147            HeaderField::LogsBloom => U256::from_str_radix(&decoded.logs_bloom.to_string(), 16)
148                .expect("logs bloom does not match U256"),
149            HeaderField::Difficulty => U256::from(decoded.difficulty),
150            HeaderField::Number => U256::from(decoded.number),
151            HeaderField::GasLimit => U256::from(decoded.gas_limit),
152            HeaderField::GasUsed => U256::from(decoded.gas_used),
153            HeaderField::Timestamp => U256::from(decoded.timestamp),
154            HeaderField::ExtraData => todo!("extra data doesn't fit into U256"),
155            HeaderField::MixHash => decoded.mix_hash.into(),
156            HeaderField::Nonce => U256::from(decoded.nonce),
157            HeaderField::BaseFeePerGas => U256::from(
158                decoded
159                    .base_fee_per_gas
160                    .expect("base fee per gas does not exist"),
161            ),
162            HeaderField::WithdrawalsRoot => decoded
163                .withdrawals_root
164                .expect("withdrawals root does not exist")
165                .into(),
166            HeaderField::BlobGasUsed => U256::from(decoded.blob_gas_used.unwrap()),
167            HeaderField::ExcessBlobGas => U256::from(decoded.excess_blob_gas.unwrap()),
168            HeaderField::ParentBeaconBlockRoot => decoded
169                .parent_beacon_block_root
170                .expect("parent beacon block root does not exist")
171                .into(),
172        }
173    }
174}
175
176impl FromStr for HeaderField {
177    type Err = anyhow::Error;
178
179    fn from_str(s: &str) -> Result<Self> {
180        match s {
181            "PARENT_HASH" => Ok(HeaderField::ParentHash),
182            "OMMERS_HASH" => Ok(HeaderField::OmmerHash),
183            "BENEFICIARY" => Ok(HeaderField::Beneficiary),
184            "STATE_ROOT" => Ok(HeaderField::StateRoot),
185            "TRANSACTIONS_ROOT" => Ok(HeaderField::TransactionsRoot),
186            "RECEIPTS_ROOT" => Ok(HeaderField::ReceiptsRoot),
187            "LOGS_BLOOM" => Ok(HeaderField::LogsBloom),
188            "DIFFICULTY" => Ok(HeaderField::Difficulty),
189            "NUMBER" => Ok(HeaderField::Number),
190            "GAS_LIMIT" => Ok(HeaderField::GasLimit),
191            "GAS_USED" => Ok(HeaderField::GasUsed),
192            "TIMESTAMP" => Ok(HeaderField::Timestamp),
193            "EXTRA_DATA" => Ok(HeaderField::ExtraData),
194            "MIX_HASH" => Ok(HeaderField::MixHash),
195            "NONCE" => Ok(HeaderField::Nonce),
196            "BASE_FEE_PER_GAS" => Ok(HeaderField::BaseFeePerGas),
197            "WITHDRAWALS_ROOT" => Ok(HeaderField::WithdrawalsRoot),
198            "BLOB_GAS_USED" => Ok(HeaderField::BlobGasUsed),
199            "EXCESS_BLOB_GAS" => Ok(HeaderField::ExcessBlobGas),
200            "PARENT_BEACON_BLOCK_ROOT" => Ok(HeaderField::ParentBeaconBlockRoot),
201            _ => bail!("Unknown header field"),
202        }
203    }
204}
205
206impl TryFrom<String> for HeaderField {
207    type Error = anyhow::Error;
208
209    fn try_from(value: String) -> Result<Self> {
210        HeaderField::from_str(&value)
211    }
212}
213
214impl Display for HeaderField {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        match self {
217            HeaderField::ParentHash => write!(f, "PARENT_HASH"),
218            HeaderField::OmmerHash => write!(f, "OMMERS_HASH"),
219            HeaderField::Beneficiary => write!(f, "BENEFICIARY"),
220            HeaderField::StateRoot => write!(f, "STATE_ROOT"),
221            HeaderField::TransactionsRoot => write!(f, "TRANSACTIONS_ROOT"),
222            HeaderField::ReceiptsRoot => write!(f, "RECEIPTS_ROOT"),
223            HeaderField::LogsBloom => write!(f, "LOGS_BLOOM"),
224            HeaderField::Difficulty => write!(f, "DIFFICULTY"),
225            HeaderField::Number => write!(f, "NUMBER"),
226            HeaderField::GasLimit => write!(f, "GAS_LIMIT"),
227            HeaderField::GasUsed => write!(f, "GAS_USED"),
228            HeaderField::Timestamp => write!(f, "TIMESTAMP"),
229            HeaderField::ExtraData => write!(f, "EXTRA_DATA"),
230            HeaderField::MixHash => write!(f, "MIX_HASH"),
231            HeaderField::Nonce => write!(f, "NONCE"),
232            HeaderField::BaseFeePerGas => write!(f, "BASE_FEE_PER_GAS"),
233            HeaderField::WithdrawalsRoot => write!(f, "WITHDRAWALS_ROOT"),
234            HeaderField::BlobGasUsed => write!(f, "BLOB_GAS_USED"),
235            HeaderField::ExcessBlobGas => write!(f, "EXCESS_BLOB_GAS"),
236            HeaderField::ParentBeaconBlockRoot => write!(f, "PARENT_BEACON_BLOCK_ROOT"),
237        }
238    }
239}
240
241// == Account Field ==
242
243#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
244#[serde(try_from = "String")]
245pub enum AccountField {
246    Nonce,
247    Balance,
248    StorageRoot,
249    CodeHash,
250}
251
252impl AccountField {
253    pub fn variants() -> Vec<String> {
254        vec![
255            "NONCE".to_string(),
256            "BALANCE".to_string(),
257            "STORAGE_ROOT".to_string(),
258            "CODE_HASH".to_string(),
259        ]
260    }
261}
262
263impl FromStr for AccountField {
264    type Err = anyhow::Error;
265
266    fn from_str(s: &str) -> Result<Self> {
267        match s {
268            "NONCE" => Ok(AccountField::Nonce),
269            "BALANCE" => Ok(AccountField::Balance),
270            "STORAGE_ROOT" => Ok(AccountField::StorageRoot),
271            "CODE_HASH" => Ok(AccountField::CodeHash),
272            _ => bail!("Unknown account field"),
273        }
274    }
275}
276
277impl TryFrom<String> for AccountField {
278    type Error = anyhow::Error;
279
280    fn try_from(value: String) -> Result<Self> {
281        AccountField::from_str(&value)
282    }
283}
284
285impl DatalakeField for AccountField {
286    fn from_index(index: u8) -> Result<Self> {
287        match index {
288            0 => Ok(AccountField::Nonce),
289            1 => Ok(AccountField::Balance),
290            2 => Ok(AccountField::StorageRoot),
291            3 => Ok(AccountField::CodeHash),
292            _ => bail!("Invalid account field index"),
293        }
294    }
295
296    fn to_index(&self) -> u8 {
297        match self {
298            AccountField::Nonce => 0,
299            AccountField::Balance => 1,
300            AccountField::StorageRoot => 2,
301            AccountField::CodeHash => 3,
302        }
303    }
304
305    fn decode_field_from_rlp(&self, account_rlp: &[u8]) -> U256 {
306        let decoded = <Account>::rlp_decode(account_rlp);
307        match self {
308            AccountField::Nonce => U256::from(decoded.nonce),
309            AccountField::Balance => U256::from(decoded.balance),
310            AccountField::StorageRoot => decoded.storage_root.into(),
311            AccountField::CodeHash => decoded.code_hash.into(),
312        }
313    }
314}
315
316impl Display for AccountField {
317    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318        match self {
319            AccountField::Nonce => write!(f, "NONCE"),
320            AccountField::Balance => write!(f, "BALANCE"),
321            AccountField::StorageRoot => write!(f, "STORAGE_ROOT"),
322            AccountField::CodeHash => write!(f, "CODE_HASH"),
323        }
324    }
325}