Repository: dusk-network/bls12_381 (language: rust)

Issue:
Scalar derives Hash but explicitly implements PartialEq
**Describe the bug**
`Scalar` derives `Hash` but also has an explicit `PartialEq` implementation. [Clippy doesn't like this](https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq) because it wants the traits to agree on the implementation of a `PartialEq`. 

Getting rid of our custom `PartialEq` implementation would mean losing the constant time equality. 

**Additional context**
Hash currently isn't being used internally on Scalar. For now, the warning has been disabled.



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d193af8..3e970653 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - Add `uni_random` scalar generation [#125]
 
+### Changed
+
+- Change `Hash` derive on `Scalar` to explicit implementation [#106]
+
 ## [0.12.2] - 2023-10-11
 
 ### Fixed
@@ -218,6 +222,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 [#117]: https://github.com/dusk-network/bls12_381/issues/117
 [#109]: https://github.com/dusk-network/bls12_381/issues/109
 [#108]: https://github.com/dusk-network/bls12_381/issues/108
+[#106]: https://github.com/dusk-network/bls12_381/issues/106
 [#100]: https://github.com/dusk-network/bls12_381/issues/100
 [#93]: https://github.com/dusk-network/bls12_381/issues/93
 [#75]: https://github.com/dusk-network/bls12_381/issues/75
diff --git a/src/scalar.rs b/src/scalar.rs
index c2f25f73..af274031 100644
--- a/src/scalar.rs
+++ b/src/scalar.rs
@@ -25,7 +25,7 @@ use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
 // The internal representation of this type is four 64-bit unsigned
 // integers in little-endian order. `Scalar` values are always in
 // Montgomery form; i.e., Scalar(a) = aR mod q, with R = 2^256.
-#[derive(Clone, Copy, Eq, Hash)]
+#[derive(Clone, Copy, Eq)]
 #[cfg_attr(
     feature = "rkyv-impl",
     derive(Archive, RkyvSerialize, RkyvDeserialize),
diff --git a/src/scalar/dusk.rs b/src/scalar/dusk.rs
index f17279aa..e3355a58 100644
--- a/src/scalar/dusk.rs
+++ b/src/scalar/dusk.rs
@@ -6,6 +6,7 @@
 
 use core::cmp::{Ord, Ordering, PartialOrd};
 use core::convert::TryFrom;
+use core::hash::{Hash, Hasher};
 use core::ops::{BitAnd, BitXor};
 use dusk_bytes::{Error as BytesError, Serializable};
 use rand_core::{CryptoRng, RngCore};
@@ -205,6 +206,13 @@ impl<'a, 'b> BitAnd<&'b Scalar> for &'a Scalar {
     }
 }
 
+impl Hash for Scalar {
+    #[inline]
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.0.hash(state);
+    }
+}
+
 impl Scalar {
     /// Checks in ct_time whether a Scalar is equal to zero.
     pub fn is_zero(&self) -> Choice {
@@ -407,3 +415,34 @@ fn pow_of_two_test() {
         assert_eq!(Scalar::pow_of_2(i as u64), two.pow(&[i as u64, 0, 0, 0]));
     }
 }
+
+#[test]
+fn test_scalar_eq_and_hash() {
+    use sha3::{Digest, Keccak256};
+
+    let r0 = Scalar::from_raw([
+        0x1fff_3231_233f_fffd,
+        0x4884_b7fa_0003_4802,
+        0x998c_4fef_ecbc_4ff3,
+        0x1824_b159_acc5_0562,
+    ]);
+    let r1 = Scalar::from_raw([
+        0x1fff_3231_233f_fffd,
+        0x4884_b7fa_0003_4802,
+        0x998c_4fef_ecbc_4ff3,
+        0x1824_b159_acc5_0562,
+    ]);
+    let r2 = Scalar::from(7);
+
+    // Check PartialEq
+    assert!(r0 == r1);
+    assert!(r0 != r2);
+
+    let hash_r0 = Keccak256::digest(&r0.to_bytes());
+    let hash_r1 = Keccak256::digest(&r1.to_bytes());
+    let hash_r2 = Keccak256::digest(&r2.to_bytes());
+
+    // Check if hash results are consistent with PartialEq results
+    assert_eq!(hash_r0, hash_r1);
+    assert_ne!(hash_r0, hash_r2);
+}


Test patch: