From 9734ea38d0803bb45e9ca4b8a18c83766e6df22d Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 29 Nov 2024 14:36:28 +0100 Subject: [PATCH] split --- ...confirmation_role_with_factor_instances.rs | 33 +++ ...archical_deterministic_factor_instances.rs | 0 .../factor_instance_level/mod.rs | 11 + .../primary_role_with_factor_instances.rs | 110 ++++++++ .../recovery_role_with_factor_instances.rs | 32 +++ .../role_with_factor_instances.rs | 77 +++++ ...onfirmation_role_with_factor_source_ids.rs | 100 +++++++ .../factor_source_id_level/mod.rs | 9 + .../primary_role_with_factor_source_ids.rs | 104 +++++++ .../recovery_role_with_factor_source_ids.rs | 63 +++++ .../roles_with_factor_ids.rs | 3 + .../confirmation_role_with_factor_sources.rs | 36 +++ .../factor_levels/factor_source_level/mod.rs | 6 + .../primary_role_with_factor_sources.rs | 36 +++ .../recovery_role_with_factor_sources.rs | 36 +++ .../roles_with_factor_sources.rs | 32 +++ crates/rules/src/roles/factor_levels/mod.rs | 7 + crates/rules/src/roles/mod.rs | 10 +- .../src/roles/role_with_factor_instances.rs | 251 ----------------- .../rules/src/roles/roles_with_factor_ids.rs | 265 ------------------ .../src/roles/roles_with_factor_sources.rs | 134 --------- 21 files changed, 697 insertions(+), 658 deletions(-) create mode 100644 crates/rules/src/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs rename crates/rules/src/roles/{ => factor_levels/factor_instance_level}/general_role_with_hierarchical_deterministic_factor_instances.rs (100%) create mode 100644 crates/rules/src/roles/factor_levels/factor_instance_level/mod.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_id_level/mod.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_level/mod.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs create mode 100644 crates/rules/src/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs create mode 100644 crates/rules/src/roles/factor_levels/mod.rs delete mode 100644 crates/rules/src/roles/role_with_factor_instances.rs delete mode 100644 crates/rules/src/roles/roles_with_factor_ids.rs delete mode 100644 crates/rules/src/roles/roles_with_factor_sources.rs diff --git a/crates/rules/src/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs b/crates/rules/src/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs new file mode 100644 index 00000000..179d18f1 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_instance_level/confirmation_role_with_factor_instances.rs @@ -0,0 +1,33 @@ +use crate::prelude::*; + +pub(crate) type ConfirmationRoleWithFactorInstances = + RoleWithFactorInstances<{ ROLE_CONFIRMATION }>; + +impl HasSampleValues for ConfirmationRoleWithFactorInstances { + fn sample() -> Self { + MatrixOfFactorInstances::sample().confirmation_role + } + + fn sample_other() -> Self { + MatrixOfFactorInstances::sample_other().confirmation_role + } +} + +#[cfg(test)] +mod confirmation_tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = ConfirmationRoleWithFactorInstances; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/rules/src/roles/general_role_with_hierarchical_deterministic_factor_instances.rs b/crates/rules/src/roles/factor_levels/factor_instance_level/general_role_with_hierarchical_deterministic_factor_instances.rs similarity index 100% rename from crates/rules/src/roles/general_role_with_hierarchical_deterministic_factor_instances.rs rename to crates/rules/src/roles/factor_levels/factor_instance_level/general_role_with_hierarchical_deterministic_factor_instances.rs diff --git a/crates/rules/src/roles/factor_levels/factor_instance_level/mod.rs b/crates/rules/src/roles/factor_levels/factor_instance_level/mod.rs new file mode 100644 index 00000000..ca98a01d --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_instance_level/mod.rs @@ -0,0 +1,11 @@ +mod confirmation_role_with_factor_instances; +mod general_role_with_hierarchical_deterministic_factor_instances; +mod primary_role_with_factor_instances; +mod recovery_role_with_factor_instances; +mod role_with_factor_instances; + +pub(crate) use confirmation_role_with_factor_instances::*; +pub use general_role_with_hierarchical_deterministic_factor_instances::*; +pub(crate) use primary_role_with_factor_instances::*; +pub(crate) use recovery_role_with_factor_instances::*; +pub(crate) use role_with_factor_instances::*; diff --git a/crates/rules/src/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs b/crates/rules/src/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs new file mode 100644 index 00000000..de794340 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_instance_level/primary_role_with_factor_instances.rs @@ -0,0 +1,110 @@ +use crate::prelude::*; + +pub(crate) type PrimaryRoleWithFactorInstances = RoleWithFactorInstances<{ ROLE_PRIMARY }>; + +impl HasSampleValues for PrimaryRoleWithFactorInstances { + fn sample() -> Self { + MatrixOfFactorInstances::sample().primary_role + } + + fn sample_other() -> Self { + MatrixOfFactorInstances::sample_other().primary_role + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = PrimaryRoleWithFactorInstances; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + #[should_panic] + fn primary_role_non_securified_threshold_instances_is_err() { + let _ = SUT::with_factors( + 1, + [ + HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_unsecurified_at_index(0).into() + ], + [] + ); + } + + #[test] + fn assert_json_sample() { + let sut = SUT::sample(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "threshold": 2, + "thresholdFactors": [ + { + "factorSourceID": { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + "badge": { + "discriminator": "virtualSource", + "virtualSource": { + "discriminator": "hierarchicalDeterministicPublicKey", + "hierarchicalDeterministicPublicKey": { + "publicKey": { + "curve": "curve25519", + "compressedData": "427969814e15d74c3ff4d9971465cb709d210c8a7627af9466bdaa67bd0929b7" + }, + "derivationPath": { + "scheme": "cap26", + "path": "m/44H/1022H/1H/525H/1460H/0S" + } + } + } + } + }, + { + "factorSourceID": { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + }, + "badge": { + "discriminator": "virtualSource", + "virtualSource": { + "discriminator": "hierarchicalDeterministicPublicKey", + "hierarchicalDeterministicPublicKey": { + "publicKey": { + "curve": "curve25519", + "compressedData": "92cd6838cd4e7b0523ed93d498e093f71139ffd5d632578189b39a26005be56b" + }, + "derivationPath": { + "scheme": "cap26", + "path": "m/44H/1022H/1H/525H/1460H/0S" + } + } + } + } + } + ], + "overrideFactors": [] + } + "#, + ); + } +} diff --git a/crates/rules/src/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs b/crates/rules/src/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs new file mode 100644 index 00000000..3ce01203 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_instance_level/recovery_role_with_factor_instances.rs @@ -0,0 +1,32 @@ +use crate::prelude::*; + +pub(crate) type RecoveryRoleWithFactorInstances = RoleWithFactorInstances<{ ROLE_RECOVERY }>; + +impl HasSampleValues for RecoveryRoleWithFactorInstances { + fn sample() -> Self { + MatrixOfFactorInstances::sample().recovery_role + } + + fn sample_other() -> Self { + MatrixOfFactorInstances::sample_other().recovery_role + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = RecoveryRoleWithFactorInstances; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/rules/src/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs b/crates/rules/src/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs new file mode 100644 index 00000000..e8204238 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_instance_level/role_with_factor_instances.rs @@ -0,0 +1,77 @@ +use crate::prelude::*; + +pub(crate) type RoleWithFactorInstances = + AbstractBuiltRoleWithFactor; + +impl RoleWithFactorSources { + fn from(other: &RoleWithFactorSources) -> Self { + Self::with_factors( + other.get_threshold(), + other.get_threshold_factors().clone(), + other.get_override_factors().clone(), + ) + } +} + +impl MatrixOfFactorSources { + pub(crate) fn get_role(&self) -> RoleWithFactorSources { + match R { + ROLE_PRIMARY => RoleWithFactorSources::from(&self.primary_role), + ROLE_RECOVERY => RoleWithFactorSources::from(&self.recovery_role), + ROLE_CONFIRMATION => RoleWithFactorSources::from(&self.confirmation_role), + _ => panic!("unknown"), + } + } +} + +impl RoleWithFactorInstances { + pub(crate) fn fulfilling_role_of_factor_sources_with_factor_instances( + consuming_instances: &IndexMap, + matrix_of_factor_sources: &MatrixOfFactorSources, + ) -> Result { + let role_kind = RoleKind::from_u8(R).unwrap(); + + let role_of_sources = matrix_of_factor_sources.get_role::(); + assert_eq!(role_of_sources.role(), role_kind); + let threshold: u8 = role_of_sources.get_threshold(); + + // Threshold factors + let threshold_factors = + Self::try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( + consuming_instances, + role_of_sources.get_threshold_factors(), + )?; + + // Override factors + let override_factors = + Self::try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( + consuming_instances, + role_of_sources.get_override_factors(), + )?; + + let role_with_instances = + Self::with_factors(threshold, threshold_factors, override_factors); + + assert_eq!(role_with_instances.role(), role_kind); + Ok(role_with_instances) + } + + fn try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( + instances: &IndexMap, + from: &[FactorSource], + ) -> Result, CommonError> { + from.iter() + .map(|f| { + if let Some(existing) = instances.get(&f.id_from_hash()) { + let hd_instance = existing + .first() + .ok_or(CommonError::MissingFactorMappingInstancesIntoRole)?; + let instance = FactorInstance::from(hd_instance); + Ok(instance) + } else { + Err(CommonError::MissingFactorMappingInstancesIntoRole) + } + }) + .collect::, CommonError>>() + } +} diff --git a/crates/rules/src/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs b/crates/rules/src/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs new file mode 100644 index 00000000..df0810e8 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_id_level/confirmation_role_with_factor_source_ids.rs @@ -0,0 +1,100 @@ +use crate::prelude::*; + +pub type ConfirmationRoleWithFactorSourceIds = RoleWithFactorSourceIds<{ ROLE_CONFIRMATION }>; + +impl HasSampleValues for ConfirmationRoleWithFactorSourceIds { + /// Config MFA 1.1 + fn sample() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source(FactorSourceID::sample_password()) + .unwrap(); + builder.build().unwrap() + } + + /// Config MFA 2.1 + fn sample_other() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source(FactorSourceID::sample_device()) + .unwrap(); + builder.build().unwrap() + } +} +impl HasSampleValues for RecoveryRoleWithFactorSourceIds { + /// Config MFA 1.1 + fn sample() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source(FactorSourceID::sample_device()) + .unwrap(); + + builder + .add_factor_source(FactorSourceID::sample_ledger()) + .unwrap(); + builder.build().unwrap() + } + + /// Config MFA 3.3 + fn sample_other() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source(FactorSourceID::sample_ledger_other()) + .unwrap(); + + builder.build().unwrap() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = ConfirmationRoleWithFactorSourceIds; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn get_all_factors() { + let sut = SUT::sample(); + let factors = sut.all_factors(); + assert_eq!( + factors.len(), + sut.get_override_factors().len() + sut.get_threshold_factors().len() + ); + } + + #[test] + fn assert_json() { + let sut = SUT::sample(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "passphrase", + "body": "181ab662e19fac3ad9f08d5c673b286d4a5ed9cd3762356dc9831dc42427c1b9" + } + } + ] + } + "#, + ); + } +} diff --git a/crates/rules/src/roles/factor_levels/factor_source_id_level/mod.rs b/crates/rules/src/roles/factor_levels/factor_source_id_level/mod.rs new file mode 100644 index 00000000..206017bd --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_id_level/mod.rs @@ -0,0 +1,9 @@ +mod confirmation_role_with_factor_source_ids; +mod primary_role_with_factor_source_ids; +mod recovery_role_with_factor_source_ids; +mod roles_with_factor_ids; + +pub use confirmation_role_with_factor_source_ids::*; +pub use primary_role_with_factor_source_ids::*; +pub use recovery_role_with_factor_source_ids::*; +pub use roles_with_factor_ids::*; diff --git a/crates/rules/src/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs b/crates/rules/src/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs new file mode 100644 index 00000000..d526ad79 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_id_level/primary_role_with_factor_source_ids.rs @@ -0,0 +1,104 @@ +use crate::prelude::*; + +pub type PrimaryRoleWithFactorSourceIds = RoleWithFactorSourceIds<{ ROLE_PRIMARY }>; + +impl PrimaryRoleWithFactorSourceIds { + /// Config MFA 1.1 + pub fn sample_primary() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source_to_threshold(FactorSourceID::sample_device()) + .unwrap(); + + builder + .add_factor_source_to_threshold(FactorSourceID::sample_ledger()) + .unwrap(); + builder.set_threshold(2).unwrap(); + builder.build().unwrap() + } +} + +impl HasSampleValues for PrimaryRoleWithFactorSourceIds { + fn sample() -> Self { + Self::sample_primary() + } + + fn sample_other() -> Self { + let mut builder = RoleBuilder::new(); + builder + .add_factor_source_to_threshold(FactorSourceID::sample_device()) + .unwrap(); + + builder + .add_factor_source_to_threshold(FactorSourceID::sample_ledger()) + .unwrap(); + builder.set_threshold(1).unwrap(); + builder.build().unwrap() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = PrimaryRoleWithFactorSourceIds; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn get_all_factors() { + let sut = SUT::sample_primary(); + let factors = sut.all_factors(); + assert_eq!( + factors.len(), + sut.get_override_factors().len() + sut.get_threshold_factors().len() + ); + } + + #[test] + fn get_threshold() { + let sut = SUT::sample_primary(); + assert_eq!(sut.get_threshold(), 2); + } + + #[test] + fn assert_json_sample_primary() { + let sut = SUT::sample_primary(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "threshold": 2, + "thresholdFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ], + "overrideFactors": [] + } + "#, + ); + } +} diff --git a/crates/rules/src/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs b/crates/rules/src/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs new file mode 100644 index 00000000..f0e7e111 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_id_level/recovery_role_with_factor_source_ids.rs @@ -0,0 +1,63 @@ +use crate::prelude::*; + +pub type RecoveryRoleWithFactorSourceIds = RoleWithFactorSourceIds<{ ROLE_RECOVERY }>; + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = RecoveryRoleWithFactorSourceIds; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn get_all_factors() { + let sut = SUT::sample(); + let factors = sut.all_factors(); + assert_eq!( + factors.len(), + sut.get_override_factors().len() + sut.get_threshold_factors().len() + ); + } + + #[test] + fn assert_json() { + let sut = SUT::sample(); + assert_eq_after_json_roundtrip( + &sut, + r#" + { + "threshold": 0, + "thresholdFactors": [], + "overrideFactors": [ + { + "discriminator": "fromHash", + "fromHash": { + "kind": "device", + "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" + } + }, + { + "discriminator": "fromHash", + "fromHash": { + "kind": "ledgerHQHardwareWallet", + "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" + } + } + ] + } + "#, + ); + } +} diff --git a/crates/rules/src/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs b/crates/rules/src/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs new file mode 100644 index 00000000..815877b9 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_id_level/roles_with_factor_ids.rs @@ -0,0 +1,3 @@ +use crate::prelude::*; + +pub type RoleWithFactorSourceIds = AbstractBuiltRoleWithFactor; diff --git a/crates/rules/src/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs b/crates/rules/src/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs new file mode 100644 index 00000000..d324f8fe --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_level/confirmation_role_with_factor_sources.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; + +pub(crate) type ConfirmationRoleWithFactorSources = RoleWithFactorSources<{ ROLE_CONFIRMATION }>; + +impl HasSampleValues for ConfirmationRoleWithFactorSources { + fn sample() -> Self { + let ids = ConfirmationRoleWithFactorSourceIds::sample(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } + + fn sample_other() -> Self { + let ids = ConfirmationRoleWithFactorSourceIds::sample_other(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = ConfirmationRoleWithFactorSources; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/rules/src/roles/factor_levels/factor_source_level/mod.rs b/crates/rules/src/roles/factor_levels/factor_source_level/mod.rs new file mode 100644 index 00000000..b53a5e14 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_level/mod.rs @@ -0,0 +1,6 @@ +mod confirmation_role_with_factor_sources; +mod primary_role_with_factor_sources; +mod recovery_role_with_factor_sources; +mod roles_with_factor_sources; + +pub(crate) use roles_with_factor_sources::*; diff --git a/crates/rules/src/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs b/crates/rules/src/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs new file mode 100644 index 00000000..d4233277 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_level/primary_role_with_factor_sources.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; + +pub(crate) type PrimaryRoleWithFactorSources = RoleWithFactorSources<{ ROLE_PRIMARY }>; + +impl HasSampleValues for PrimaryRoleWithFactorSources { + fn sample() -> Self { + let ids = PrimaryRoleWithFactorSourceIds::sample(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } + + fn sample_other() -> Self { + let ids = PrimaryRoleWithFactorSourceIds::sample_other(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = PrimaryRoleWithFactorSources; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/rules/src/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs b/crates/rules/src/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs new file mode 100644 index 00000000..9c56d167 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_level/recovery_role_with_factor_sources.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; + +pub(crate) type RecoveryRoleWithFactorSources = RoleWithFactorSources<{ ROLE_RECOVERY }>; + +impl HasSampleValues for RecoveryRoleWithFactorSources { + fn sample() -> Self { + let ids = RecoveryRoleWithFactorSourceIds::sample(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } + + fn sample_other() -> Self { + let ids = RecoveryRoleWithFactorSourceIds::sample_other(); + let factor_sources = FactorSources::sample_values_all(); + Self::new(ids, &factor_sources).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = RecoveryRoleWithFactorSources; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } +} diff --git a/crates/rules/src/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs b/crates/rules/src/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs new file mode 100644 index 00000000..20955fd9 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/factor_source_level/roles_with_factor_sources.rs @@ -0,0 +1,32 @@ +use crate::prelude::*; + +pub(crate) type RoleWithFactorSources = AbstractBuiltRoleWithFactor; + +impl RoleWithFactorSources { + pub fn new( + role_with_factor_source_ids: RoleWithFactorSourceIds, + factor_sources: &FactorSources, + ) -> Result { + let lookup_f = |id: &FactorSourceID| -> Result { + factor_sources + .get_id(id) + .ok_or(CommonError::FactorSourceDiscrepancy) + .cloned() + }; + + let lookup = |ids: &Vec| -> Result, CommonError> { + ids.iter() + .map(lookup_f) + .collect::, CommonError>>() + }; + + let threshold_factors = lookup(role_with_factor_source_ids.get_threshold_factors())?; + let override_factors = lookup(role_with_factor_source_ids.get_override_factors())?; + + Ok(Self::with_factors( + role_with_factor_source_ids.get_threshold(), + threshold_factors, + override_factors, + )) + } +} diff --git a/crates/rules/src/roles/factor_levels/mod.rs b/crates/rules/src/roles/factor_levels/mod.rs new file mode 100644 index 00000000..4f5b7785 --- /dev/null +++ b/crates/rules/src/roles/factor_levels/mod.rs @@ -0,0 +1,7 @@ +mod factor_instance_level; +mod factor_source_id_level; +mod factor_source_level; + +pub use factor_instance_level::*; +pub use factor_source_id_level::*; +pub(crate) use factor_source_level::*; diff --git a/crates/rules/src/roles/mod.rs b/crates/rules/src/roles/mod.rs index 90812439..c99cb397 100644 --- a/crates/rules/src/roles/mod.rs +++ b/crates/rules/src/roles/mod.rs @@ -1,13 +1,7 @@ mod abstract_role_builder_or_built; mod builder; -mod general_role_with_hierarchical_deterministic_factor_instances; -mod role_with_factor_instances; -mod roles_with_factor_ids; -mod roles_with_factor_sources; +mod factor_levels; pub(crate) use abstract_role_builder_or_built::*; pub use builder::*; -pub use general_role_with_hierarchical_deterministic_factor_instances::*; -pub(crate) use role_with_factor_instances::*; -pub use roles_with_factor_ids::*; -pub(crate) use roles_with_factor_sources::*; +pub use factor_levels::*; diff --git a/crates/rules/src/roles/role_with_factor_instances.rs b/crates/rules/src/roles/role_with_factor_instances.rs deleted file mode 100644 index a643396b..00000000 --- a/crates/rules/src/roles/role_with_factor_instances.rs +++ /dev/null @@ -1,251 +0,0 @@ -use crate::prelude::*; - -pub(crate) type RoleWithFactorInstances = - AbstractBuiltRoleWithFactor; - -impl RoleWithFactorSources { - fn from(other: &RoleWithFactorSources) -> Self { - Self::with_factors( - other.get_threshold(), - other.get_threshold_factors().clone(), - other.get_override_factors().clone(), - ) - } -} - -impl MatrixOfFactorSources { - pub(crate) fn get_role(&self) -> RoleWithFactorSources { - match R { - ROLE_PRIMARY => RoleWithFactorSources::from(&self.primary_role), - ROLE_RECOVERY => RoleWithFactorSources::from(&self.recovery_role), - ROLE_CONFIRMATION => RoleWithFactorSources::from(&self.confirmation_role), - _ => panic!("unknown"), - } - } -} - -impl RoleWithFactorInstances { - // TODO: MFA - Upgrade this method to follow the rules of when a factor instance might - // be used by MULTIPLE roles. This is a temporary solution to get the tests to pass. - // A proper solution should use follow the rules laid out in: - // https://radixdlt.atlassian.net/wiki/spaces/AT/pages/3758063620/MFA+Rules+for+Factors+and+Security+Shields - pub(crate) fn fulfilling_role_of_factor_sources_with_factor_instances( - consuming_instances: &IndexMap, - matrix_of_factor_sources: &MatrixOfFactorSources, - ) -> Result { - let role_kind = RoleKind::from_u8(R).unwrap(); - - let role_of_sources = matrix_of_factor_sources.get_role::(); - assert_eq!(role_of_sources.role(), role_kind); - let threshold: u8 = role_of_sources.get_threshold(); - - // Threshold factors - let threshold_factors = - Self::try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( - consuming_instances, - role_of_sources.get_threshold_factors(), - )?; - - // Override factors - let override_factors = - Self::try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( - consuming_instances, - role_of_sources.get_override_factors(), - )?; - - let role_with_instances = - Self::with_factors(threshold, threshold_factors, override_factors); - - assert_eq!(role_with_instances.role(), role_kind); - Ok(role_with_instances) - } - - fn try_filling_factor_list_of_role_of_factor_sources_with_factor_instances( - instances: &IndexMap, - from: &[FactorSource], - ) -> Result, CommonError> { - from.iter() - .map(|f| { - if let Some(existing) = instances.get(&f.id_from_hash()) { - let hd_instance = existing - .first() - .ok_or(CommonError::MissingFactorMappingInstancesIntoRole)?; - let instance = FactorInstance::from(hd_instance); - Ok(instance) - } else { - Err(CommonError::MissingFactorMappingInstancesIntoRole) - } - }) - .collect::, CommonError>>() - } -} - -pub(crate) type PrimaryRoleWithFactorInstances = RoleWithFactorInstances<{ ROLE_PRIMARY }>; -pub(crate) type RecoveryRoleWithFactorInstances = RoleWithFactorInstances<{ ROLE_RECOVERY }>; -pub(crate) type ConfirmationRoleWithFactorInstances = - RoleWithFactorInstances<{ ROLE_CONFIRMATION }>; - -impl HasSampleValues for PrimaryRoleWithFactorInstances { - fn sample() -> Self { - MatrixOfFactorInstances::sample().primary_role - } - - fn sample_other() -> Self { - MatrixOfFactorInstances::sample_other().primary_role - } -} - -impl HasSampleValues for ConfirmationRoleWithFactorInstances { - fn sample() -> Self { - MatrixOfFactorInstances::sample().confirmation_role - } - - fn sample_other() -> Self { - MatrixOfFactorInstances::sample_other().confirmation_role - } -} - -impl HasSampleValues for RecoveryRoleWithFactorInstances { - fn sample() -> Self { - MatrixOfFactorInstances::sample().recovery_role - } - - fn sample_other() -> Self { - MatrixOfFactorInstances::sample_other().recovery_role - } -} - -#[cfg(test)] -mod primary_tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = PrimaryRoleWithFactorInstances; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } - - #[test] - #[should_panic] - fn primary_role_non_securified_threshold_instances_is_err() { - let _ = SUT::with_factors( - 1, - [ - HierarchicalDeterministicFactorInstance::sample_mainnet_account_device_factor_fs_10_unsecurified_at_index(0).into() - ], - [] - ); - } - - #[test] - fn assert_json_sample() { - let sut = SUT::sample(); - assert_eq_after_json_roundtrip( - &sut, - r#" - { - "threshold": 2, - "thresholdFactors": [ - { - "factorSourceID": { - "discriminator": "fromHash", - "fromHash": { - "kind": "device", - "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" - } - }, - "badge": { - "discriminator": "virtualSource", - "virtualSource": { - "discriminator": "hierarchicalDeterministicPublicKey", - "hierarchicalDeterministicPublicKey": { - "publicKey": { - "curve": "curve25519", - "compressedData": "427969814e15d74c3ff4d9971465cb709d210c8a7627af9466bdaa67bd0929b7" - }, - "derivationPath": { - "scheme": "cap26", - "path": "m/44H/1022H/1H/525H/1460H/0S" - } - } - } - } - }, - { - "factorSourceID": { - "discriminator": "fromHash", - "fromHash": { - "kind": "ledgerHQHardwareWallet", - "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" - } - }, - "badge": { - "discriminator": "virtualSource", - "virtualSource": { - "discriminator": "hierarchicalDeterministicPublicKey", - "hierarchicalDeterministicPublicKey": { - "publicKey": { - "curve": "curve25519", - "compressedData": "92cd6838cd4e7b0523ed93d498e093f71139ffd5d632578189b39a26005be56b" - }, - "derivationPath": { - "scheme": "cap26", - "path": "m/44H/1022H/1H/525H/1460H/0S" - } - } - } - } - } - ], - "overrideFactors": [] - } - "#, - ); - } -} - -#[cfg(test)] -mod confirmation_tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = ConfirmationRoleWithFactorInstances; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} - -#[cfg(test)] -mod recovery_tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = RecoveryRoleWithFactorInstances; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} diff --git a/crates/rules/src/roles/roles_with_factor_ids.rs b/crates/rules/src/roles/roles_with_factor_ids.rs deleted file mode 100644 index 47b36eee..00000000 --- a/crates/rules/src/roles/roles_with_factor_ids.rs +++ /dev/null @@ -1,265 +0,0 @@ -use crate::prelude::*; - -pub type RoleWithFactorSourceIds = AbstractBuiltRoleWithFactor; - -pub type PrimaryRoleWithFactorSourceIds = RoleWithFactorSourceIds<{ ROLE_PRIMARY }>; -pub type RecoveryRoleWithFactorSourceIds = RoleWithFactorSourceIds<{ ROLE_RECOVERY }>; -pub type ConfirmationRoleWithFactorSourceIds = RoleWithFactorSourceIds<{ ROLE_CONFIRMATION }>; - -impl PrimaryRoleWithFactorSourceIds { - /// Config MFA 1.1 - pub fn sample_primary() -> Self { - let mut builder = RoleBuilder::new(); - builder - .add_factor_source_to_threshold(FactorSourceID::sample_device()) - .unwrap(); - - builder - .add_factor_source_to_threshold(FactorSourceID::sample_ledger()) - .unwrap(); - builder.set_threshold(2).unwrap(); - builder.build().unwrap() - } -} - -impl HasSampleValues for PrimaryRoleWithFactorSourceIds { - fn sample() -> Self { - Self::sample_primary() - } - - fn sample_other() -> Self { - let mut builder = RoleBuilder::new(); - builder - .add_factor_source_to_threshold(FactorSourceID::sample_device()) - .unwrap(); - - builder - .add_factor_source_to_threshold(FactorSourceID::sample_ledger()) - .unwrap(); - builder.set_threshold(1).unwrap(); - builder.build().unwrap() - } -} - -impl HasSampleValues for ConfirmationRoleWithFactorSourceIds { - /// Config MFA 1.1 - fn sample() -> Self { - let mut builder = RoleBuilder::new(); - builder - .add_factor_source(FactorSourceID::sample_password()) - .unwrap(); - builder.build().unwrap() - } - - /// Config MFA 2.1 - fn sample_other() -> Self { - let mut builder = RoleBuilder::new(); - builder - .add_factor_source(FactorSourceID::sample_device()) - .unwrap(); - builder.build().unwrap() - } -} -impl HasSampleValues for RecoveryRoleWithFactorSourceIds { - /// Config MFA 1.1 - fn sample() -> Self { - let mut builder = RoleBuilder::new(); - builder - .add_factor_source(FactorSourceID::sample_device()) - .unwrap(); - - builder - .add_factor_source(FactorSourceID::sample_ledger()) - .unwrap(); - builder.build().unwrap() - } - - /// Config MFA 3.3 - fn sample_other() -> Self { - let mut builder = RoleBuilder::new(); - builder - .add_factor_source(FactorSourceID::sample_ledger_other()) - .unwrap(); - - builder.build().unwrap() - } -} - -#[cfg(test)] -mod primary_tests { - - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = PrimaryRoleWithFactorSourceIds; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } - - #[test] - fn get_all_factors() { - let sut = SUT::sample_primary(); - let factors = sut.all_factors(); - assert_eq!( - factors.len(), - sut.get_override_factors().len() + sut.get_threshold_factors().len() - ); - } - - #[test] - fn get_threshold() { - let sut = SUT::sample_primary(); - assert_eq!(sut.get_threshold(), 2); - } - - #[test] - fn assert_json_sample_primary() { - let sut = SUT::sample_primary(); - assert_eq_after_json_roundtrip( - &sut, - r#" - { - "threshold": 2, - "thresholdFactors": [ - { - "discriminator": "fromHash", - "fromHash": { - "kind": "device", - "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" - } - }, - { - "discriminator": "fromHash", - "fromHash": { - "kind": "ledgerHQHardwareWallet", - "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" - } - } - ], - "overrideFactors": [] - } - "#, - ); - } -} - -#[cfg(test)] -mod recovery_tests { - - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = RecoveryRoleWithFactorSourceIds; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } - - #[test] - fn get_all_factors() { - let sut = SUT::sample(); - let factors = sut.all_factors(); - assert_eq!( - factors.len(), - sut.get_override_factors().len() + sut.get_threshold_factors().len() - ); - } - - #[test] - fn assert_json() { - let sut = SUT::sample(); - assert_eq_after_json_roundtrip( - &sut, - r#" - { - "threshold": 0, - "thresholdFactors": [], - "overrideFactors": [ - { - "discriminator": "fromHash", - "fromHash": { - "kind": "device", - "body": "f1a93d324dd0f2bff89963ab81ed6e0c2ee7e18c0827dc1d3576b2d9f26bbd0a" - } - }, - { - "discriminator": "fromHash", - "fromHash": { - "kind": "ledgerHQHardwareWallet", - "body": "ab59987eedd181fe98e512c1ba0f5ff059f11b5c7c56f15614dcc9fe03fec58b" - } - } - ] - } - "#, - ); - } -} - -#[cfg(test)] -mod confirmation_tests { - - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = ConfirmationRoleWithFactorSourceIds; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } - - #[test] - fn get_all_factors() { - let sut = SUT::sample(); - let factors = sut.all_factors(); - assert_eq!( - factors.len(), - sut.get_override_factors().len() + sut.get_threshold_factors().len() - ); - } - - #[test] - fn assert_json() { - let sut = SUT::sample(); - assert_eq_after_json_roundtrip( - &sut, - r#" - { - "threshold": 0, - "thresholdFactors": [], - "overrideFactors": [ - { - "discriminator": "fromHash", - "fromHash": { - "kind": "passphrase", - "body": "181ab662e19fac3ad9f08d5c673b286d4a5ed9cd3762356dc9831dc42427c1b9" - } - } - ] - } - "#, - ); - } -} diff --git a/crates/rules/src/roles/roles_with_factor_sources.rs b/crates/rules/src/roles/roles_with_factor_sources.rs deleted file mode 100644 index b7304850..00000000 --- a/crates/rules/src/roles/roles_with_factor_sources.rs +++ /dev/null @@ -1,134 +0,0 @@ -use crate::prelude::*; - -pub(crate) type RoleWithFactorSources = AbstractBuiltRoleWithFactor; -pub(crate) type PrimaryRoleWithFactorSources = RoleWithFactorSources<{ ROLE_PRIMARY }>; -pub(crate) type RecoveryRoleWithFactorSources = RoleWithFactorSources<{ ROLE_RECOVERY }>; -pub(crate) type ConfirmationRoleWithFactorSources = RoleWithFactorSources<{ ROLE_CONFIRMATION }>; - -impl RoleWithFactorSources { - pub fn new( - role_with_factor_source_ids: RoleWithFactorSourceIds, - factor_sources: &FactorSources, - ) -> Result { - let lookup_f = |id: &FactorSourceID| -> Result { - factor_sources - .get_id(id) - .ok_or(CommonError::FactorSourceDiscrepancy) - .cloned() - }; - - let lookup = |ids: &Vec| -> Result, CommonError> { - ids.iter() - .map(lookup_f) - .collect::, CommonError>>() - }; - - let threshold_factors = lookup(role_with_factor_source_ids.get_threshold_factors())?; - let override_factors = lookup(role_with_factor_source_ids.get_override_factors())?; - - Ok(Self::with_factors( - role_with_factor_source_ids.get_threshold(), - threshold_factors, - override_factors, - )) - } -} - -impl HasSampleValues for PrimaryRoleWithFactorSources { - fn sample() -> Self { - let ids = PrimaryRoleWithFactorSourceIds::sample(); - let factor_sources = FactorSources::sample_values_all(); - Self::new(ids, &factor_sources).unwrap() - } - - fn sample_other() -> Self { - let ids = PrimaryRoleWithFactorSourceIds::sample_other(); - let factor_sources = FactorSources::sample_values_all(); - Self::new(ids, &factor_sources).unwrap() - } -} - -impl HasSampleValues for RecoveryRoleWithFactorSources { - fn sample() -> Self { - let ids = RecoveryRoleWithFactorSourceIds::sample(); - let factor_sources = FactorSources::sample_values_all(); - Self::new(ids, &factor_sources).unwrap() - } - - fn sample_other() -> Self { - let ids = RecoveryRoleWithFactorSourceIds::sample_other(); - let factor_sources = FactorSources::sample_values_all(); - Self::new(ids, &factor_sources).unwrap() - } -} - -impl HasSampleValues for ConfirmationRoleWithFactorSources { - fn sample() -> Self { - let ids = ConfirmationRoleWithFactorSourceIds::sample(); - let factor_sources = FactorSources::sample_values_all(); - Self::new(ids, &factor_sources).unwrap() - } - - fn sample_other() -> Self { - let ids = ConfirmationRoleWithFactorSourceIds::sample_other(); - let factor_sources = FactorSources::sample_values_all(); - Self::new(ids, &factor_sources).unwrap() - } -} - -#[cfg(test)] -mod primary_tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = PrimaryRoleWithFactorSources; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} - -#[cfg(test)] -mod recovery_tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = RecoveryRoleWithFactorSources; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -} - -#[cfg(test)] -mod confirmation_tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = ConfirmationRoleWithFactorSources; - - #[test] - fn equality() { - assert_eq!(SUT::sample(), SUT::sample()); - assert_eq!(SUT::sample_other(), SUT::sample_other()); - } - - #[test] - fn inequality() { - assert_ne!(SUT::sample(), SUT::sample_other()); - } -}