use alloc::{collections::BTreeSet, vec::Vec}; use proptest::prelude::*; use rand_utils::rand_value; use super::{Felt, Hasher, Rpx256, StarkField, ZERO}; use crate::{hash::rescue::RpxDigest, ONE}; #[test] fn hash_elements_vs_merge() { let elements = [Felt::new(rand_value()); 8]; let digests: [RpxDigest; 2] = [ RpxDigest::new(elements[..4].try_into().unwrap()), RpxDigest::new(elements[4..].try_into().unwrap()), ]; let m_result = Rpx256::merge(&digests); let h_result = Rpx256::hash_elements(&elements); assert_eq!(m_result, h_result); } #[test] fn merge_vs_merge_in_domain() { let elements = [Felt::new(rand_value()); 8]; let digests: [RpxDigest; 2] = [ RpxDigest::new(elements[..4].try_into().unwrap()), RpxDigest::new(elements[4..].try_into().unwrap()), ]; let merge_result = Rpx256::merge(&digests); // ----- merge with domain = 0 ---------------------------------------------------------------- // set domain to ZERO. This should not change the result. let domain = ZERO; let merge_in_domain_result = Rpx256::merge_in_domain(&digests, domain); assert_eq!(merge_result, merge_in_domain_result); // ----- merge with domain = 1 ---------------------------------------------------------------- // set domain to ONE. This should change the result. let domain = ONE; let merge_in_domain_result = Rpx256::merge_in_domain(&digests, domain); assert_ne!(merge_result, merge_in_domain_result); } #[test] fn hash_elements_vs_merge_with_int() { let tmp = [Felt::new(rand_value()); 4]; let seed = RpxDigest::new(tmp); // ----- value fits into a field element ------------------------------------------------------ let val: Felt = Felt::new(rand_value()); let m_result = Rpx256::merge_with_int(seed, val.as_int()); let mut elements = seed.as_elements().to_vec(); elements.push(val); let h_result = Rpx256::hash_elements(&elements); assert_eq!(m_result, h_result); // ----- value does not fit into a field element ---------------------------------------------- let val = Felt::MODULUS + 2; let m_result = Rpx256::merge_with_int(seed, val); let mut elements = seed.as_elements().to_vec(); elements.push(Felt::new(val)); elements.push(ONE); let h_result = Rpx256::hash_elements(&elements); assert_eq!(m_result, h_result); } #[test] fn hash_padding() { // adding a zero bytes at the end of a byte string should result in a different hash let r1 = Rpx256::hash(&[1_u8, 2, 3]); let r2 = Rpx256::hash(&[1_u8, 2, 3, 0]); assert_ne!(r1, r2); // same as above but with bigger inputs let r1 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6]); let r2 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 0]); assert_ne!(r1, r2); // same as above but with input splitting over two elements let r1 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 7]); let r2 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 7, 0]); assert_ne!(r1, r2); // same as above but with multiple zeros let r1 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 7, 0, 0]); let r2 = Rpx256::hash(&[1_u8, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0]); assert_ne!(r1, r2); } #[test] fn hash_elements_padding() { let e1 = [Felt::new(rand_value()); 2]; let e2 = [e1[0], e1[1], ZERO]; let r1 = Rpx256::hash_elements(&e1); let r2 = Rpx256::hash_elements(&e2); assert_ne!(r1, r2); } #[test] fn hash_elements() { let elements = [ ZERO, ONE, Felt::new(2), Felt::new(3), Felt::new(4), Felt::new(5), Felt::new(6), Felt::new(7), ]; let digests: [RpxDigest; 2] = [ RpxDigest::new(elements[..4].try_into().unwrap()), RpxDigest::new(elements[4..8].try_into().unwrap()), ]; let m_result = Rpx256::merge(&digests); let h_result = Rpx256::hash_elements(&elements); assert_eq!(m_result, h_result); } #[test] fn hash_empty() { let elements: Vec = vec![]; let zero_digest = RpxDigest::default(); let h_result = Rpx256::hash_elements(&elements); assert_eq!(zero_digest, h_result); } #[test] fn hash_empty_bytes() { let bytes: Vec = vec![]; let zero_digest = RpxDigest::default(); let h_result = Rpx256::hash(&bytes); assert_eq!(zero_digest, h_result); } #[test] fn sponge_bytes_with_remainder_length_wont_panic() { // this test targets to assert that no panic will happen with the edge case of having an inputs // with length that is not divisible by the used binary chunk size. 113 is a non-negligible // input length that is prime; hence guaranteed to not be divisible by any choice of chunk // size. // // this is a preliminary test to the fuzzy-stress of proptest. Rpx256::hash(&[0; 113]); } #[test] fn sponge_collision_for_wrapped_field_element() { let a = Rpx256::hash(&[0; 8]); let b = Rpx256::hash(&Felt::MODULUS.to_le_bytes()); assert_ne!(a, b); } #[test] fn sponge_zeroes_collision() { let mut zeroes = Vec::with_capacity(255); let mut set = BTreeSet::new(); (0..255).for_each(|_| { let hash = Rpx256::hash(&zeroes); zeroes.push(0); // panic if a collision was found assert!(set.insert(hash)); }); } proptest! { #[test] fn rpo256_wont_panic_with_arbitrary_input(ref bytes in any::>()) { Rpx256::hash(bytes); } }