neo3/neo_types/
syncing.rs

1//! Types for `neo_syncing` RPC call
2
3use ethereum_types::U64;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6/// Structure used in `neo_syncing` RPC
7#[derive(Clone, Debug, Eq, PartialEq)]
8pub enum SyncingStatus {
9	/// When client is synced to highest block, neo_syncing with return string "false"
10	IsFalse,
11	/// When client is still syncing past blocks we get IsSyncing information.
12	IsSyncing(Box<SyncProgress>),
13}
14
15impl Serialize for SyncingStatus {
16	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
17	where
18		S: Serializer,
19	{
20		match self {
21			SyncingStatus::IsFalse => serializer.serialize_bool(false),
22			SyncingStatus::IsSyncing(sync) => sync.serialize(serializer),
23		}
24	}
25}
26
27impl<'de> Deserialize<'de> for SyncingStatus {
28	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
29	where
30		D: Deserializer<'de>,
31	{
32		#[derive(Debug, Serialize, Deserialize)]
33		#[serde(untagged)]
34		pub(crate) enum SyncingStatusIntermediate {
35			/// When client is synced to the highest block, neo_syncing with return string "false"
36			IsFalse(bool),
37			/// When client is still syncing past blocks we get IsSyncing information.
38			IsSyncing(Box<SyncProgress>),
39		}
40
41		match SyncingStatusIntermediate::deserialize(deserializer)? {
42			SyncingStatusIntermediate::IsFalse(false) => Ok(SyncingStatus::IsFalse),
43			SyncingStatusIntermediate::IsFalse(true) => Err(serde::de::Error::custom(
44				"neo_syncing returned `true` that is undefined value.",
45			)),
46			SyncingStatusIntermediate::IsSyncing(sync) => Ok(SyncingStatus::IsSyncing(sync)),
47		}
48	}
49}
50
51/// Represents the sync status of the node
52///
53/// **Note:** while the `neo_syncing` RPC response is defined as:
54///
55/// > Returns:
56/// >
57/// > Object|Boolean, An object with sync status data or FALSE, when not syncing:
58///
59/// > startingBlock: QUANTITY - The block at which the import started (will only be reset, after the
60/// > sync reached his head)
61/// > currentBlock: QUANTITY - The current block, same as neo_blockNumber
62/// > highestBlock: QUANTITY - The estimated highest block
63///
64/// Geth returns additional fields: <https://github.com/neo/go-neo/blob/0ce494b60cd00d70f1f9f2dd0b9bfbd76204168a/ethclient/ethclient.go#L597-L617>
65#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
66#[serde(rename_all = "camelCase")]
67/// Struct to track progress of node syncing
68pub struct SyncProgress {
69	/// Current block number we've processed
70	pub current_block: U64,
71
72	/// Highest known block number on the chain
73	pub highest_block: U64,
74
75	/// Block number we started syncing from
76	pub starting_block: U64,
77
78	/// Number of state entries pulled so far (optional)
79	#[serde(default, skip_serializing_if = "Option::is_none")]
80	pub pulled_states: Option<U64>,
81
82	/// Total number of known state entries (optional)
83	#[serde(default, skip_serializing_if = "Option::is_none")]
84	pub known_states: Option<U64>,
85
86	/// Bytes of bytecodes healed
87	#[serde(default, skip_serializing_if = "Option::is_none")]
88	pub healed_bytecode_bytes: Option<U64>,
89
90	/// Number of bytecode entries healed
91	#[serde(default, skip_serializing_if = "Option::is_none")]
92	pub healed_bytecodes: Option<U64>,
93
94	/// Bytes of trie nodes healed
95	#[serde(default, skip_serializing_if = "Option::is_none")]
96	pub healed_trienode_bytes: Option<U64>,
97
98	/// Number of trie nodes healed
99	#[serde(default, skip_serializing_if = "Option::is_none")]
100	pub healed_trienodes: Option<U64>,
101
102	/// Bytecode entries currently healing
103	#[serde(default, skip_serializing_if = "Option::is_none")]
104	pub healing_bytecode: Option<U64>,
105
106	/// Trie nodes currently healing
107	#[serde(default, skip_serializing_if = "Option::is_none")]
108	pub healing_trienodes: Option<U64>,
109
110	/// Bytes of account data synced
111	#[serde(default, skip_serializing_if = "Option::is_none")]
112	pub synced_account_bytes: Option<U64>,
113
114	/// Accounts synced
115	#[serde(default, skip_serializing_if = "Option::is_none")]
116	pub synced_accounts: Option<U64>,
117
118	/// Bytes of bytecode synced
119	#[serde(default, skip_serializing_if = "Option::is_none")]
120	pub synced_bytecode_bytes: Option<U64>,
121
122	/// Bytecode entries synced
123	#[serde(default, skip_serializing_if = "Option::is_none")]
124	pub synced_bytecodes: Option<U64>,
125
126	/// Storage entries synced
127	#[serde(default, skip_serializing_if = "Option::is_none")]
128	pub synced_storage: Option<U64>,
129
130	/// Bytes of storage synced
131	#[serde(default, skip_serializing_if = "Option::is_none")]
132	pub synced_storage_bytes: Option<U64>,
133}
134
135#[cfg(test)]
136mod tests {
137	use super::*;
138
139	// <https://github.com/gakonst/neo-rs/issues/1623>
140	#[test]
141	fn deserialize_sync_geth() {
142		let s = r#"{
143        "currentBlock": "0xeaa2b4",
144        "healedBytecodeBytes": "0xaad91fe",
145        "healedBytecodes": "0x61d3",
146        "healedTrienodeBytes": "0x156ac02b1",
147        "healedTrienodes": "0x2885aa4",
148        "healingBytecode": "0x0",
149        "healingTrienodes": "0x454",
150        "highestBlock": "0xeaa329",
151        "startingBlock": "0xea97ee",
152        "syncedAccountBytes": "0xa29fec90d",
153        "syncedAccounts": "0xa7ed9ad",
154        "syncedBytecodeBytes": "0xdec39008",
155        "syncedBytecodes": "0x8d407",
156        "syncedStorage": "0x2a517da1",
157        "syncedStorageBytes": "0x23634dbedf"
158    }"#;
159
160		let sync: SyncingStatus = serde_json::from_str(s).unwrap();
161		match sync {
162			SyncingStatus::IsFalse => {
163				assert!(false, "Expected IsSyncing variant, got IsFalse")
164			},
165			SyncingStatus::IsSyncing(_) => {},
166		}
167	}
168
169	#[test]
170	fn deserialize_sync_minimal() {
171		let s = r#"{
172        "currentBlock": "0xeaa2b4",
173        "highestBlock": "0xeaa329",
174        "startingBlock": "0xea97ee"
175    }"#;
176
177		let sync: SyncingStatus = serde_json::from_str(s).unwrap();
178		match sync {
179			SyncingStatus::IsFalse => {
180				assert!(false, "Expected IsSyncing variant, got IsFalse")
181			},
182			SyncingStatus::IsSyncing(_) => {},
183		}
184	}
185
186	#[test]
187	fn deserialize_sync_false() {
188		let s = r"false";
189
190		let sync: SyncingStatus = serde_json::from_str(s).unwrap();
191		match sync {
192			SyncingStatus::IsFalse => {},
193			SyncingStatus::IsSyncing(_) => {
194				assert!(false, "Expected IsFalse variant, got IsSyncing")
195			},
196		}
197	}
198}