Skip to main content

qp_poseidon/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4
5use alloc::vec::Vec;
6
7use codec::{Decode, Encode};
8use core::{
9	clone::Clone,
10	cmp::{Eq, PartialEq},
11	convert::TryInto,
12	default::Default,
13	fmt::Debug,
14	iter::Extend,
15	prelude::rust_2024::derive,
16};
17
18use p3_field::PrimeCharacteristicRing;
19use p3_goldilocks::Goldilocks;
20use qp_poseidon_core::{
21	hash_for_circuit, hash_to_bytes,
22	serialization::{bytes_to_digest, u128_to_quantized_felt, u64_to_felts},
23	PROOF_NODE_MAX_SIZE_FELTS,
24};
25use scale_info::TypeInfo;
26use sp_core::{Hasher, H256};
27use sp_storage::StateVersion;
28use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration};
29
30/// Converts types to Goldilocks field elements for Poseidon hashing.
31///
32/// Implementations must align with the circuit's expected layout for the given type.
33/// All byte array types (digests, accounts) use 4 bytes per field element for
34/// collision-resistant encoding.
35pub trait ToFelts {
36	/// Append felts to the buffer to minimize allocations
37	fn write_felts(&self, dest: &mut Vec<Goldilocks>);
38
39	/// Convenience method to convert to a vector of felts
40	fn to_felts(&self) -> Vec<Goldilocks> {
41		let mut vec = Vec::new();
42		self.write_felts(&mut vec);
43		vec
44	}
45}
46
47// Specific implementations for primitives and types used in the system.
48// We use specific implementations instead of a blanket `MaxEncodedLen` impl to allow
49// for structural composition of tuples via macros, which is required for correct circuit alignment.
50
51impl ToFelts for u32 {
52	fn write_felts(&self, dest: &mut Vec<Goldilocks>) {
53		dest.push(Goldilocks::from_u32(*self));
54	}
55}
56
57impl ToFelts for u64 {
58	fn write_felts(&self, dest: &mut Vec<Goldilocks>) {
59		dest.extend(u64_to_felts(*self));
60	}
61}
62
63/// Here we quantize the u128 balance type to a u64 (constrained to 32-bit range) and then to a
64/// single felt.
65impl ToFelts for u128 {
66	fn write_felts(&self, dest: &mut Vec<Goldilocks>) {
67		dest.push(u128_to_quantized_felt(*self));
68	}
69}
70
71/// 32-byte arrays are encoded as 4 field elements (8 bytes per felt).
72/// This encoding is used for hash outputs and account IDs in storage.
73/// The values in the leaf are constrained by the storage proof, so
74/// collision resistance is provided by the merkle proof verification.
75impl ToFelts for [u8; 32] {
76	fn write_felts(&self, dest: &mut Vec<Goldilocks>) {
77		dest.extend(bytes_to_digest::<Goldilocks>(self));
78	}
79}
80
81/// Account IDs are encoded as 4 field elements (8 bytes per felt).
82/// The values are constrained by the storage proof, so collision resistance
83/// is provided by the merkle proof verification rather than the encoding.
84impl ToFelts for sp_core::crypto::AccountId32 {
85	fn write_felts(&self, dest: &mut Vec<Goldilocks>) {
86		let bytes: &[u8; 32] = self.as_ref();
87		dest.extend(bytes_to_digest::<Goldilocks>(bytes));
88	}
89}
90
91impl<T: ToFelts> ToFelts for Option<T> {
92	fn write_felts(&self, dest: &mut Vec<Goldilocks>) {
93		match self {
94			Some(v) => {
95				dest.push(Goldilocks::ONE);
96				v.write_felts(dest);
97			},
98			None => {
99				dest.push(Goldilocks::ZERO);
100			},
101		}
102	}
103}
104
105impl<T: ToFelts> ToFelts for Vec<T> {
106	fn write_felts(&self, dest: &mut Vec<Goldilocks>) {
107		// Length prefix + items
108		dest.push(Goldilocks::from_u32(self.len() as u32));
109		for item in self {
110			item.write_felts(dest);
111		}
112	}
113}
114
115// Macro to implement ToFelts for tuples
116macro_rules! impl_to_felts_tuple {
117	($($name:ident)+) => {
118		impl<$($name: ToFelts),+> ToFelts for ($($name,)+) {
119			fn write_felts(&self, dest: &mut Vec<Goldilocks>) {
120				#[allow(non_snake_case)]
121				let ($($name,)+) = self;
122				$($name.write_felts(dest);)+
123			}
124		}
125	}
126}
127
128impl_to_felts_tuple!(A);
129impl_to_felts_tuple!(A B);
130impl_to_felts_tuple!(A B C);
131impl_to_felts_tuple!(A B C D);
132impl_to_felts_tuple!(A B C D E);
133impl_to_felts_tuple!(A B C D E F);
134
135#[cfg(feature = "serde")]
136use serde::{Deserialize, Serialize};
137
138/// A standard library hasher implementation using Poseidon
139#[derive(Default)]
140pub struct PoseidonStdHasher(Vec<u8>);
141
142impl core::hash::Hasher for PoseidonStdHasher {
143	fn finish(&self) -> u64 {
144		let hash = PoseidonHasher::hash_for_circuit(self.0.as_slice());
145		u64::from_le_bytes(hash[0..8].try_into().unwrap())
146	}
147
148	fn write(&mut self, bytes: &[u8]) {
149		self.0.extend_from_slice(bytes)
150	}
151}
152
153/// Substrate-compatible Poseidon hasher with codec traits
154#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode, TypeInfo)]
155#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
156pub struct PoseidonHasher;
157
158impl Hasher for PoseidonHasher {
159	type Out = H256;
160	type StdHasher = PoseidonStdHasher;
161	const LENGTH: usize = 32;
162
163	fn hash(x: &[u8]) -> H256 {
164		H256::from_slice(&Self::hash_for_circuit(x))
165	}
166}
167
168impl PoseidonHasher {
169	/// Hash bytes for circuit compatibility (used by Substrate's Hasher trait).
170	///
171	/// Converts bytes to field elements (4 bytes per felt with terminator),
172	/// pads to a fixed number of elements, then hashes.
173	pub fn hash_for_circuit(x: &[u8]) -> [u8; 32] {
174		hash_for_circuit::<PROOF_NODE_MAX_SIZE_FELTS>(x)
175	}
176
177	/// Hash storage key or value.
178	///
179	/// Decodes the input bytes into `T` and converts to felts according to `ToFelts`.
180	/// This ensures the hash matches the circuit's expected preimage for type `T`.
181	pub fn hash_storage<T: Decode + ToFelts>(x: &[u8]) -> [u8; 32] {
182		let t = T::decode(&mut &x[..])
183			.expect("Input bytes length or format mismatch for the expected type");
184
185		let felts = t.to_felts();
186		hash_to_bytes(&felts)
187	}
188}
189
190impl sp_runtime::traits::Hash for PoseidonHasher {
191	type Output = H256;
192
193	fn hash(s: &[u8]) -> Self::Output {
194		H256::from_slice(&Self::hash_for_circuit(s))
195	}
196
197	/// Produce the hash of some codec-encodable value.
198	fn hash_of<S: Encode>(s: &S) -> Self::Output {
199		Encode::using_encoded(s, <Self as Hasher>::hash)
200	}
201
202	fn ordered_trie_root(input: Vec<Vec<u8>>, state_version: StateVersion) -> Self::Output {
203		log::debug!(target: "poseidon",
204			"PoseidonHasher::ordered_trie_root input={input:?} version={state_version:?}",
205		);
206		let res = match state_version {
207			StateVersion::V0 => LayoutV0::<PoseidonHasher>::ordered_trie_root(input),
208			StateVersion::V1 => LayoutV1::<PoseidonHasher>::ordered_trie_root(input),
209		};
210		log::debug!(target: "poseidon", "PoseidonHasher::ordered_trie_root res={res:?}");
211		res
212	}
213
214	fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>, version: StateVersion) -> Self::Output {
215		log::debug!(target: "poseidon",
216			"PoseidonHasher::trie_root input={input:?} version={version:?}"
217		);
218		let res = match version {
219			StateVersion::V0 => LayoutV0::<PoseidonHasher>::trie_root(input),
220			StateVersion::V1 => LayoutV1::<PoseidonHasher>::trie_root(input),
221		};
222		log::debug!(target: "poseidon", "PoseidonHasher::trie_root res={res:?}");
223		res
224	}
225}
226
227#[cfg(test)]
228mod tests {
229	use super::*;
230	use hex;
231	use qp_poseidon_constants::POSEIDON2_OUTPUT;
232	use qp_poseidon_core::serialization::bytes_to_felts;
233	use scale_info::prelude::vec;
234
235	#[cfg(feature = "std")]
236	use env_logger;
237
238	#[cfg(all(feature = "std", test))]
239	#[ctor::ctor]
240	fn init_logger_global() {
241		let _ = env_logger::builder().is_test(true).try_init();
242	}
243
244	#[test]
245	fn test_substrate_wrapper_compatibility() {
246		let input = b"test data";
247		let core_hash = hash_for_circuit::<PROOF_NODE_MAX_SIZE_FELTS>(input);
248		let wrapper_hash = PoseidonHasher::hash_for_circuit(input);
249		assert_eq!(core_hash, wrapper_hash);
250	}
251
252	#[test]
253	fn test_empty_input() {
254		let result = PoseidonHasher::hash_for_circuit(&[]);
255		assert_eq!(result.len(), 32);
256	}
257
258	#[test]
259	fn test_single_byte() {
260		let input = vec![42u8];
261		let result = PoseidonHasher::hash_for_circuit(&input);
262		assert_eq!(result.len(), 32);
263	}
264
265	#[test]
266	fn test_exactly_32_bytes() {
267		let input = [1u8; 32];
268		let result = PoseidonHasher::hash_for_circuit(&input);
269		assert_eq!(result.len(), 32);
270	}
271
272	#[test]
273	fn test_multiple_chunks() {
274		let input = [2u8; 64];
275		let result = PoseidonHasher::hash_for_circuit(&input);
276		assert_eq!(result.len(), 32);
277	}
278
279	#[test]
280	fn test_partial_chunk() {
281		let input = [3u8; 40];
282		let result = PoseidonHasher::hash_for_circuit(&input);
283		assert_eq!(result.len(), 32);
284	}
285
286	#[test]
287	fn test_consistency() {
288		let input = [4u8; 50];
289		let iterations = 100;
290		let current_hash = PoseidonHasher::hash_for_circuit(&input);
291
292		for _ in 0..iterations {
293			let hash1 = PoseidonHasher::hash_for_circuit((&current_hash).as_ref());
294			let current_hash = PoseidonHasher::hash_for_circuit((&current_hash).as_ref());
295			assert_eq!(hash1, current_hash, "Hash function should be deterministic");
296		}
297	}
298
299	#[test]
300	fn test_different_inputs() {
301		let input1 = [5u8; 32];
302		let input2 = [6u8; 32];
303		let hash1 = PoseidonHasher::hash_for_circuit(&input1);
304		let hash2 = PoseidonHasher::hash_for_circuit(&input2);
305		assert_ne!(hash1, hash2, "Different inputs should produce different hashes");
306	}
307
308	#[test]
309	fn test_poseidon_hash_input_sizes() {
310		for size in 1..=128 {
311			let input: Vec<u8> = (0..size).map(|i| (i * i % 256) as u8).collect();
312			let hash = PoseidonHasher::hash_for_circuit(&input);
313			assert_eq!(
314				hash.as_slice().len(),
315				32,
316				"Input size {} should produce 32-byte hash",
317				size
318			);
319		}
320	}
321
322	#[test]
323	fn test_big_preimage() {
324		for overflow in 1..=200 {
325			let preimage = (18446744069414584321u64 + overflow).to_le_bytes();
326			let _hash = PoseidonHasher::hash_for_circuit(&preimage);
327		}
328	}
329
330	#[test]
331	fn test_circuit_preimage() {
332		let preimage =
333			hex::decode("afd8e7530b95ee5ebab950c9a0c62fae1e80463687b3982233028e914f8ec7cc");
334		let hash = PoseidonHasher::hash_for_circuit(&*preimage.unwrap());
335		let _hash = PoseidonHasher::hash_for_circuit(hash.as_slice());
336	}
337
338	#[test]
339	fn test_random_inputs() {
340		let hex_strings = [
341			"a3f8",
342			"1b7e9d",
343			"4c2a6f81",
344			"e5d30b9a",
345			"1a4f7c2e9b0d8356",
346			"3e8d2a7f5c1b09e4d6f7a2c8",
347			"7b3e9a1f4c8d2e6b0a5f9d3c",
348			"1a4f7c2e9b0d83561a4f7c2e9b0d83561a4f7c2e9b0d83561a4f7c2e9b0d8356",
349			"e5d30b9a4c2a6f81e5d30b9a4c2a6f81e5d30b9a4c2a6f81e5d30b9a4c2a6f81",
350		];
351
352		for hex_string in hex_strings.iter() {
353			let preimage = hex::decode(hex_string).unwrap();
354			let hash = PoseidonHasher::hash_for_circuit(&preimage);
355			let _hash2 = PoseidonHasher::hash_for_circuit(&hash.as_slice());
356		}
357	}
358
359	#[test]
360	fn test_substrate_hash_512() {
361		use qp_poseidon_core::{hash_bytes, hash_squeeze_twice};
362
363		let input = b"test substrate 512-bit";
364		let hash512 = hash_squeeze_twice(input);
365
366		// Should be exactly 64 bytes
367		assert_eq!(hash512.len(), 64);
368
369		// Should be deterministic
370		let hash512_2 = hash_squeeze_twice(input);
371		assert_eq!(hash512, hash512_2);
372
373		// First 32 bytes should match regular hash
374		let regular_hash = hash_bytes(input);
375		assert_eq!(&hash512[0..32], &regular_hash);
376	}
377
378	#[test]
379	fn test_hash_twice() {
380		use qp_poseidon_core::hash_twice;
381
382		let preimage =
383			hex::decode("afd8e7530b95ee5ebab950c9a0c62fae1e80463687b3982233028e914f8ec7cc")
384				.unwrap();
385		let felts = bytes_to_felts(&preimage);
386		let _hash = hash_twice(&felts);
387	}
388
389	#[test]
390	fn test_hash_storage() {
391		use sp_core::crypto::AccountId32;
392
393		let asset_id = 42_u32;
394		let transfer_count = 7_u64;
395		let from_account = AccountId32::new([1u8; 32]);
396		let to_account = AccountId32::new([2u8; 32]);
397		let amount = 1_000_000_u128;
398
399		let encoded =
400			(asset_id, transfer_count, from_account.clone(), to_account.clone(), amount).encode();
401
402		// The generic type T must match the structure of the encoded data
403		type TransferKey = (u32, u64, AccountId32, AccountId32, u128);
404
405		let hash = PoseidonHasher::hash_storage::<TransferKey>(&encoded);
406		assert_eq!(hash.len(), 32);
407
408		// Should fail if the input length is incorrect
409		let invalid_encoded = &encoded[0..encoded.len() - 1];
410
411		let result = std::panic::catch_unwind(|| {
412			let _ = PoseidonHasher::hash_storage::<TransferKey>(invalid_encoded);
413		});
414		assert!(result.is_err(), "Expected panic due to invalid input length");
415	}
416
417	#[test]
418	fn test_hash_storage_generic() {
419		// Test with simple u64
420		let val = 12345_u64;
421		let encoded = val.encode();
422		let hash = PoseidonHasher::hash_storage::<u64>(&encoded);
423		assert_eq!(hash.len(), 32);
424
425		// Test with tuple
426		let val_tuple = (1u32, 2u64);
427		let encoded_tuple = val_tuple.encode();
428		let hash_tuple = PoseidonHasher::hash_storage::<(u32, u64)>(&encoded_tuple);
429		assert_eq!(hash_tuple.len(), 32);
430
431		// Test with u32
432		let val_u32 = 12345_u32;
433		let encoded_u32 = val_u32.encode();
434		let hash_u32 = PoseidonHasher::hash_storage::<u32>(&encoded_u32);
435		assert_eq!(hash_u32.len(), 32);
436
437		// Test with Vec
438		let val_vec = vec![1u32, 2u32, 3u32];
439		let encoded_vec = val_vec.encode();
440		let hash_vec = PoseidonHasher::hash_storage::<Vec<u32>>(&encoded_vec);
441		assert_eq!(hash_vec.len(), 32);
442	}
443
444	#[test]
445	fn test_to_felts_uses_4_felts_for_accounts() {
446		use sp_core::crypto::AccountId32;
447
448		let account = AccountId32::new([42u8; 32]);
449		let felts = account.to_felts();
450
451		// Should produce 4 felts (8 bytes per felt for 32 bytes)
452		assert_eq!(felts.len(), POSEIDON2_OUTPUT);
453	}
454
455	#[test]
456	fn test_to_felts_uses_4_felts_for_byte_arrays() {
457		let bytes = [42u8; 32];
458		let felts = bytes.to_felts();
459
460		// Should produce 4 felts (8 bytes per felt for 32 bytes)
461		assert_eq!(felts.len(), POSEIDON2_OUTPUT);
462	}
463}