diff --git a/ethers-core/src/types/mod.rs b/ethers-core/src/types/mod.rs index a5703e95..c03e9a7a 100644 --- a/ethers-core/src/types/mod.rs +++ b/ethers-core/src/types/mod.rs @@ -68,3 +68,6 @@ mod other; pub use other::OtherFields; pub mod serde_helpers; + +mod syncing; +pub use syncing::{SyncProgress, SyncingStatus}; diff --git a/ethers-core/src/types/syncing.rs b/ethers-core/src/types/syncing.rs new file mode 100644 index 00000000..58e685a6 --- /dev/null +++ b/ethers-core/src/types/syncing.rs @@ -0,0 +1,164 @@ +//! Types for `eth_syncing` RPC call + +use crate::types::U64; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +/// Structure used in `eth_syncing` RPC +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum SyncingStatus { + /// When client is synced to highest block, eth_syncing with return string "false" + IsFalse, + /// When client is still syncing past blocks we get IsSyncing information. + IsSyncing(Box), +} + +impl Serialize for SyncingStatus { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + SyncingStatus::IsFalse => serializer.serialize_bool(false), + SyncingStatus::IsSyncing(sync) => sync.serialize(serializer), + } + } +} + +impl<'de> Deserialize<'de> for SyncingStatus { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Debug, Serialize, Deserialize)] + #[serde(untagged)] + pub enum SyncingStatusIntermediate { + /// When client is synced to the highest block, eth_syncing with return string "false" + IsFalse(bool), + /// When client is still syncing past blocks we get IsSyncing information. + IsSyncing(Box), + } + + match SyncingStatusIntermediate::deserialize(deserializer)? { + SyncingStatusIntermediate::IsFalse(false) => Ok(SyncingStatus::IsFalse), + SyncingStatusIntermediate::IsFalse(true) => Err(serde::de::Error::custom( + "eth_syncing returned `true` that is undefined value.", + )), + SyncingStatusIntermediate::IsSyncing(sync) => Ok(SyncingStatus::IsSyncing(sync)), + } + } +} + +/// Represents the sync status of the node +/// +/// **Note:** while the `eth_syncing` RPC response is defined as: +/// +/// > Returns: +/// > +/// > Object|Boolean, An object with sync status data or FALSE, when not syncing: +/// +/// > startingBlock: QUANTITY - The block at which the import started (will only be reset, after the +/// > sync reached his head) +/// > currentBlock: QUANTITY - The current block, same as eth_blockNumber +/// > highestBlock: QUANTITY - The estimated highest block +/// +/// Geth returns additional fields: +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SyncProgress { + pub current_block: U64, + pub highest_block: U64, + pub starting_block: U64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub pulled_states: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub known_states: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub healed_bytecode_bytes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub healed_bytecodes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub healed_trienode_bytes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub healed_trienodes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub healing_bytecode: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub healing_trienodes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub synced_account_bytes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub synced_accounts: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub synced_bytecode_bytes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub synced_bytecodes: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub synced_storage: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub synced_storage_bytes: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + + // + #[test] + fn deserialize_sync_geth() { + let s = r#"{ + "currentBlock": "0xeaa2b4", + "healedBytecodeBytes": "0xaad91fe", + "healedBytecodes": "0x61d3", + "healedTrienodeBytes": "0x156ac02b1", + "healedTrienodes": "0x2885aa4", + "healingBytecode": "0x0", + "healingTrienodes": "0x454", + "highestBlock": "0xeaa329", + "startingBlock": "0xea97ee", + "syncedAccountBytes": "0xa29fec90d", + "syncedAccounts": "0xa7ed9ad", + "syncedBytecodeBytes": "0xdec39008", + "syncedBytecodes": "0x8d407", + "syncedStorage": "0x2a517da1", + "syncedStorageBytes": "0x23634dbedf" + }"#; + + let sync: SyncingStatus = serde_json::from_str(s).unwrap(); + match sync { + SyncingStatus::IsFalse => { + panic!("unexpected variant") + } + SyncingStatus::IsSyncing(_) => {} + } + } + + #[test] + fn deserialize_sync_minimal() { + let s = r#"{ + "currentBlock": "0xeaa2b4", + "highestBlock": "0xeaa329", + "startingBlock": "0xea97ee" + }"#; + + let sync: SyncingStatus = serde_json::from_str(s).unwrap(); + match sync { + SyncingStatus::IsFalse => { + panic!("unexpected variant") + } + SyncingStatus::IsSyncing(_) => {} + } + } + + #[test] + fn deserialize_sync_false() { + let s = r#"false"#; + + let sync: SyncingStatus = serde_json::from_str(s).unwrap(); + match sync { + SyncingStatus::IsFalse => {} + SyncingStatus::IsSyncing(_) => { + panic!("unexpected variant") + } + } + } +} diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs index f5f58fa3..8a323db1 100644 --- a/ethers-providers/src/lib.rs +++ b/ethers-providers/src/lib.rs @@ -88,15 +88,6 @@ where } } -/// Structure used in eth_syncing RPC -#[derive(Clone, Debug)] -pub enum SyncingStatus { - /// When client is synced to highest block, eth_syncing with return string "false" - IsFalse, - /// When client is still syncing past blocks we get IsSyncing information. - IsSyncing { starting_block: U256, current_block: U256, highest_block: U256 }, -} - /// A middleware allows customizing requests send and received from an ethereum node. /// /// Writing a middleware is as simple as: diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index ed57facd..7d36041b 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -29,7 +29,7 @@ use ethers_core::{ utils, }; use hex::FromHex; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Serialize}; use thiserror::Error; use url::{ParseError, Url}; @@ -539,26 +539,7 @@ impl Middleware for Provider

{ /// Return current client syncing status. If IsFalse sync is over. async fn syncing(&self) -> Result { - #[derive(Debug, Serialize, Deserialize)] - #[serde(untagged)] - pub enum SyncingStatusIntermediate { - /// When client is synced to highest block, eth_syncing with return string "false" - IsFalse(bool), - /// When client is still syncing past blocks we get IsSyncing information. - IsSyncing { starting_block: U256, current_block: U256, highest_block: U256 }, - } - let intermediate: SyncingStatusIntermediate = self.request("eth_syncing", ()).await?; - match intermediate { - SyncingStatusIntermediate::IsFalse(false) => Ok(SyncingStatus::IsFalse), - SyncingStatusIntermediate::IsFalse(true) => Err(ProviderError::CustomError( - "eth_syncing returned `true` that is undefined value.".to_owned(), - )), - SyncingStatusIntermediate::IsSyncing { - starting_block, - current_block, - highest_block, - } => Ok(SyncingStatus::IsSyncing { starting_block, current_block, highest_block }), - } + self.request("eth_syncing", ()).await } /// Returns the network version.