evm_coder/
custom_signature.rs

1//! # A module for custom signature support.
2//!
3//! ## Overview
4//! This module allows you to create arbitrary signatures for types and functions in compile time.
5//!
6//! ### Type signatures
7//! To create the desired type signature, you need to create your own trait with the `SIGNATURE` constant.
8//! Then in the implementation, for the required type, use the macro [`make_signature`](crate::make_signature)
9//! #### Example
10//! ```
11//! use std::str::from_utf8;
12//! use evm_coder::{custom_signature::SignatureUnit, make_signature};
13//!
14//! // Create trait for our signature
15//! trait SoliditySignature {
16//!     const SIGNATURE: SignatureUnit;
17//!
18//!     fn name() -> &'static str {
19//!         from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8")
20//!     }
21//! }
22//!
23//! // Make signatures for some types
24//! impl SoliditySignature for u8 {
25//!     const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8"));
26//! }
27//! impl SoliditySignature for u32 {
28//!     const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint32"));
29//! }
30//! impl<T: SoliditySignature> SoliditySignature for Vec<T> {
31//!     const SIGNATURE: SignatureUnit = make_signature!(new nameof(T::SIGNATURE) fixed("[]"));
32//! }
33//! impl<A: SoliditySignature, B: SoliditySignature> SoliditySignature for (A, B) {
34//!     const SIGNATURE: SignatureUnit = make_signature!(new fixed("(") nameof(A::SIGNATURE) fixed(",") nameof(B::SIGNATURE) fixed(")"));
35//! }
36//! impl<A: SoliditySignature> SoliditySignature for (A,) {
37//!     const SIGNATURE: SignatureUnit = make_signature!(new fixed("(") nameof(A::SIGNATURE) fixed(",") shift_left(1) fixed(")"));
38//! }
39//!
40//! assert_eq!(u8::name(), "uint8");
41//! assert_eq!(<Vec<u8>>::name(), "uint8[]");
42//! assert_eq!(<(u32, u8)>::name(), "(uint32,uint8)");
43//! ```
44//!
45//! #### Example
46//! ```
47//! use core::str::from_utf8;
48//! use evm_coder::{custom_signature::SignatureUnit, make_signature};
49//! // Trait for our signature
50//! trait SoliditySignature {
51//!     const SIGNATURE: SignatureUnit;
52//!
53//!     fn name() -> &'static str {
54//!         from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8")
55//!     }
56//! }
57//!
58//! // Make signatures for some types
59//! impl SoliditySignature for u8 {
60//!     const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8"));
61//! }
62//! impl<T: SoliditySignature> SoliditySignature for Vec<T> {
63//!     const SIGNATURE: SignatureUnit = make_signature!(new nameof(T::SIGNATURE) fixed("[]"));
64//! }
65//! ```
66
67/// The maximum length of the signature.
68pub const SIGNATURE_SIZE_LIMIT: usize = 256;
69
70/// Storage for the signature or its elements.
71#[derive(Debug)]
72pub struct SignatureUnit {
73	/// Signature data.
74	pub data: [u8; SIGNATURE_SIZE_LIMIT],
75	/// The actual size of the data.
76	pub len: usize,
77}
78
79impl SignatureUnit {
80	/// Create a signature from `&str`.
81	#[must_use]
82	pub const fn new(name: &'static str) -> SignatureUnit {
83		let mut signature = [0_u8; SIGNATURE_SIZE_LIMIT];
84		let name = name.as_bytes();
85		let name_len = name.len();
86		let mut dst_offset = 0;
87		crate::make_signature!(@copy(name, signature, name_len, dst_offset));
88		SignatureUnit {
89			data: signature,
90			len: name_len,
91		}
92	}
93	/// String conversion
94	#[must_use]
95	pub fn as_str(&self) -> Option<&str> {
96		core::str::from_utf8(&self.data[0..self.len]).ok()
97	}
98}
99
100/// ### Macro to create signatures of types and functions.
101///
102/// Format for creating a type of signature:
103/// ```ignore
104/// make_signature!(new fixed("uint8")); // Simple type
105/// make_signature!(new fixed("(") nameof(u8) fixed(",") nameof(u8) fixed(")")); // Composite type
106/// ```
107#[macro_export]
108macro_rules! make_signature {
109	(new $($tt:tt)*) => {
110		($crate::custom_signature::SignatureUnit {
111			data: {
112				let mut out = [0u8; $crate::custom_signature::SIGNATURE_SIZE_LIMIT];
113				let mut dst_offset = 0;
114				$crate::make_signature!(@data(out, dst_offset); $($tt)*);
115				out
116			},
117			len: {0 + $crate::make_signature!(@size; $($tt)*)},
118		})
119	};
120
121	(@size;) => {
122		0
123	};
124	(@size; fixed($expr:expr) $($tt:tt)*) => {
125		$expr.len() + $crate::make_signature!(@size; $($tt)*)
126	};
127	(@size; nameof($expr:expr) $($tt:tt)*) => {
128		$expr.len + $crate::make_signature!(@size; $($tt)*)
129	};
130	(@size; numof($expr:expr) $($tt:tt)*) => {
131		{
132			let mut out = 0;
133			let mut v = $expr;
134			if v == 0 {
135				out = 1;
136			} else {
137				while v > 0 {
138					out += 1;
139					v /= 10;
140				}
141			}
142			out
143		} + $crate::make_signature!(@size; $($tt)*)
144	};
145	(@size; shift_left($expr:expr) $($tt:tt)*) => {
146		$crate::make_signature!(@size; $($tt)*) - $expr
147	};
148
149	(@data($dst:ident, $dst_offset:ident);) => {};
150	(@data($dst:ident, $dst_offset:ident); fixed($expr:expr) $($tt:tt)*) => {
151		{
152			let data = $expr.as_bytes();
153			let data_len = data.len();
154			$crate::make_signature!(@copy(data, $dst, data_len, $dst_offset));
155		}
156		$crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
157	};
158	(@data($dst:ident, $dst_offset:ident); nameof($expr:expr) $($tt:tt)*) => {
159		{
160			$crate::make_signature!(@copy(&$expr.data, $dst, $expr.len, $dst_offset));
161		}
162		$crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
163	};
164	(@data($dst:ident, $dst_offset:ident); numof($expr:expr) $($tt:tt)*) => {
165		{
166			let mut v = $expr;
167			let mut need_to_swap = 0;
168			if v == 0 {
169				$dst[$dst_offset] = b'0';
170				$dst_offset += 1;
171			} else {
172				while v > 0 {
173					let n = (v % 10) as u8;
174					$dst[$dst_offset] = b'0' + n;
175					v /= 10;
176					need_to_swap += 1;
177					$dst_offset += 1;
178				}
179			}
180			let mut i = 0;
181			#[allow(clippy::manual_swap)]
182			while i < need_to_swap / 2 {
183				let a = $dst_offset - i - 1;
184				let b = $dst_offset - need_to_swap + i;
185				let v = $dst[a];
186				$dst[a] = $dst[b];
187				$dst[b] = v;
188				i += 1;
189			}
190		}
191		$crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
192	};
193	(@data($dst:ident, $dst_offset:ident); shift_left($expr:expr) $($tt:tt)*) => {
194		$dst_offset -= $expr;
195		$crate::make_signature!(@data($dst, $dst_offset); $($tt)*)
196	};
197
198	(@copy($src:expr, $dst:expr, $src_len:expr, $dst_offset:ident)) => {
199		{
200			let mut src_offset = 0;
201			let src_len: usize = $src_len;
202			while src_offset < src_len {
203				$dst[$dst_offset] = $src[src_offset];
204				$dst_offset += 1;
205				src_offset += 1;
206			}
207		}
208	}
209}
210
211#[cfg(test)]
212mod tests {
213	use core::str::from_utf8;
214
215	use super::{SignatureUnit, SIGNATURE_SIZE_LIMIT};
216
217	trait Name {
218		const NAME: SignatureUnit;
219
220		fn name() -> &'static str {
221			from_utf8(&Self::NAME.data[..Self::NAME.len]).expect("bad utf-8")
222		}
223	}
224
225	impl Name for u8 {
226		const NAME: SignatureUnit = make_signature!(new fixed("uint8"));
227	}
228	impl Name for u32 {
229		const NAME: SignatureUnit = make_signature!(new fixed("uint32"));
230	}
231	impl<T: Name> Name for Vec<T> {
232		const NAME: SignatureUnit = make_signature!(new nameof(T::NAME) fixed("[]"));
233	}
234	impl<A: Name, B: Name> Name for (A, B) {
235		const NAME: SignatureUnit =
236			make_signature!(new fixed("(") nameof(A::NAME) fixed(",") nameof(B::NAME) fixed(")"));
237	}
238	impl<A: Name> Name for (A,) {
239		const NAME: SignatureUnit =
240			make_signature!(new fixed("(") nameof(A::NAME) fixed(",") shift_left(1) fixed(")"));
241	}
242	impl<A: Name, const SIZE: usize> Name for [A; SIZE] {
243		const NAME: SignatureUnit =
244			make_signature!(new nameof(A::NAME) fixed("[") numof(SIZE) fixed("]"));
245	}
246
247	struct MaxSize();
248	impl Name for MaxSize {
249		const NAME: SignatureUnit = SignatureUnit {
250			data: [b'!'; SIGNATURE_SIZE_LIMIT],
251			len: SIGNATURE_SIZE_LIMIT,
252		};
253	}
254
255	#[test]
256	fn simple() {
257		assert_eq!(u8::name(), "uint8");
258		assert_eq!(u32::name(), "uint32");
259	}
260
261	#[test]
262	fn vector_of_simple() {
263		assert_eq!(<Vec<u8>>::name(), "uint8[]");
264		assert_eq!(<Vec<u32>>::name(), "uint32[]");
265	}
266
267	#[test]
268	fn vector_of_vector() {
269		assert_eq!(<Vec<Vec<u8>>>::name(), "uint8[][]");
270	}
271
272	#[test]
273	fn tuple_of_simple() {
274		assert_eq!(<(u32, u8)>::name(), "(uint32,uint8)");
275	}
276
277	#[test]
278	fn tuple_of_tuple() {
279		assert_eq!(
280			<((u32, u8), (u8, u32))>::name(),
281			"((uint32,uint8),(uint8,uint32))"
282		);
283	}
284
285	#[test]
286	fn vector_of_tuple() {
287		assert_eq!(<Vec<(u32, u8)>>::name(), "(uint32,uint8)[]");
288	}
289
290	#[test]
291	fn tuple_of_vector() {
292		assert_eq!(<(Vec<u32>, u8)>::name(), "(uint32[],uint8)");
293	}
294
295	#[test]
296	fn complex() {
297		assert_eq!(
298			<(Vec<u32>, (u32, Vec<u8>))>::name(),
299			"(uint32[],(uint32,uint8[]))"
300		);
301	}
302
303	#[test]
304	fn max_size() {
305		assert_eq!(<MaxSize>::name(), "!".repeat(SIGNATURE_SIZE_LIMIT));
306	}
307
308	#[test]
309	fn shift() {
310		assert_eq!(<(u32,)>::name(), "(uint32)");
311	}
312
313	#[test]
314	fn num() {
315		assert_eq!(<[u8; 0]>::name(), "uint8[0]");
316		assert_eq!(<[u8; 1234]>::name(), "uint8[1234]");
317		assert_eq!(<[u8; 12345]>::name(), "uint8[12345]");
318	}
319
320	#[test]
321	#[ignore = "fails in ci due to rust version"]
322	fn over_max_size() {
323		let t = trybuild::TestCases::new();
324		t.compile_fail("tests/build_failed/custom_signature_over_max_size.rs");
325	}
326}