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::{format, string::String};
190	use core::{fmt, str::FromStr};
191	use serde::de::Deserialize;
192
193	/// Serialize into a string
194	pub fn serialize<S, T: fmt::Display>(data: T, serializer: S) -> Result<S::Ok, S::Error>
195	where
196		S: serde::Serializer,
197	{
198		let output = format!("{data}");
199		serializer.collect_str(&output)
200	}
201
202	/// Deserialize from string
203	pub fn deserialize<'de, D, T: FromStr>(deserializer: D) -> Result<T, D::Error>
204	where
205		D: serde::Deserializer<'de>,
206	{
207		let s: String = <String>::deserialize(deserializer)?;
208		let inner: T = s
209			.parse()
210			.map_err(|_| serde::de::Error::custom("failure to parse string data"))?;
211		Ok(inner)
212	}
213}
214
215/// Serializing a sequence of any generic types into a sequence of strings
216pub mod seq_of_str {
217	use super::*;
218	use core::{fmt, marker::PhantomData, str::FromStr};
219	use serde::{
220		de::{Deserializer, Error},
221		ser::SerializeSeq,
222	};
223
224	/// Serialize generic type into a sequence of strings
225	pub fn serialize<S, T, U>(data: T, serializer: S) -> Result<S::Ok, S::Error>
226	where
227		S: serde::Serializer,
228		T: AsRef<[U]>,
229		U: fmt::Display,
230	{
231		let mut seq = serializer.serialize_seq(None)?;
232		for elem in data.as_ref().iter() {
233			let rendered_elem = format!("{elem}");
234			seq.serialize_element(&rendered_elem)?;
235		}
236		seq.end()
237	}
238
239	struct Visitor<T>(PhantomData<Vec<T>>);
240
241	impl<'de, T: FromStr> serde::de::Visitor<'de> for Visitor<T> {
242		type Value = Vec<T>;
243
244		fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
245			formatter.write_str("sequence of string")
246		}
247
248		fn visit_seq<S>(self, mut access: S) -> Result<Self::Value, S::Error>
249		where
250			S: serde::de::SeqAccess<'de>,
251		{
252			let mut coll = Vec::with_capacity(access.size_hint().unwrap_or(0));
253
254			while let Some(elem) = access.next_element()? {
255				let recovered_elem = T::from_str(elem).map_err(|_| {
256					Error::custom("failure to parse element of sequence from string")
257				})?;
258				coll.push(recovered_elem);
259			}
260			Ok(coll)
261		}
262	}
263
264	/// Deserialize generic type from a sequence of strings
265	pub fn deserialize<'de, D, T, U>(deserializer: D) -> Result<T, D::Error>
266	where
267		D: Deserializer<'de>,
268		T: TryFrom<Vec<U>>,
269		U: FromStr,
270	{
271		let data = deserializer.deserialize_seq(Visitor(PhantomData))?;
272		T::try_from(data).map_err(|_| serde::de::Error::custom("failure to parse collection"))
273	}
274}
275
276#[cfg(test)]
277mod test {
278	use primitive_types::{H256, H512};
279
280	use ismp::router::{GetRequest, GetResponse, PostRequest, PostResponse, StorageValue};
281
282	#[test]
283	fn serialize_and_deserialize_post_request() {
284		let post = PostRequest {
285			source: ismp::host::StateMachine::Polkadot(100),
286			dest: ismp::host::StateMachine::Polkadot(2000),
287			nonce: 300,
288			from: H256::random().0.to_vec(),
289			to: H256::random().0.to_vec(),
290			timeout_timestamp: 0,
291			body: H512::random().0.to_vec(),
292		};
293
294		let serialized = serde_json::to_string(&post).unwrap();
295
296		println!("{serialized:?}\n");
297
298		let deserialized: PostRequest = serde_json::from_str(&serialized).unwrap();
299
300		assert_eq!(post, deserialized);
301	}
302
303	#[test]
304	fn serialize_and_deserialize_post_response() {
305		let post = PostRequest {
306			source: ismp::host::StateMachine::Polkadot(100),
307			dest: ismp::host::StateMachine::Polkadot(2000),
308			nonce: 300,
309			from: H256::random().0.to_vec(),
310			to: H256::random().0.to_vec(),
311			timeout_timestamp: 0,
312			body: H512::random().0.to_vec(),
313		};
314
315		let response =
316			PostResponse { post, response: H512::random().0.to_vec(), timeout_timestamp: 30000 };
317
318		let serialized = serde_json::to_string(&response).unwrap();
319
320		println!("{serialized:?}\n");
321
322		let deserialized: PostResponse = serde_json::from_str(&serialized).unwrap();
323
324		assert_eq!(response, deserialized);
325	}
326
327	#[test]
328	fn serialize_and_deserialize_get_request() {
329		let get = GetRequest {
330			source: ismp::host::StateMachine::Polkadot(100),
331			dest: ismp::host::StateMachine::Polkadot(2000),
332			nonce: 300,
333			context: Default::default(),
334			from: H256::random().0.to_vec(),
335			keys: vec![
336				H256::random().0.to_vec(),
337				H256::random().0.to_vec(),
338				H256::random().0.to_vec(),
339			],
340			timeout_timestamp: 40000,
341			height: 289900,
342		};
343
344		let serialized = serde_json::to_string(&get).unwrap();
345
346		println!("{serialized:?}\n");
347
348		let deserialized: GetRequest = serde_json::from_str(&serialized).unwrap();
349
350		assert_eq!(get, deserialized);
351	}
352
353	#[test]
354	fn serialize_and_deserialize_get_response() {
355		let get = GetRequest {
356			source: ismp::host::StateMachine::Polkadot(100),
357			dest: ismp::host::StateMachine::Polkadot(2000),
358			nonce: 300,
359			context: Default::default(),
360
361			from: H256::random().0.to_vec(),
362			keys: vec![
363				H256::random().0.to_vec(),
364				H256::random().0.to_vec(),
365				H256::random().0.to_vec(),
366			],
367			timeout_timestamp: 40000,
368			height: 289900,
369		};
370
371		let response = GetResponse {
372			get,
373			values: vec![
374				StorageValue {
375					key: H256::random().0.to_vec(),
376					value: Some(H256::random().0.to_vec()),
377				},
378				StorageValue { key: H256::random().0.to_vec(), value: None },
379				StorageValue {
380					key: H256::random().0.to_vec(),
381					value: Some(H256::random().0.to_vec()),
382				},
383			],
384		};
385
386		let serialized = serde_json::to_string(&response).unwrap();
387
388		println!("{serialized:?}\n");
389
390		let deserialized: GetResponse = serde_json::from_str(&serialized).unwrap();
391
392		assert_eq!(response, deserialized);
393	}
394
395	#[test]
396	fn serialize_state_machine_id() {
397		use ismp::{consensus::StateMachineId, host::StateMachine};
398		let state_machine_updated =
399			StateMachineId { state_id: StateMachine::Evm(11155111), consensus_state_id: *b"ETH0" };
400		let serialized = serde_json::to_string(&state_machine_updated).unwrap();
401
402		println!("{serialized:?}\n");
403
404		let deserialized: StateMachineId = serde_json::from_str(&serialized).unwrap();
405
406		assert_eq!(state_machine_updated, deserialized);
407	}
408}