Skip to main content

serde_hex_utils/
lib.rs

1// Copyright (C) Polytope Labs Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Utilities for serde serialization and deserialization
17
18#![cfg_attr(not(feature = "std"), no_std)]
19#![deny(missing_docs)]
20
21extern crate alloc;
22use alloc::{format, vec::Vec};
23use anyhow::anyhow;
24
25const HEX_ENCODING_PREFIX: &str = "0x";
26
27/// Vec from Hex string
28pub fn try_bytes_from_hex_str(s: &str) -> Result<Vec<u8>, anyhow::Error> {
29	let target = s.replace(HEX_ENCODING_PREFIX, "");
30	let data = hex::decode(target).map_err(|e| anyhow!("{e:?}"))?;
31	Ok(data)
32}
33
34/// Hex serializer and Deserializer for Vec<u8>
35pub mod as_hex {
36	use super::*;
37	use alloc::string::String;
38	use serde::de::Deserialize;
39
40	/// Serialize Vec<u8> into hex string
41	pub fn serialize<S, T: AsRef<[u8]>>(data: T, serializer: S) -> Result<S::Ok, S::Error>
42	where
43		S: serde::Serializer,
44	{
45		let encoding = hex::encode(data.as_ref());
46		let output = format!("{HEX_ENCODING_PREFIX}{encoding}");
47		serializer.collect_str(&output)
48	}
49
50	/// Serialize Option<Vec<u8>> into hex string
51	pub fn serialize_option<S, T: AsRef<[u8]>>(
52		data: &Option<T>,
53		serializer: S,
54	) -> Result<S::Ok, S::Error>
55	where
56		S: serde::Serializer,
57	{
58		if let Some(data) = data {
59			let encoding = hex::encode(data.as_ref());
60			let output = format!("{HEX_ENCODING_PREFIX}{encoding}");
61			serializer.collect_str(&output)
62		} else {
63			serializer.collect_str(&"")
64		}
65	}
66
67	/// Deserialize hex string into Option<Vec<u8>>
68	pub fn deserialize_option<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
69	where
70		D: serde::Deserializer<'de>,
71		T: TryFrom<Vec<u8>>,
72	{
73		let s = <String>::deserialize(deserializer)?;
74
75		let data = try_bytes_from_hex_str(&s).map_err(serde::de::Error::custom)?;
76		if data.is_empty() {
77			Ok(None)
78		} else {
79			let inner = T::try_from(data).map_err(|_| {
80				serde::de::Error::custom("type failed to parse bytes from hex data")
81			})?;
82			Ok(Some(inner))
83		}
84	}
85
86	/// Deserialize hex string into Vec<u8>
87	pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
88	where
89		D: serde::Deserializer<'de>,
90		T: TryFrom<Vec<u8>>,
91	{
92		let s = <String>::deserialize(deserializer)?;
93
94		let data = try_bytes_from_hex_str(&s).map_err(serde::de::Error::custom)?;
95
96		let inner = T::try_from(data)
97			.map_err(|_| serde::de::Error::custom("type failed to parse bytes from hex data"))?;
98		Ok(inner)
99	}
100}
101
102/// Hex serializer and Deserializer for utf8 bytes
103pub mod as_utf8_string {
104	use super::*;
105	use alloc::string::String;
106	use serde::de::Deserialize;
107
108	/// Serialize [u8;4] into a utf8 string
109	pub fn serialize<S, T: AsRef<[u8]>>(data: T, serializer: S) -> Result<S::Ok, S::Error>
110	where
111		S: serde::Serializer,
112	{
113		let output =
114			String::from_utf8(data.as_ref().to_vec()).map_err(serde::ser::Error::custom)?;
115		serializer.collect_str(&output)
116	}
117
118	/// Deserialize a string into utf8 bytes
119	pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
120	where
121		D: serde::Deserializer<'de>,
122		T: From<[u8; 4]>,
123	{
124		let s = <String>::deserialize(deserializer)?;
125
126		let mut bytes = [0u8; 4];
127		bytes.copy_from_slice(s.as_bytes());
128		Ok(bytes.into())
129	}
130}
131
132/// Hex serializer and Deserializer for Vec<Vec<u8>>
133pub mod seq_of_hex {
134	use super::*;
135	use core::{fmt, marker::PhantomData};
136	use serde::{de::Deserializer, ser::SerializeSeq};
137
138	/// Serialize Vec<Vec<u8>> into an array of hex string
139	pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
140	where
141		S: serde::Serializer,
142		T: AsRef<[Vec<u8>]>,
143	{
144		let mut seq = serializer.serialize_seq(None)?;
145		for elem in data.as_ref().iter() {
146			let encoding = hex::encode(elem);
147			let output = format!("{HEX_ENCODING_PREFIX}{encoding}");
148			seq.serialize_element(&output)?;
149		}
150		seq.end()
151	}
152
153	struct Visitor(PhantomData<Vec<Vec<u8>>>);
154
155	impl<'de> serde::de::Visitor<'de> for Visitor {
156		type Value = Vec<Vec<u8>>;
157
158		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
159			formatter.write_str("sequence of string")
160		}
161
162		fn visit_seq<S>(self, mut access: S) -> Result<Self::Value, S::Error>
163		where
164			S: serde::de::SeqAccess<'de>,
165		{
166			let mut coll = Vec::with_capacity(access.size_hint().unwrap_or(0));
167
168			while let Some(elem) = access.next_element()? {
169				let recovered_elem =
170					try_bytes_from_hex_str(elem).map_err(serde::de::Error::custom)?;
171				coll.push(recovered_elem);
172			}
173			Ok(coll)
174		}
175	}
176
177	/// Deserialize for an array of hex strings into Vec<Vec<u8>>
178	pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Vec<u8>>, D::Error>
179	where
180		D: Deserializer<'de>,
181	{
182		let data = deserializer.deserialize_seq(Visitor(PhantomData))?;
183		Ok(data)
184	}
185}
186
187/// String serializer and deserializer
188pub mod as_string {
189	use alloc::{
190		format,
191		string::{String, ToString},
192	};
193	use core::{fmt, marker::PhantomData, str::FromStr};
194	use serde::de::Error;
195
196	/// Serde Visitor for deserializing sequence of strings or hex string into sequence of bytes
197	struct AnyVisitor<T>(PhantomData<T>);
198
199	impl<'de, T: FromStr> serde::de::Visitor<'de> for AnyVisitor<T> {
200		type Value = T;
201
202		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
203			formatter.write_str("string or integer")
204		}
205
206		fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
207		where
208			E: Error,
209		{
210			let inner: T = v
211				.parse()
212				.map_err(|_| serde::de::Error::custom("failure to parse string data"))?;
213			Ok(inner)
214		}
215
216		fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
217		where
218			E: Error,
219		{
220			let inner: T = v
221				.parse()
222				.map_err(|_| serde::de::Error::custom("failure to parse string data"))?;
223			Ok(inner)
224		}
225
226		fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
227		where
228			E: Error,
229		{
230			let inner: T = v
231				.to_string()
232				.parse()
233				.map_err(|_| serde::de::Error::custom("failure to parse string data"))?;
234			Ok(inner)
235		}
236	}
237
238	/// Serialize into a string
239	pub fn serialize<S, T: fmt::Display>(data: T, serializer: S) -> Result<S::Ok, S::Error>
240	where
241		S: serde::Serializer,
242	{
243		let output = format!("{data}");
244		serializer.collect_str(&output)
245	}
246
247	/// Deserialize from string
248	pub fn deserialize<'de, D, T: FromStr>(deserializer: D) -> Result<T, D::Error>
249	where
250		D: serde::Deserializer<'de>,
251	{
252		let data = deserializer.deserialize_any(AnyVisitor(PhantomData::<T>))?;
253		T::try_from(data).map_err(|_| serde::de::Error::custom("failure to parse string"))
254	}
255}
256
257/// Serializing a sequence of any generic types into a sequence of strings
258pub mod seq_of_str {
259	use super::*;
260	use core::{fmt, marker::PhantomData, str::FromStr};
261	use serde::{
262		de::{Deserializer, Error},
263		ser::SerializeSeq,
264	};
265
266	/// Serialize generic type into a sequence of strings
267	pub fn serialize<S, T, U>(data: T, serializer: S) -> Result<S::Ok, S::Error>
268	where
269		S: serde::Serializer,
270		T: AsRef<[U]>,
271		U: fmt::Display,
272	{
273		let mut seq = serializer.serialize_seq(None)?;
274		for elem in data.as_ref().iter() {
275			let rendered_elem = format!("{elem}");
276			seq.serialize_element(&rendered_elem)?;
277		}
278		seq.end()
279	}
280
281	/// Serde Visitor for deserializing sequence of strings
282	struct Visitor<T>(PhantomData<Vec<T>>);
283
284	impl<'de, T: FromStr> serde::de::Visitor<'de> for Visitor<T> {
285		type Value = Vec<T>;
286
287		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
288			formatter.write_str("sequence of string")
289		}
290
291		fn visit_seq<S>(self, mut access: S) -> Result<Self::Value, S::Error>
292		where
293			S: serde::de::SeqAccess<'de>,
294		{
295			let mut coll = Vec::with_capacity(access.size_hint().unwrap_or(0));
296
297			while let Some(elem) = access.next_element()? {
298				let recovered_elem = T::from_str(elem).map_err(|_| {
299					Error::custom("failure to parse element of sequence from string")
300				})?;
301				coll.push(recovered_elem);
302			}
303			Ok(coll)
304		}
305	}
306
307	/// Deserialize generic type from a sequence of strings
308	pub fn deserialize<'de, D, T, U>(deserializer: D) -> Result<T, D::Error>
309	where
310		D: Deserializer<'de>,
311		T: TryFrom<Vec<U>>,
312		U: FromStr,
313	{
314		let data = deserializer.deserialize_seq(Visitor(PhantomData))?;
315		T::try_from(data).map_err(|_| serde::de::Error::custom("failure to parse collection"))
316	}
317}
318
319/// Deserializer needed to fix edge case with deserializing current_epoch_participation and
320/// next_epoch_participation Erigon's beacon state
321pub mod seq_of_u8_str_or_hex {
322	use super::*;
323	use alloc::string::String;
324	use core::{fmt, marker::PhantomData, str::FromStr};
325	use serde::de::{Deserializer, Error};
326
327	pub use seq_of_str::serialize;
328
329	/// Serde Visitor for deserializing sequence of strings or hex string into sequence of bytes
330	struct AnyVisitor(PhantomData<Vec<u8>>);
331
332	impl<'de> serde::de::Visitor<'de> for AnyVisitor {
333		type Value = Vec<u8>;
334
335		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
336			formatter.write_str("sequence of string or hex string")
337		}
338
339		fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
340		where
341			E: Error,
342		{
343			let data = try_bytes_from_hex_str(v).map_err(serde::de::Error::custom)?;
344			Ok(data)
345		}
346
347		fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
348		where
349			E: Error,
350		{
351			let data = try_bytes_from_hex_str(&v).map_err(serde::de::Error::custom)?;
352			Ok(data)
353		}
354
355		fn visit_seq<S>(self, mut access: S) -> Result<Self::Value, S::Error>
356		where
357			S: serde::de::SeqAccess<'de>,
358		{
359			let mut coll = Vec::with_capacity(access.size_hint().unwrap_or(0));
360
361			while let Some(elem) = access.next_element()? {
362				let recovered_elem = <u8>::from_str(elem).map_err(|_| {
363					Error::custom("failure to parse element of sequence from string")
364				})?;
365				coll.push(recovered_elem);
366			}
367			Ok(coll)
368		}
369	}
370
371	/// Deserialize generic type from a sequence of strings or
372	pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
373	where
374		D: Deserializer<'de>,
375		T: TryFrom<Vec<u8>>,
376	{
377		let data = deserializer.deserialize_any(AnyVisitor(PhantomData))?;
378		T::try_from(data).map_err(|_| serde::de::Error::custom("failure to parse collection"))
379	}
380}
381
382#[cfg(test)]
383mod test {
384	use super::{as_string, seq_of_u8_str_or_hex};
385	use ismp::router::{GetRequest, GetResponse, PostRequest, StorageValue};
386	use primitive_types::{H256, H512};
387	use serde::Deserialize;
388
389	#[test]
390	fn should_deserialize_from_hex_string_and_sequence_of_strings() {
391		#[derive(Deserialize, Debug, PartialEq, Eq)]
392		struct TestData {
393			#[serde(with = "seq_of_u8_str_or_hex")]
394			data: Vec<u8>,
395			#[serde(with = "as_string")]
396			value: u64,
397		}
398
399		let json_value_1 = r#"{
400			"data":"0x00050708",
401			"value":29716
402			}"#;
403		let json_value_2 = r#"{
404			"data":["0", "5", "7", "8"],
405			"value": "29716"
406			}"#;
407
408		let deserialized_1 = serde_json::from_str::<TestData>(json_value_1);
409		let deserialized_2 = serde_json::from_str::<TestData>(json_value_2);
410		println!("{deserialized_1:?}");
411		println!("{deserialized_2:?}");
412
413		assert!(deserialized_1.is_ok());
414		assert!(deserialized_2.is_ok());
415
416		assert!(deserialized_1.unwrap() == deserialized_2.unwrap());
417	}
418
419	#[test]
420	fn serialize_and_deserialize_post_request() {
421		let post = PostRequest {
422			source: ismp::host::StateMachine::Polkadot(100),
423			dest: ismp::host::StateMachine::Polkadot(2000),
424			nonce: 300,
425			from: H256::random().0.to_vec(),
426			to: H256::random().0.to_vec(),
427			timeout_timestamp: 0,
428			body: H512::random().0.to_vec(),
429		};
430
431		let serialized = serde_json::to_string(&post).unwrap();
432
433		println!("{serialized:?}\n");
434
435		let deserialized: PostRequest = serde_json::from_str(&serialized).unwrap();
436
437		assert_eq!(post, deserialized);
438	}
439
440	#[test]
441	fn serialize_and_deserialize_get_request() {
442		let get = GetRequest {
443			source: ismp::host::StateMachine::Polkadot(100),
444			dest: ismp::host::StateMachine::Polkadot(2000),
445			nonce: 300,
446			context: Default::default(),
447			from: H256::random().0.to_vec(),
448			keys: vec![
449				H256::random().0.to_vec(),
450				H256::random().0.to_vec(),
451				H256::random().0.to_vec(),
452			],
453			timeout_timestamp: 40000,
454			height: 289900,
455		};
456
457		let serialized = serde_json::to_string(&get).unwrap();
458
459		println!("{serialized:?}\n");
460
461		let deserialized: GetRequest = serde_json::from_str(&serialized).unwrap();
462
463		assert_eq!(get, deserialized);
464	}
465
466	#[test]
467	fn serialize_and_deserialize_get_response() {
468		let get = GetRequest {
469			source: ismp::host::StateMachine::Polkadot(100),
470			dest: ismp::host::StateMachine::Polkadot(2000),
471			nonce: 300,
472			context: Default::default(),
473
474			from: H256::random().0.to_vec(),
475			keys: vec![
476				H256::random().0.to_vec(),
477				H256::random().0.to_vec(),
478				H256::random().0.to_vec(),
479			],
480			timeout_timestamp: 40000,
481			height: 289900,
482		};
483
484		let response = GetResponse {
485			get,
486			values: vec![
487				StorageValue {
488					key: H256::random().0.to_vec(),
489					value: Some(H256::random().0.to_vec()),
490				},
491				StorageValue { key: H256::random().0.to_vec(), value: None },
492				StorageValue {
493					key: H256::random().0.to_vec(),
494					value: Some(H256::random().0.to_vec()),
495				},
496			],
497		};
498
499		let serialized = serde_json::to_string(&response).unwrap();
500
501		println!("{serialized:?}\n");
502
503		let deserialized: GetResponse = serde_json::from_str(&serialized).unwrap();
504
505		assert_eq!(response, deserialized);
506	}
507
508	#[test]
509	fn serialize_state_machine_id() {
510		use ismp::{consensus::StateMachineId, host::StateMachine};
511		let state_machine_updated =
512			StateMachineId { state_id: StateMachine::Evm(11155111), consensus_state_id: *b"ETH0" };
513		let serialized = serde_json::to_string(&state_machine_updated).unwrap();
514
515		println!("{serialized:?}\n");
516
517		let deserialized: StateMachineId = serde_json::from_str(&serialized).unwrap();
518
519		assert_eq!(state_machine_updated, deserialized);
520	}
521}