cfx_rpc_eth_types/
block_number.rs

1// Copyright 2015-2020 Parity Technologies (UK) Ltd.
2// This file is part of OpenEthereum.
3
4// OpenEthereum is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// OpenEthereum is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with OpenEthereum.  If not, see <http://www.gnu.org/licenses/>.
16
17// Copyright 2022 Conflux Foundation. All rights reserved.
18// Conflux is free software and distributed under GNU General Public License.
19// See http://www.gnu.org/licenses/
20
21use crate::Error;
22use cfx_types::H256;
23use primitives::{BlockHashOrEpochNumber, EpochNumber};
24use serde::{
25    de::{Error as SerdeError, MapAccess, Visitor},
26    Deserialize, Deserializer, Serialize, Serializer,
27};
28use std::{convert::TryFrom, fmt};
29
30/// Represents rpc api block number param.
31#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
32pub enum BlockId {
33    /// Hash
34    Hash {
35        /// block hash
36        hash: H256,
37        /// only return blocks part of the canon chain
38        // note: we only keep this for compatibility
39        require_canonical: Option<bool>,
40    },
41    /// Number
42    Num(u64),
43    /// Latest block
44    Latest,
45    /// Earliest block (genesis)
46    Earliest,
47    /// Pending block (being mined)
48    Pending,
49    /// Compatibility tag support for ethereum "safe" tag. Will reflect to
50    /// "latest_confirmed"
51    Safe,
52    /// Finalized block
53    Finalized,
54}
55
56impl Default for BlockId {
57    fn default() -> Self { BlockId::Latest }
58}
59
60impl<'a> Deserialize<'a> for BlockId {
61    fn deserialize<D>(deserializer: D) -> Result<BlockId, D::Error>
62    where D: Deserializer<'a> {
63        deserializer.deserialize_any(BlockNumberVisitor)
64    }
65}
66
67impl BlockId {
68    /// Convert block number to min block target.
69    pub fn to_min_block_num(&self) -> Option<u64> {
70        match *self {
71            BlockId::Num(ref x) => Some(*x),
72            _ => None,
73        }
74    }
75}
76
77impl Serialize for BlockId {
78    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
79    where S: Serializer {
80        use serde::ser::SerializeStruct;
81
82        match *self {
83            BlockId::Hash {
84                hash,
85                require_canonical,
86            } => {
87                let mut s = serializer.serialize_struct("BlockIdEip1898", 1)?;
88                s.serialize_field("blockHash", &hash)?;
89                if let Some(require_canonical) = require_canonical {
90                    s.serialize_field("requireCanonical", &require_canonical)?;
91                }
92                s.end()
93            }
94            BlockId::Num(ref x) => {
95                serializer.serialize_str(&format!("0x{:x}", x))
96            }
97            BlockId::Latest => serializer.serialize_str("latest"),
98            BlockId::Earliest => serializer.serialize_str("earliest"),
99            BlockId::Pending => serializer.serialize_str("pending"),
100            BlockId::Safe => serializer.serialize_str("safe"),
101            BlockId::Finalized => serializer.serialize_str("finalized"),
102        }
103    }
104}
105
106struct BlockNumberVisitor;
107
108impl<'a> Visitor<'a> for BlockNumberVisitor {
109    type Value = BlockId;
110
111    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
112        write!(
113            formatter,
114            "a block number or 'latest', 'earliest' or 'pending'"
115        )
116    }
117
118    fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
119    where V: MapAccess<'a> {
120        let (mut require_canonical, mut block_number, mut block_hash) =
121            (None::<bool>, None::<u64>, None::<H256>);
122
123        loop {
124            let key_str: Option<String> = visitor.next_key()?;
125
126            match key_str {
127                Some(key) => match key.as_str() {
128                    "blockNumber" => {
129                        let value: String = visitor.next_value()?;
130                        if value.starts_with("0x") {
131                            let number = u64::from_str_radix(&value[2..], 16)
132                                .map_err(|e| {
133                                SerdeError::custom(format!(
134                                    "Invalid block number: {}",
135                                    e
136                                ))
137                            })?;
138
139                            block_number = Some(number);
140                            break;
141                        } else {
142                            return Err(SerdeError::custom(
143                                "Invalid block number: missing 0x prefix"
144                                    .to_string(),
145                            ));
146                        }
147                    }
148                    "blockHash" => {
149                        block_hash = Some(visitor.next_value()?);
150                    }
151                    "requireCanonical" => {
152                        require_canonical = Some(visitor.next_value()?);
153                    }
154                    key => {
155                        return Err(SerdeError::custom(format!(
156                            "Unknown key: {}",
157                            key
158                        )))
159                    }
160                },
161                None => break,
162            };
163        }
164
165        if let Some(number) = block_number {
166            return Ok(BlockId::Num(number));
167        }
168
169        if let Some(hash) = block_hash {
170            return Ok(BlockId::Hash {
171                hash,
172                require_canonical,
173            });
174        }
175
176        return Err(SerdeError::custom("Invalid input"));
177    }
178
179    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
180    where E: SerdeError {
181        match value {
182            "latest" => Ok(BlockId::Latest),
183            "earliest" => Ok(BlockId::Earliest),
184            "pending" => Ok(BlockId::Pending),
185            "safe" => Ok(BlockId::Safe),
186            "finalized" => Ok(BlockId::Finalized),
187            _ if value.starts_with("0x") => {
188                // Since there is no way to clearly distinguish between a DATA parameter and a QUANTITY parameter. A str is therefore deserialized into a Block Number: <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md>
189                // However, since the hex string should be a QUANTITY, we can safely assume that if the len is 66 bytes, it is in fact a hash, ref <https://github.com/ethereum/go-ethereum/blob/ee530c0d5aa70d2c00ab5691a89ab431b73f8165/rpc/types.go#L184-L184>
190                if value.len() == 66 {
191                    let hash =
192                        value[2..].parse().map_err(SerdeError::custom)?;
193                    Ok(BlockId::Hash {
194                        hash,
195                        require_canonical: None,
196                    })
197                } else {
198                    u64::from_str_radix(&value[2..], 16)
199                        .map(BlockId::Num)
200                        .map_err(|e| {
201                            SerdeError::custom(format!(
202                                "Invalid block number: {}",
203                                e
204                            ))
205                        })
206                }
207            }
208            _ => Err(SerdeError::custom(
209                "Invalid block number: missing 0x prefix".to_string(),
210            )),
211        }
212    }
213
214    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
215    where E: SerdeError {
216        self.visit_str(value.as_ref())
217    }
218}
219
220impl TryFrom<BlockId> for EpochNumber {
221    type Error = Error;
222
223    fn try_from(x: BlockId) -> Result<EpochNumber, Error> {
224        match x {
225            BlockId::Num(num) => Ok(EpochNumber::Number(num)),
226            BlockId::Latest => Ok(EpochNumber::LatestState),
227            BlockId::Earliest => Ok(EpochNumber::Earliest),
228            BlockId::Pending => Ok(EpochNumber::LatestState), /* TODO: LatestMined maybe is better or add a new Pending variant for EpochNumber */
229            BlockId::Safe => Ok(EpochNumber::LatestConfirmed),
230            BlockId::Finalized => Ok(EpochNumber::LatestFinalized),
231            BlockId::Hash { .. } => Err(Error::InvalidParams(
232                "block_num".into(),
233                "Expected block number, found block hash".into(),
234            )),
235        }
236    }
237}
238
239impl From<BlockId> for BlockHashOrEpochNumber {
240    fn from(x: BlockId) -> BlockHashOrEpochNumber {
241        match x {
242            BlockId::Num(num) => {
243                BlockHashOrEpochNumber::EpochNumber(EpochNumber::Number(num))
244            }
245            BlockId::Latest => {
246                BlockHashOrEpochNumber::EpochNumber(EpochNumber::LatestState)
247            }
248            BlockId::Earliest => {
249                BlockHashOrEpochNumber::EpochNumber(EpochNumber::Earliest)
250            }
251            BlockId::Pending => {
252                BlockHashOrEpochNumber::EpochNumber(EpochNumber::LatestState)
253            }
254            BlockId::Safe => BlockHashOrEpochNumber::EpochNumber(
255                EpochNumber::LatestConfirmed,
256            ),
257            BlockId::Finalized => BlockHashOrEpochNumber::EpochNumber(
258                EpochNumber::LatestFinalized,
259            ),
260            BlockId::Hash {
261                hash,
262                require_canonical,
263            } => BlockHashOrEpochNumber::BlockHashWithOption {
264                hash,
265                require_pivot: require_canonical,
266            },
267        }
268    }
269}
270
271#[cfg(test)]
272mod tests {
273    use super::*;
274    use serde_json;
275    use std::str::FromStr;
276
277    #[test]
278    fn block_number_serialization() {
279        let hash = H256::from_str(
280            "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
281        )
282        .unwrap();
283
284        let block_number = BlockId::Hash {
285            hash,
286            require_canonical: None,
287        };
288        let serialized = serde_json::to_string(&block_number).unwrap();
289        assert_eq!(
290            serialized,
291            r#"{"blockHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"}"#
292        );
293
294        let block_number = BlockId::Hash {
295            hash,
296            require_canonical: Some(false),
297        };
298        let serialized = serde_json::to_string(&block_number).unwrap();
299        assert_eq!(
300            serialized,
301            r#"{"blockHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","requireCanonical":false}"#
302        );
303
304        let block_number = BlockId::Hash {
305            hash,
306            require_canonical: Some(true),
307        };
308        let serialized = serde_json::to_string(&block_number).unwrap();
309        assert_eq!(
310            serialized,
311            r#"{"blockHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","requireCanonical":true}"#
312        );
313    }
314
315    #[test]
316    fn block_number_deserialization() {
317        let s = r#"[
318			"0xa",
319			"latest",
320			"earliest",
321			"pending",
322            "safe",
323            "finalized",
324			{"blockNumber": "0xa"},
325			{"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"},
326            {"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "requireCanonical": false},
327			{"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "requireCanonical": true}
328		]"#;
329        let deserialized: Vec<BlockId> = serde_json::from_str(s).unwrap();
330
331        assert_eq!(
332            deserialized,
333            vec![
334                BlockId::Num(10),
335                BlockId::Latest,
336                BlockId::Earliest,
337                BlockId::Pending,
338                BlockId::Safe,
339                BlockId::Finalized,
340                BlockId::Num(10),
341                BlockId::Hash {
342                    hash: H256::from_str(
343                        "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
344                    )
345                    .unwrap(),
346                    require_canonical: None,
347                },
348                BlockId::Hash {
349                    hash: H256::from_str(
350                        "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
351                    )
352                    .unwrap(),
353                    require_canonical: Some(false),
354                },
355                BlockId::Hash {
356                    hash: H256::from_str(
357                        "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
358                    )
359                    .unwrap(),
360                    require_canonical: Some(true),
361                }
362            ]
363        )
364    }
365
366    #[test]
367    fn should_not_deserialize() {
368        let s = r#"[{}, "10"]"#;
369        assert!(serde_json::from_str::<Vec<BlockId>>(s).is_err());
370    }
371}