Skip to content

Commit 9a6a74e

Browse files
committed
feat(Tip5): Implement the Hasher trait
changelog: ignore
2 parents 437116f + d1e364f commit 9a6a74e

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

twenty-first/src/math/tip5.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::hash::Hasher;
2+
13
use arbitrary::Arbitrary;
24
use get_size2::GetSize;
35
use itertools::Itertools;
@@ -673,10 +675,35 @@ impl Sponge for Tip5 {
673675
}
674676
}
675677

678+
impl Hasher for Tip5 {
679+
fn finish(&self) -> u64 {
680+
self.state[0].value()
681+
}
682+
683+
fn write(&mut self, bytes: &[u8]) {
684+
let bfield_elements = bytes.chunks(BFieldElement::BYTES).map(|chunk| {
685+
let mut buffer = [0u8; BFieldElement::BYTES];
686+
buffer[..chunk.len()].copy_from_slice(chunk);
687+
BFieldElement::new(u64::from_le_bytes(buffer))
688+
});
689+
690+
for chunk in bfield_elements.chunks(Tip5::RATE).into_iter() {
691+
let mut buffer = [BFieldElement::ZERO; Tip5::RATE];
692+
for (buffer_elem, chunk_elem) in buffer.iter_mut().zip(chunk) {
693+
*buffer_elem = chunk_elem;
694+
}
695+
self.absorb(buffer)
696+
}
697+
}
698+
}
699+
676700
#[cfg(test)]
677701
pub(crate) mod tip5_tests {
702+
use std::hash::Hash;
678703
use std::ops::Mul;
679704

705+
use insta::assert_snapshot;
706+
use prop::sample::size_range;
680707
use proptest::prelude::*;
681708
use proptest_arbitrary_interop::arb;
682709
use rand::thread_rng;
@@ -1087,6 +1114,38 @@ pub(crate) mod tip5_tests {
10871114
);
10881115
}
10891116

1117+
#[test]
1118+
fn tip5_hasher_trait_test() {
1119+
let mut hasher = Tip5::init();
1120+
let data = b"hello world";
1121+
hasher.write(data);
1122+
assert_snapshot!(hasher.finish(), @"2267905471610932299");
1123+
}
1124+
1125+
#[proptest]
1126+
fn tip5_hasher_consumes_small_data(#[filter(!#bytes.is_empty())] bytes: Vec<u8>) {
1127+
let mut hasher = Tip5::init();
1128+
bytes.hash(&mut hasher);
1129+
1130+
prop_assert_ne!(Tip5::init().finish(), hasher.finish());
1131+
}
1132+
1133+
#[proptest]
1134+
fn appending_small_data_to_big_data_changes_tip5_hash(
1135+
#[any(size_range(2_000..8_000).lift())] big_data: Vec<u8>,
1136+
#[filter(!#small_data.is_empty())] small_data: Vec<u8>,
1137+
) {
1138+
let mut hasher = Tip5::init();
1139+
big_data.hash(&mut hasher);
1140+
let big_data_hash = hasher.finish();
1141+
1142+
// finish doesn't terminate the hasher; see it's documentation
1143+
small_data.hash(&mut hasher);
1144+
let all_data_hash = hasher.finish();
1145+
1146+
prop_assert_ne!(big_data_hash, all_data_hash);
1147+
}
1148+
10901149
#[proptest]
10911150
fn tip5_trace_starts_with_initial_state_and_is_equivalent_to_permutation(
10921151
#[strategy(arb())] mut tip5: Tip5,

0 commit comments

Comments
 (0)