mithril_client/certificate_client/
mod.rs

1//! A client which retrieves and validates certificates from an Aggregator.
2//!
3//! In order to do so it defines a [CertificateClient] exposes the following features:
4//!  - [get][CertificateClient::get]: get a certificate data from its hash
5//!  - [list][CertificateClient::list]: get the list of available certificates
6//!  - [verify_chain][CertificateClient::verify_chain]: verify a certificate chain
7//!
8//! # Get a certificate
9//!
10//! To get a certificate using the [ClientBuilder][crate::client::ClientBuilder].
11//!
12//! ```no_run
13//! # async fn run() -> mithril_client::MithrilResult<()> {
14//! use mithril_client::ClientBuilder;
15//!
16//! let client = ClientBuilder::aggregator("YOUR_AGGREGATOR_ENDPOINT", "YOUR_GENESIS_VERIFICATION_KEY").build()?;
17//! let certificate = client.certificate().get("CERTIFICATE_HASH").await?.unwrap();
18//!
19//! println!("Certificate hash={}, signed_message={}", certificate.hash, certificate.signed_message);
20//! #    Ok(())
21//! # }
22//! ```
23//!
24//! # List available certificates
25//!
26//! To list available certificates using the [ClientBuilder][crate::client::ClientBuilder].
27//!
28//! ```no_run
29//! # async fn run() -> mithril_client::MithrilResult<()> {
30//! use mithril_client::ClientBuilder;
31//!
32//! let client = mithril_client::ClientBuilder::aggregator("YOUR_AGGREGATOR_ENDPOINT", "YOUR_GENESIS_VERIFICATION_KEY").build()?;
33//! let certificates = client.certificate().list().await?;
34//!
35//! for certificate in certificates {
36//!     println!("Certificate hash={}, signed_message={}", certificate.hash, certificate.signed_message);
37//! }
38//! #    Ok(())
39//! # }
40//! ```
41//!
42//! # Validate a certificate chain
43//!
44//! To validate a certificate using the [ClientBuilder][crate::client::ClientBuilder].
45//!
46//! ```no_run
47//! # async fn run() -> mithril_client::MithrilResult<()> {
48//! use mithril_client::ClientBuilder;
49//!
50//! let client = ClientBuilder::aggregator("YOUR_AGGREGATOR_ENDPOINT", "YOUR_GENESIS_VERIFICATION_KEY").build()?;
51//! let certificate = client.certificate().verify_chain("CERTIFICATE_HASH").await?;
52//!
53//! println!("Chain of Certificate (hash: {}) is valid", certificate.hash);
54//! #    Ok(())
55//! # }
56//! ```
57
58mod api;
59mod fetch;
60mod verify;
61#[cfg(feature = "unstable")]
62mod verify_cache;
63
64pub use api::*;
65pub use verify::MithrilCertificateVerifier;
66#[cfg(feature = "unstable")]
67pub use verify_cache::MemoryCertificateVerifierCache;
68
69#[cfg(test)]
70pub(crate) mod tests_utils {
71    use mithril_common::crypto_helper::ProtocolGenesisVerificationKey;
72    use mithril_common::entities::Certificate;
73    use mithril_common::messages::CertificateMessage;
74    use mockall::predicate::eq;
75    use std::sync::Arc;
76
77    use crate::aggregator_client::{AggregatorRequest, MockAggregatorClient};
78    use crate::feedback::{FeedbackReceiver, FeedbackSender};
79    use crate::test_utils::TestLogger;
80
81    use super::*;
82
83    #[derive(Default)]
84    pub(crate) struct CertificateClientTestBuilder {
85        aggregator_client: MockAggregatorClient,
86        genesis_verification_key: Option<String>,
87        feedback_receivers: Vec<Arc<dyn FeedbackReceiver>>,
88        #[cfg(feature = "unstable")]
89        verifier_cache: Option<Arc<dyn CertificateVerifierCache>>,
90    }
91
92    impl CertificateClientTestBuilder {
93        pub fn config_aggregator_client_mock(
94            mut self,
95            config: impl FnOnce(&mut MockAggregatorClient),
96        ) -> Self {
97            config(&mut self.aggregator_client);
98            self
99        }
100
101        pub fn with_genesis_verification_key(
102            mut self,
103            genesis_verification_key: ProtocolGenesisVerificationKey,
104        ) -> Self {
105            self.genesis_verification_key = Some(genesis_verification_key.try_into().unwrap());
106            self
107        }
108
109        pub fn add_feedback_receiver(
110            mut self,
111            feedback_receiver: Arc<dyn FeedbackReceiver>,
112        ) -> Self {
113            self.feedback_receivers.push(feedback_receiver);
114            self
115        }
116
117        #[cfg(feature = "unstable")]
118        pub fn with_verifier_cache(
119            mut self,
120            verifier_cache: Arc<dyn CertificateVerifierCache>,
121        ) -> Self {
122            self.verifier_cache = Some(verifier_cache);
123            self
124        }
125
126        /// Builds a new [CertificateClient] with the given configuration.
127        ///
128        /// If no genesis verification key is provided, a [MockCertificateVerifier] will be used,
129        /// else a [MithrilCertificateVerifier] will be used.
130        pub fn build(self) -> CertificateClient {
131            let logger = TestLogger::stdout();
132            let aggregator_client = Arc::new(self.aggregator_client);
133
134            let certificate_verifier: Arc<dyn CertificateVerifier> =
135                match self.genesis_verification_key {
136                    None => Arc::new(MockCertificateVerifier::new()),
137                    Some(genesis_verification_key) => Arc::new(
138                        MithrilCertificateVerifier::new(
139                            aggregator_client.clone(),
140                            &genesis_verification_key,
141                            FeedbackSender::new(&self.feedback_receivers),
142                            #[cfg(feature = "unstable")]
143                            self.verifier_cache,
144                            logger.clone(),
145                        )
146                        .unwrap(),
147                    ),
148                };
149
150            CertificateClient::new(aggregator_client.clone(), certificate_verifier, logger)
151        }
152    }
153
154    impl MockAggregatorClient {
155        pub(crate) fn expect_certificate_chain(&mut self, certificate_chain: Vec<Certificate>) {
156            for certificate in certificate_chain {
157                let hash = certificate.hash.clone();
158                let message = serde_json::to_string(
159                    &TryInto::<CertificateMessage>::try_into(certificate).unwrap(),
160                )
161                .unwrap();
162                self.expect_get_content()
163                    .with(eq(AggregatorRequest::GetCertificate { hash }))
164                    .once()
165                    .returning(move |_| Ok(message.to_owned()));
166            }
167        }
168    }
169}