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::feedback::{FeedbackReceiver, FeedbackSender};
78    use crate::test_utils::TestLogger;
79
80    use super::*;
81
82    #[derive(Default)]
83    pub(crate) struct CertificateClientTestBuilder {
84        aggregator_requester: MockCertificateAggregatorRequest,
85        genesis_verification_key: Option<String>,
86        feedback_receivers: Vec<Arc<dyn FeedbackReceiver>>,
87        #[cfg(feature = "unstable")]
88        verifier_cache: Option<Arc<dyn CertificateVerifierCache>>,
89    }
90
91    impl CertificateClientTestBuilder {
92        pub fn config_aggregator_requester_mock(
93            mut self,
94            config: impl FnOnce(&mut MockCertificateAggregatorRequest),
95        ) -> Self {
96            config(&mut self.aggregator_requester);
97            self
98        }
99
100        pub fn with_genesis_verification_key(
101            mut self,
102            genesis_verification_key: ProtocolGenesisVerificationKey,
103        ) -> Self {
104            self.genesis_verification_key = Some(genesis_verification_key.try_into().unwrap());
105            self
106        }
107
108        pub fn add_feedback_receiver(
109            mut self,
110            feedback_receiver: Arc<dyn FeedbackReceiver>,
111        ) -> Self {
112            self.feedback_receivers.push(feedback_receiver);
113            self
114        }
115
116        #[cfg(feature = "unstable")]
117        pub fn with_verifier_cache(
118            mut self,
119            verifier_cache: Arc<dyn CertificateVerifierCache>,
120        ) -> Self {
121            self.verifier_cache = Some(verifier_cache);
122            self
123        }
124
125        /// Builds a new [CertificateClient] with the given configuration.
126        ///
127        /// If no genesis verification key is provided, a [MockCertificateVerifier] will be used,
128        /// else a [MithrilCertificateVerifier] will be used.
129        pub fn build(self) -> CertificateClient {
130            let logger = TestLogger::stdout();
131            let aggregator_client = Arc::new(self.aggregator_requester);
132
133            let certificate_verifier: Arc<dyn CertificateVerifier> =
134                match self.genesis_verification_key {
135                    None => Arc::new(MockCertificateVerifier::new()),
136                    Some(genesis_verification_key) => Arc::new(
137                        MithrilCertificateVerifier::new(
138                            aggregator_client.clone(),
139                            &genesis_verification_key,
140                            FeedbackSender::new(&self.feedback_receivers),
141                            #[cfg(feature = "unstable")]
142                            self.verifier_cache,
143                            logger.clone(),
144                        )
145                        .unwrap(),
146                    ),
147                };
148
149            CertificateClient::new(aggregator_client.clone(), certificate_verifier, logger)
150        }
151    }
152
153    impl MockCertificateAggregatorRequest {
154        pub(crate) fn expect_certificate_chain(&mut self, certificate_chain: Vec<Certificate>) {
155            for certificate in certificate_chain {
156                let hash = certificate.hash.clone();
157                let message: CertificateMessage = certificate.try_into().unwrap();
158
159                self.expect_get_by_hash()
160                    .with(eq(hash))
161                    .once()
162                    .returning(move |_| Ok(Some(message.to_owned())));
163            }
164        }
165    }
166}