web3/types/
block.rs

1use crate::types::{Bytes, H160, H2048, H256, H64, U256, U64};
2use serde::{de::Error, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
3
4/// The block header type returned from RPC calls.
5#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
6pub struct BlockHeader {
7    /// Hash of the block
8    pub hash: Option<H256>,
9    /// Hash of the parent
10    #[serde(rename = "parentHash")]
11    pub parent_hash: H256,
12    /// Hash of the uncles
13    #[serde(rename = "sha3Uncles")]
14    #[cfg_attr(feature = "allow-missing-fields", serde(default))]
15    pub uncles_hash: H256,
16    /// Miner/author's address.
17    #[serde(rename = "miner", default, deserialize_with = "null_to_default")]
18    pub author: H160,
19    /// State root hash
20    #[serde(rename = "stateRoot")]
21    pub state_root: H256,
22    /// Transactions root hash
23    #[serde(rename = "transactionsRoot")]
24    pub transactions_root: H256,
25    /// Transactions receipts root hash
26    #[serde(rename = "receiptsRoot")]
27    pub receipts_root: H256,
28    /// Block number. None if pending.
29    pub number: Option<U64>,
30    /// Gas Used
31    #[serde(rename = "gasUsed")]
32    pub gas_used: U256,
33    /// Gas Limit
34    #[serde(rename = "gasLimit")]
35    #[cfg_attr(feature = "allow-missing-fields", serde(default))]
36    pub gas_limit: U256,
37    /// Base fee per unit of gas (if past London)
38    #[serde(rename = "baseFeePerGas", skip_serializing_if = "Option::is_none")]
39    pub base_fee_per_gas: Option<U256>,
40    /// Extra data
41    #[serde(rename = "extraData")]
42    pub extra_data: Bytes,
43    /// Logs bloom
44    #[serde(rename = "logsBloom")]
45    pub logs_bloom: H2048,
46    /// Timestamp
47    pub timestamp: U256,
48    /// Difficulty
49    #[cfg_attr(feature = "allow-missing-fields", serde(default))]
50    pub difficulty: U256,
51    /// Mix Hash
52    #[serde(rename = "mixHash")]
53    pub mix_hash: Option<H256>,
54    /// Nonce
55    pub nonce: Option<H64>,
56}
57
58/// The block type returned from RPC calls.
59/// This is generic over a `TX` type.
60#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
61pub struct Block<TX> {
62    /// Hash of the block
63    pub hash: Option<H256>,
64    /// Hash of the parent
65    #[serde(rename = "parentHash")]
66    pub parent_hash: H256,
67    /// Hash of the uncles
68    #[serde(rename = "sha3Uncles")]
69    #[cfg_attr(feature = "allow-missing-fields", serde(default))]
70    pub uncles_hash: H256,
71    /// Miner/author's address.
72    #[serde(rename = "miner", default, deserialize_with = "null_to_default")]
73    pub author: H160,
74    /// State root hash
75    #[serde(rename = "stateRoot")]
76    pub state_root: H256,
77    /// Transactions root hash
78    #[serde(rename = "transactionsRoot")]
79    pub transactions_root: H256,
80    /// Transactions receipts root hash
81    #[serde(rename = "receiptsRoot")]
82    pub receipts_root: H256,
83    /// Block number. None if pending.
84    pub number: Option<U64>,
85    /// Gas Used
86    #[serde(rename = "gasUsed")]
87    pub gas_used: U256,
88    /// Gas Limit
89    #[serde(rename = "gasLimit")]
90    #[cfg_attr(feature = "allow-missing-fields", serde(default))]
91    pub gas_limit: U256,
92    /// Base fee per unit of gas (if past London)
93    #[serde(rename = "baseFeePerGas", skip_serializing_if = "Option::is_none")]
94    pub base_fee_per_gas: Option<U256>,
95    /// Extra data
96    #[serde(rename = "extraData")]
97    pub extra_data: Bytes,
98    /// Logs bloom
99    #[serde(rename = "logsBloom")]
100    pub logs_bloom: Option<H2048>,
101    /// Timestamp
102    pub timestamp: U256,
103    /// Difficulty
104    #[cfg_attr(feature = "allow-missing-fields", serde(default))]
105    pub difficulty: U256,
106    /// Total difficulty
107    #[serde(rename = "totalDifficulty")]
108    pub total_difficulty: Option<U256>,
109    /// Seal fields
110    #[serde(default, rename = "sealFields")]
111    pub seal_fields: Vec<Bytes>,
112    /// Uncles' hashes
113    #[cfg_attr(feature = "allow-missing-fields", serde(default))]
114    pub uncles: Vec<H256>,
115    /// Transactions
116    pub transactions: Vec<TX>,
117    /// Size in bytes
118    pub size: Option<U256>,
119    /// Mix Hash
120    #[serde(rename = "mixHash")]
121    pub mix_hash: Option<H256>,
122    /// Nonce
123    pub nonce: Option<H64>,
124}
125
126fn null_to_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
127where
128    T: Default + Deserialize<'de>,
129    D: Deserializer<'de>,
130{
131    let option = Option::deserialize(deserializer)?;
132    Ok(option.unwrap_or_default())
133}
134
135/// Block Number
136#[derive(Copy, Clone, Debug, PartialEq)]
137pub enum BlockNumber {
138    /// Finalized block
139    Finalized,
140    /// Safe block
141    Safe,
142    /// Latest block
143    Latest,
144    /// Earliest block (genesis)
145    Earliest,
146    /// Pending block (not yet part of the blockchain)
147    Pending,
148    /// Block by number from canon chain
149    Number(U64),
150}
151
152impl<T: Into<U64>> From<T> for BlockNumber {
153    fn from(num: T) -> Self {
154        BlockNumber::Number(num.into())
155    }
156}
157
158impl Serialize for BlockNumber {
159    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
160    where
161        S: Serializer,
162    {
163        match *self {
164            BlockNumber::Number(ref x) => serializer.serialize_str(&format!("0x{:x}", x)),
165            BlockNumber::Latest => serializer.serialize_str("latest"),
166            BlockNumber::Earliest => serializer.serialize_str("earliest"),
167            BlockNumber::Pending => serializer.serialize_str("pending"),
168            BlockNumber::Finalized => serializer.serialize_str("finalized"),
169            BlockNumber::Safe => serializer.serialize_str("safe"),
170        }
171    }
172}
173
174impl<'a> Deserialize<'a> for BlockNumber {
175    fn deserialize<D>(deserializer: D) -> Result<BlockNumber, D::Error>
176    where
177        D: Deserializer<'a>,
178    {
179        let value = String::deserialize(deserializer)?;
180        match value.as_str() {
181            "latest" => Ok(BlockNumber::Latest),
182            "earliest" => Ok(BlockNumber::Earliest),
183            "pending" => Ok(BlockNumber::Pending),
184            "finalized" => Ok(BlockNumber::Finalized),
185            "safe" => Ok(BlockNumber::Safe),
186            _ if value.starts_with("0x") => U64::from_str_radix(&value[2..], 16)
187                .map(BlockNumber::Number)
188                .map_err(|e| D::Error::custom(format!("invalid block number: {}", e))),
189            _ => Err(D::Error::custom("invalid block number: missing 0x prefix".to_string())),
190        }
191    }
192}
193
194/// Block Identifier
195#[derive(Copy, Clone, Debug, PartialEq)]
196pub enum BlockId {
197    /// By Hash
198    Hash(H256),
199    /// By Number
200    Number(BlockNumber),
201}
202
203impl Serialize for BlockId {
204    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
205    where
206        S: Serializer,
207    {
208        match *self {
209            BlockId::Hash(ref x) => {
210                let mut s = serializer.serialize_struct("BlockIdEip1898", 1)?;
211                s.serialize_field("blockHash", &format!("{:?}", x))?;
212                s.end()
213            }
214            BlockId::Number(ref num) => num.serialize(serializer),
215        }
216    }
217}
218
219impl From<U64> for BlockId {
220    fn from(num: U64) -> Self {
221        BlockNumber::Number(num).into()
222    }
223}
224
225impl From<BlockNumber> for BlockId {
226    fn from(num: BlockNumber) -> Self {
227        BlockId::Number(num)
228    }
229}
230
231impl From<H256> for BlockId {
232    fn from(hash: H256) -> Self {
233        BlockId::Hash(hash)
234    }
235}
236
237#[cfg(test)]
238mod tests {
239    use super::*;
240    use serde_json::Value;
241
242    #[test]
243    fn block_miner() {
244        let mut json = serde_json::json!(
245        {
246            "miner": "0x0000000000000000000000000000000000000001",
247            "number": "0x1b4",
248            "hash": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
249            "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5",
250            "mixHash": "0x1010101010101010101010101010101010101010101010101010101010101010",
251            "nonce": "0x0000000000000000",
252            "sealFields": [
253              "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2",
254              "0x0000000000000042"
255            ],
256            "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
257            "logsBloom":  "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
258            "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
259            "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
260            "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff",
261            "difficulty": "0x27f07",
262            "totalDifficulty": "0x27f07",
263            "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
264            "size": "0x27f07",
265            "gasLimit": "0x9f759",
266            "minGasPrice": "0x9f759",
267            "gasUsed": "0x9f759",
268            "timestamp": "0x54e34e8e",
269            "transactions": [],
270            "uncles": []
271          }
272        );
273
274        let block: Block<()> = serde_json::from_value(json.clone()).unwrap();
275        assert_eq!(block.author, H160::from_low_u64_be(1));
276        assert!(block.base_fee_per_gas.is_none());
277
278        // Null miner
279        // We test this too because it was observed that Infura nodes behave this way even though it
280        // goes against the ethrpc documentation.
281        json.as_object_mut().unwrap().insert("miner".to_string(), Value::Null);
282        let block: Block<()> = serde_json::from_value(json.clone()).unwrap();
283        assert_eq!(block.author, Default::default());
284
285        // No miner
286        json.as_object_mut().unwrap().remove("miner");
287        let block: Block<()> = serde_json::from_value(json).unwrap();
288        assert_eq!(block.author, Default::default());
289    }
290
291    #[test]
292    fn post_london_block() {
293        let json = serde_json::json!(
294        {
295            "baseFeePerGas": "0x7",
296            "miner": "0x0000000000000000000000000000000000000001",
297            "number": "0x1b4",
298            "hash": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
299            "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5",
300            "mixHash": "0x1010101010101010101010101010101010101010101010101010101010101010",
301            "nonce": "0x0000000000000000",
302            "sealFields": [
303              "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2",
304              "0x0000000000000042"
305            ],
306            "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
307            "logsBloom":  "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273310e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
308            "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
309            "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
310            "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff",
311            "difficulty": "0x27f07",
312            "totalDifficulty": "0x27f07",
313            "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
314            "size": "0x27f07",
315            "gasLimit": "0x9f759",
316            "minGasPrice": "0x9f759",
317            "gasUsed": "0x9f759",
318            "timestamp": "0x54e34e8e",
319            "transactions": [],
320            "uncles": []
321          }
322        );
323
324        let block: Block<()> = serde_json::from_value(json).unwrap();
325        assert_eq!(block.base_fee_per_gas, Some(U256::from(7)));
326    }
327
328    #[test]
329    fn serialize_deserialize_block_number() {
330        // BlockNumber::Latest
331        let serialized = serde_json::to_value(BlockNumber::Latest).unwrap();
332        assert_eq!(serialized, "latest");
333        let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
334        assert_eq!(deserialized, BlockNumber::Latest);
335
336        // BlockNumber::Earliest
337        let serialized = serde_json::to_value(BlockNumber::Earliest).unwrap();
338        assert_eq!(serialized, "earliest");
339        let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
340        assert_eq!(deserialized, BlockNumber::Earliest);
341
342        // BlockNumber::Pending
343        let serialized = serde_json::to_value(BlockNumber::Pending).unwrap();
344        assert_eq!(serialized, "pending");
345        let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
346        assert_eq!(deserialized, BlockNumber::Pending);
347
348        // BlockNumber::Finalized
349        let serialized = serde_json::to_value(BlockNumber::Finalized).unwrap();
350        assert_eq!(serialized, "finalized");
351        let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
352        assert_eq!(deserialized, BlockNumber::Finalized);
353
354        // BlockNumber::Safe
355        let serialized = serde_json::to_value(BlockNumber::Safe).unwrap();
356        assert_eq!(serialized, "safe");
357        let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
358        assert_eq!(deserialized, BlockNumber::Safe);
359
360        // BlockNumber::Number
361        let serialized = serde_json::to_value(BlockNumber::Number(100.into())).unwrap();
362        assert_eq!(serialized, "0x64");
363        let deserialized = serde_json::from_value::<BlockNumber>(serialized).unwrap();
364        assert_eq!(deserialized, BlockNumber::Number(100.into()));
365        let deserialized = serde_json::from_value::<BlockNumber>("64".into());
366        assert_eq!(
367            deserialized.unwrap_err().to_string(),
368            "invalid block number: missing 0x prefix"
369        );
370    }
371}