Skip to main content

mithril_common/entities/
signed_entity_config_validator.rs

1use std::collections::BTreeSet;
2
3use thiserror::Error;
4
5use crate::entities::{
6    CardanoBlocksTransactionsSigningConfig, CardanoTransactionsSigningConfig,
7    SignedEntityTypeDiscriminants,
8};
9
10/// Validates the consistency of signed entity configuration inputs.
11///
12/// This validator checks whether the set of enabled signed entity discriminants is compatible
13/// with the available signing configuration, without depending on a concrete configuration type.
14///
15/// It is intended to be reusable by different configuration sources that expose the same
16/// validation data.
17pub struct SignedEntityConfigValidator;
18
19impl SignedEntityConfigValidator {
20    /// Checks that all given discriminants have the required signing configuration present.
21    ///
22    ///- Returns `Ok` when all required configurations are present.
23    ///- Returns `Err(...)` with the usable and non-usable discriminants when one or more required
24    ///  configurations are missing.
25    pub fn check_consistency(
26        enabled_discriminants: &BTreeSet<SignedEntityTypeDiscriminants>,
27        cardano_transactions_signing_config: &Option<CardanoTransactionsSigningConfig>,
28        cardano_blocks_transactions_signing_config: &Option<CardanoBlocksTransactionsSigningConfig>,
29    ) -> Result<(), InconsistentSignedEntityConfigError> {
30        let (usable_discriminants, not_usable_discriminants): (BTreeSet<_>, BTreeSet<_>) =
31            enabled_discriminants
32                .iter()
33                .partition(|discriminant| match discriminant {
34                    SignedEntityTypeDiscriminants::CardanoTransactions => {
35                        cardano_transactions_signing_config.is_some()
36                    }
37                    SignedEntityTypeDiscriminants::CardanoBlocksTransactions => {
38                        cardano_blocks_transactions_signing_config.is_some()
39                    }
40                    // All other discriminants require no additional config and are always usable
41                    SignedEntityTypeDiscriminants::MithrilStakeDistribution
42                    | SignedEntityTypeDiscriminants::CardanoStakeDistribution
43                    | SignedEntityTypeDiscriminants::CardanoImmutableFilesFull
44                    | SignedEntityTypeDiscriminants::CardanoDatabase => true,
45                });
46
47        if not_usable_discriminants.is_empty() {
48            Ok(())
49        } else {
50            Err(InconsistentSignedEntityConfigError {
51                usable_discriminants,
52                not_usable_discriminants,
53            })
54        }
55    }
56}
57
58/// [SignedEntityConfigValidator::check_consistency] error
59#[derive(Error, Debug, Clone, PartialEq, Eq)]
60#[error(
61    "The following signed entity can't be used '{not_usable_discriminants:?}': missing associated signing configuration"
62)]
63pub struct InconsistentSignedEntityConfigError {
64    /// The subset of the allowed discriminants that can be used.
65    pub usable_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
66    /// The discriminants that can't be used because the configuration is inconsistent.
67    pub not_usable_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
68}
69
70#[cfg(test)]
71mod tests {
72    use crate::test::double::Dummy;
73
74    use SignedEntityTypeDiscriminants::*;
75
76    use super::*;
77
78    #[test]
79    fn valid_with_no_discriminants_and_no_additional_config() {
80        assert_eq!(
81            Ok(()),
82            SignedEntityConfigValidator::check_consistency(&BTreeSet::new(), &None, &None)
83        );
84    }
85
86    #[test]
87    fn valid_if_no_discriminants_with_additional_config_are_allowed() {
88        assert_eq!(
89            Ok(()),
90            SignedEntityConfigValidator::check_consistency(
91                &BTreeSet::from([CardanoStakeDistribution, CardanoDatabase]),
92                &None,
93                &None
94            )
95        );
96    }
97
98    #[test]
99    fn valid_if_all_discriminants_with_additional_config_are_allowed_and_their_configs_are_set() {
100        assert_eq!(
101            Ok(()),
102            SignedEntityConfigValidator::check_consistency(
103                &BTreeSet::from([CardanoTransactions, CardanoBlocksTransactions]),
104                &Some(CardanoTransactionsSigningConfig::dummy()),
105                &Some(CardanoBlocksTransactionsSigningConfig::dummy())
106            )
107        );
108    }
109
110    #[test]
111    fn invalid_if_cardano_transactions_is_allowed_but_its_config_is_not_set() {
112        assert_eq!(
113            Err(InconsistentSignedEntityConfigError {
114                usable_discriminants: BTreeSet::new(),
115                not_usable_discriminants: BTreeSet::from([CardanoTransactions])
116            }),
117            SignedEntityConfigValidator::check_consistency(
118                &BTreeSet::from([CardanoTransactions]),
119                &None,
120                &Some(CardanoBlocksTransactionsSigningConfig::dummy()),
121            )
122        );
123    }
124
125    #[test]
126    fn invalid_if_cardano_blocks_transactions_is_allowed_but_its_config_is_not_set() {
127        assert_eq!(
128            Err(InconsistentSignedEntityConfigError {
129                usable_discriminants: BTreeSet::new(),
130                not_usable_discriminants: BTreeSet::from([CardanoBlocksTransactions])
131            }),
132            SignedEntityConfigValidator::check_consistency(
133                &BTreeSet::from([CardanoBlocksTransactions]),
134                &Some(CardanoTransactionsSigningConfig::dummy()),
135                &None,
136            )
137        );
138    }
139
140    #[test]
141    fn invalid_with_all_discriminants_allowed_but_no_additional_configs_set() {
142        let expected_not_usable_discriminants =
143            BTreeSet::from([CardanoTransactions, CardanoBlocksTransactions]);
144
145        assert_eq!(
146            Err(InconsistentSignedEntityConfigError {
147                usable_discriminants: SignedEntityTypeDiscriminants::all()
148                    .difference(&expected_not_usable_discriminants)
149                    .cloned()
150                    .collect(),
151                not_usable_discriminants: expected_not_usable_discriminants
152            }),
153            SignedEntityConfigValidator::check_consistency(
154                &SignedEntityTypeDiscriminants::all(),
155                &None,
156                &None,
157            )
158        );
159    }
160}