pink_web3/types/
block.rs

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