feat: add eip-1898 deserialize for BlockId (#1257)

This commit is contained in:
Matthias Seitz 2022-05-13 21:56:14 +02:00 committed by GitHub
parent f5efbbb86a
commit 94cf8a8f77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 136 additions and 5 deletions

View File

@ -3,8 +3,13 @@ use crate::types::{Address, Bloom, Bytes, Transaction, TxHash, H256, U256, U64};
use chrono::{DateTime, TimeZone, Utc}; use chrono::{DateTime, TimeZone, Utc};
#[cfg(not(feature = "celo"))] #[cfg(not(feature = "celo"))]
use core::cmp::Ordering; use core::cmp::Ordering;
use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr; use serde::{
de::{MapAccess, Visitor},
ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt::Formatter, str::FromStr};
use thiserror::Error; use thiserror::Error;
/// The block type returned from RPC calls. /// The block type returned from RPC calls.
@ -446,6 +451,67 @@ impl Serialize for BlockId {
} }
} }
impl<'de> Deserialize<'de> for BlockId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct BlockIdVisitor;
impl<'de> Visitor<'de> for BlockIdVisitor {
type Value = BlockId;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("Block identifier following EIP-1898")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(BlockId::Number(v.parse().map_err(serde::de::Error::custom)?))
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut number = None;
let mut hash = None;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"blockNumber" => {
if number.is_some() || hash.is_some() {
return Err(serde::de::Error::duplicate_field("blockNumber"))
}
number = Some(BlockId::Number(map.next_value::<BlockNumber>()?))
}
"blockHash" => {
if number.is_some() || hash.is_some() {
return Err(serde::de::Error::duplicate_field("blockHash"))
}
hash = Some(BlockId::Hash(map.next_value::<H256>()?))
}
key => {
return Err(serde::de::Error::unknown_field(
key,
&["blockNumber", "blockHash"],
))
}
}
}
number.or(hash).ok_or_else(|| {
serde::de::Error::custom("Expected `blockNumber` or `blockHash`")
})
}
}
deserializer.deserialize_any(BlockIdVisitor)
}
}
/// A block Number (or tag - "latest", "earliest", "pending") /// A block Number (or tag - "latest", "earliest", "pending")
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum BlockNumber { pub enum BlockNumber {
@ -495,12 +561,21 @@ impl<'de> Deserialize<'de> for BlockNumber {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let s = String::deserialize(deserializer)?.to_lowercase(); let s = String::deserialize(deserializer)?.to_lowercase();
Ok(match s.as_str() { s.parse().map_err(serde::de::Error::custom)
}
}
impl FromStr for BlockNumber {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let block = match s {
"latest" => Self::Latest, "latest" => Self::Latest,
"earliest" => Self::Earliest, "earliest" => Self::Earliest,
"pending" => Self::Pending, "pending" => Self::Pending,
n => BlockNumber::Number(U64::from_str(n).map_err(serde::de::Error::custom)?), n => BlockNumber::Number(n.parse::<U64>().map_err(|err| err.to_string())?),
}) };
Ok(block)
} }
} }
@ -510,6 +585,62 @@ mod tests {
use super::*; use super::*;
use crate::types::{Transaction, TxHash}; use crate::types::{Transaction, TxHash};
#[test]
fn can_parse_eip1898_block_ids() {
let num = serde_json::json!(
{ "blockNumber": "0x0" }
);
let id = serde_json::from_value::<BlockId>(num).unwrap();
assert_eq!(id, BlockId::Number(BlockNumber::Number(0u64.into())));
let num = serde_json::json!(
{ "blockNumber": "pending" }
);
let id = serde_json::from_value::<BlockId>(num).unwrap();
assert_eq!(id, BlockId::Number(BlockNumber::Pending));
let num = serde_json::json!(
{ "blockNumber": "latest" }
);
let id = serde_json::from_value::<BlockId>(num).unwrap();
assert_eq!(id, BlockId::Number(BlockNumber::Latest));
let num = serde_json::json!(
{ "blockNumber": "earliest" }
);
let id = serde_json::from_value::<BlockId>(num).unwrap();
assert_eq!(id, BlockId::Number(BlockNumber::Earliest));
let num = serde_json::json!("0x0");
let id = serde_json::from_value::<BlockId>(num).unwrap();
assert_eq!(id, BlockId::Number(BlockNumber::Number(0u64.into())));
let num = serde_json::json!("pending");
let id = serde_json::from_value::<BlockId>(num).unwrap();
assert_eq!(id, BlockId::Number(BlockNumber::Pending));
let num = serde_json::json!("latest");
let id = serde_json::from_value::<BlockId>(num).unwrap();
assert_eq!(id, BlockId::Number(BlockNumber::Latest));
let num = serde_json::json!("earliest");
let id = serde_json::from_value::<BlockId>(num).unwrap();
assert_eq!(id, BlockId::Number(BlockNumber::Earliest));
let num = serde_json::json!(
{ "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" }
);
let id = serde_json::from_value::<BlockId>(num).unwrap();
assert_eq!(
id,
BlockId::Hash(
"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"
.parse()
.unwrap()
)
);
}
#[test] #[test]
fn serde_block_number() { fn serde_block_number() {
for b in &[BlockNumber::Latest, BlockNumber::Earliest, BlockNumber::Pending] { for b in &[BlockNumber::Latest, BlockNumber::Earliest, BlockNumber::Pending] {