Skip to content

Commit 5e90ce3

Browse files
authored
feat(rust/catalyst-types): CatalystId proper comparison functionality (#597)
* disable PartialEq and Eq traits for CatalystId * wip * add `eq_with_userinfo` and `eq_with_rolekey` with tests * wip * fix * fix spelling * fix clippy
1 parent 4b8a4e8 commit 5e90ce3

File tree

2 files changed

+88
-2
lines changed

2 files changed

+88
-2
lines changed

rust/catalyst-types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
3939
proptest = { version = "1.6.0", features = ["attr-macro"] }
4040
rand = "0.8.5"
4141
serde_json = "1"
42+
test-case = "3.3.1"

rust/catalyst-types/src/catalyst_id/mod.rs

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod role_index;
99

1010
use std::{
1111
fmt::{Display, Formatter},
12+
hash::Hash,
1213
str::FromStr,
1314
sync::Arc,
1415
};
@@ -42,7 +43,7 @@ pub struct CatalystId {
4243
}
4344

4445
/// A Catalyst ID data intended to be wrapper in `Arc`.
45-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
46+
#[derive(Debug, Clone)]
4647
struct CatalystIdInner {
4748
/// Username
4849
username: Option<String>,
@@ -535,6 +536,50 @@ impl CatalystId {
535536
.without_encryption()
536537
.as_id()
537538
}
539+
540+
/// Comparisons of `CatalystId` based on original `PartialEq` plus including
541+
/// `username` and `nonce` fields.
542+
#[must_use]
543+
pub fn eq_with_userinfo(
544+
&self,
545+
other: &Self,
546+
) -> bool {
547+
self.eq(other) && self.username().eq(&other.username()) && self.nonce().eq(&other.nonce())
548+
}
549+
550+
/// Comparisons of `CatalystId` based on `CatalystId::eq_with_userinfo` plus including
551+
/// `role` and `rotation` fields.
552+
#[must_use]
553+
pub fn eq_with_role(
554+
&self,
555+
other: &Self,
556+
) -> bool {
557+
self.eq_with_userinfo(other) && self.role_and_rotation().eq(&other.role_and_rotation())
558+
}
559+
}
560+
561+
impl PartialEq for CatalystIdInner {
562+
fn eq(
563+
&self,
564+
other: &Self,
565+
) -> bool {
566+
self.network.eq(&other.network)
567+
&& self.subnet.eq(&other.subnet)
568+
&& self.role0_pk.eq(&other.role0_pk)
569+
}
570+
}
571+
572+
impl Eq for CatalystIdInner {}
573+
574+
impl Hash for CatalystIdInner {
575+
fn hash<H: std::hash::Hasher>(
576+
&self,
577+
state: &mut H,
578+
) {
579+
self.network.hash(state);
580+
self.subnet.hash(state);
581+
self.role0_pk.hash(state);
582+
}
538583
}
539584

540585
impl FromStr for CatalystId {
@@ -743,6 +788,7 @@ mod tests {
743788
use chrono::{DateTime, Utc};
744789
use ed25519_dalek::SigningKey;
745790
use rand::rngs::OsRng;
791+
use test_case::test_case;
746792

747793
use super::CatalystId;
748794

@@ -796,7 +842,46 @@ mod tests {
796842
let uri_id = test_uri.parse::<CatalystId>().unwrap();
797843
let short_id = expected_id.parse::<CatalystId>().unwrap();
798844

799-
assert_eq!(uri_id.as_short_id(), short_id);
845+
assert_eq!(uri_id.as_short_id().inner, short_id.inner);
846+
}
847+
848+
#[test_case(0, 1, true, false, false; "base vs user")]
849+
#[test_case(0, 2, true, false, false; "base vs user_nonce")]
850+
#[test_case(0, 3, true, false, false; "base vs nonce")]
851+
#[test_case(0, 4, true, true, true; "base vs base_duplicate")]
852+
#[test_case(7, 8, true, true, false; "midnight_0_1 vs midnight_2_1")]
853+
#[test_case(0, 5, false, false, false; "cardano vs preprod")]
854+
#[test_case(5, 6, false, false, false; "preprod vs preview")]
855+
#[test_case(6, 7, false, false, false; "preview vs midnight")]
856+
#[test_case(1, 2, true, false, false; "user vs user_nonce")]
857+
#[test_case(2, 3, true, false, false; "user_nonce vs nonce")]
858+
#[test_case(8, 8, true, true, true; "identical self comparison")]
859+
#[allow(clippy::indexing_slicing, clippy::similar_names)]
860+
fn test_all_comparisons(
861+
idx_a: usize,
862+
idx_b: usize,
863+
expected_eq: bool,
864+
expected_userinfo: bool,
865+
expected_role: bool,
866+
) {
867+
let id_a = CATALYST_ID_TEST_VECTOR[idx_a]
868+
.parse::<CatalystId>()
869+
.unwrap();
870+
let id_b = CATALYST_ID_TEST_VECTOR[idx_b]
871+
.parse::<CatalystId>()
872+
.unwrap();
873+
874+
assert_eq!(id_a == id_b, expected_eq, "PartialEq failed");
875+
assert_eq!(
876+
id_a.eq_with_userinfo(&id_b),
877+
expected_userinfo,
878+
"eq_with_userinfo failed"
879+
);
880+
assert_eq!(
881+
id_a.eq_with_role(&id_b),
882+
expected_role,
883+
"eq_with_role failed"
884+
);
800885
}
801886

802887
#[ignore = "Test to be fixed and re-enabled"]

0 commit comments

Comments
 (0)