-
Notifications
You must be signed in to change notification settings - Fork 75
Description
The Hash docs says the following:
§ Hash and Eq
When implementing both Hash and Eq, it is important that the following property holds:k1 == k2 -> hash(k1) == hash(k2)
In other words, if two keys are equal, their hashes must also be equal. HashMap and HashSet both rely on this behavior.
Thankfully, you won’t need to worry about upholding this property when deriving both Eq and Hash with #[derive(PartialEq, Eq, Hash)].
Violating this property is a logic error. The behavior resulting from a logic error is not specified, but users of the trait must ensure that such logic errors do not result in undefined behavior. This means that unsafe code must not rely on the correctness of these methods.
But the current Hash implementation from: #350 doesn't align with the current PartialEq implementation.
Using the following code: (works on 64/32 bit machines), Tested on version 0.7.0-rc.10
use std::hash::{DefaultHasher, Hash, Hasher};
use crypto_bigint::BoxedUint;
fn hash(hasher: &DefaultHasher, value: &BoxedUint) -> u64 {
let mut hasher = hasher.clone();
value.hash(&mut hasher);
hasher.finish()
}
fn main() {
let hasher = DefaultHasher::new();
let a = BoxedUint::from_words_with_precision([1], 64);
let b = BoxedUint::from_words_with_precision([1], 128);
assert_eq!(a, b); // Passes
let hash_a = hash(&hasher, &a);
let hash_b = hash(&hasher, &b);
assert_eq!(hash_a, hash_b); //assertion `left == right` failed left: 7912899488978770657, right: 6739742464691574241
}Using a custom Hasher we can inspect the difference:
assertion `left == right` failed
left: [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
right: [2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Buffer Hasher
use std::hash::{Hash, Hasher};
use crypto_bigint::BoxedUint;
struct HasherBytes(Vec<u8>);
impl HasherBytes {
fn new() -> Self {
Self(Vec::new())
}
}
impl Hasher for HasherBytes {
fn finish(&self) -> u64 {
unimplemented!()
}
fn write(&mut self, bytes: &[u8]) {
self.0.extend_from_slice(bytes);
}
}
fn hash_bytes(value: &BoxedUint) -> Vec<u8> {
let mut hasher = HasherBytes::new();
value.hash(&mut hasher);
hasher.0
}
fn main() {
let a = BoxedUint::from_words_with_precision([1], 64);
let b = BoxedUint::from_words_with_precision([1], 128);
assert_eq!(a, b);
let hash_a = hash_bytes(&a);
let hash_b = hash_bytes(&b);
assert_eq!(hash_a, hash_b);
}