mithril_aggregator/http_server/routes/
root_routes.rs

1use super::middlewares;
2use crate::http_server::routes::router::RouterState;
3use warp::Filter;
4
5pub fn routes(
6    router_state: &RouterState,
7) -> impl Filter<Extract = (impl warp::Reply + use<>,), Error = warp::Rejection> + Clone + use<> {
8    root(router_state)
9}
10
11/// GET /
12fn root(
13    router_state: &RouterState,
14) -> impl Filter<Extract = (impl warp::Reply + use<>,), Error = warp::Rejection> + Clone + use<> {
15    warp::path::end()
16        .and(middlewares::with_logger(router_state))
17        .and(middlewares::with_api_version_provider(router_state))
18        .and(middlewares::extract_config(router_state, |config| {
19            config.allowed_discriminants.clone()
20        }))
21        .and(middlewares::extract_config(router_state, |config| {
22            config.cardano_transactions_prover_max_hashes_allowed_by_request
23        }))
24        .and_then(handlers::root)
25}
26
27mod handlers {
28    use std::collections::BTreeSet;
29    use std::{convert::Infallible, sync::Arc};
30
31    use slog::Logger;
32    use warp::http::StatusCode;
33
34    use mithril_common::api_version::APIVersionProvider;
35    use mithril_common::entities::SignedEntityTypeDiscriminants;
36    use mithril_common::messages::{
37        AggregatorCapabilities, AggregatorFeaturesMessage, CardanoTransactionsProverCapabilities,
38    };
39
40    use crate::http_server::routes::reply::json;
41    use crate::unwrap_to_internal_server_error;
42
43    /// Root
44    pub async fn root(
45        logger: Logger,
46        api_version_provider: Arc<APIVersionProvider>,
47        allowed_signed_entity_type_discriminants: BTreeSet<SignedEntityTypeDiscriminants>,
48        max_hashes_allowed_by_request: usize,
49    ) -> Result<impl warp::Reply, Infallible> {
50        let open_api_version = unwrap_to_internal_server_error!(
51            api_version_provider.compute_current_version(),
52            logger => "root::error"
53        );
54
55        let mut capabilities = AggregatorCapabilities {
56            signed_entity_types: allowed_signed_entity_type_discriminants,
57            cardano_transactions_prover: None,
58        };
59
60        if capabilities
61            .signed_entity_types
62            .contains(&SignedEntityTypeDiscriminants::CardanoTransactions)
63        {
64            capabilities.cardano_transactions_prover =
65                Some(CardanoTransactionsProverCapabilities {
66                    max_hashes_allowed_by_request,
67                });
68        }
69
70        Ok(json(
71            &AggregatorFeaturesMessage {
72                open_api_version: open_api_version.to_string(),
73                documentation_url: env!("CARGO_PKG_HOMEPAGE").to_string(),
74                capabilities,
75            },
76            StatusCode::OK,
77        ))
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use serde_json::Value::Null;
84    use std::collections::BTreeSet;
85    use std::sync::Arc;
86    use warp::http::{Method, StatusCode};
87    use warp::test::request;
88
89    use mithril_api_spec::APISpec;
90    use mithril_common::entities::SignedEntityTypeDiscriminants;
91    use mithril_common::messages::{
92        AggregatorCapabilities, AggregatorFeaturesMessage, CardanoTransactionsProverCapabilities,
93    };
94    use mithril_common::test::double::Dummy;
95
96    use crate::http_server::routes::router::RouterConfig;
97    use crate::initialize_dependencies;
98
99    use super::*;
100
101    fn setup_router(
102        state: RouterState,
103    ) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
104        let cors = warp::cors()
105            .allow_any_origin()
106            .allow_headers(vec!["content-type"])
107            .allow_methods(vec![Method::GET, Method::POST, Method::OPTIONS]);
108
109        warp::any().and(routes(&state).with(cors))
110    }
111
112    #[tokio::test]
113    async fn test_root_route_ok() {
114        let method = Method::GET.as_str();
115        let path = "/";
116        let config = RouterConfig {
117            allowed_discriminants: BTreeSet::from([
118                SignedEntityTypeDiscriminants::CardanoStakeDistribution,
119                SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
120                SignedEntityTypeDiscriminants::MithrilStakeDistribution,
121            ]),
122            ..RouterConfig::dummy()
123        };
124        let dependency_manager = initialize_dependencies!().await;
125
126        let expected_open_api_version = dependency_manager
127            .api_version_provider
128            .clone()
129            .compute_current_version()
130            .unwrap()
131            .to_string();
132
133        let response = request()
134            .method(method)
135            .path(path)
136            .reply(&setup_router(RouterState::new(
137                Arc::new(dependency_manager),
138                config,
139            )))
140            .await;
141
142        let response_body: AggregatorFeaturesMessage =
143            serde_json::from_slice(response.body()).unwrap();
144
145        assert_eq!(response.status(), StatusCode::OK);
146
147        assert_eq!(
148            response_body,
149            AggregatorFeaturesMessage {
150                open_api_version: expected_open_api_version,
151                documentation_url: env!("CARGO_PKG_HOMEPAGE").to_string(),
152                capabilities: AggregatorCapabilities {
153                    signed_entity_types: BTreeSet::from_iter([
154                        SignedEntityTypeDiscriminants::CardanoStakeDistribution,
155                        SignedEntityTypeDiscriminants::CardanoImmutableFilesFull,
156                        SignedEntityTypeDiscriminants::MithrilStakeDistribution,
157                    ]),
158                    cardano_transactions_prover: None,
159                },
160            }
161        );
162
163        APISpec::verify_conformity(
164            APISpec::get_default_spec_file_from(crate::http_server::API_SPEC_LOCATION),
165            method,
166            path,
167            "application/json",
168            &Null,
169            &response,
170            &StatusCode::OK,
171        )
172        .unwrap();
173    }
174
175    #[tokio::test]
176    async fn test_root_route_ok_with_cardano_transactions_enabled() {
177        let method = Method::GET.as_str();
178        let path = "/";
179        let config = RouterConfig {
180            allowed_discriminants: BTreeSet::from([
181                SignedEntityTypeDiscriminants::CardanoTransactions,
182            ]),
183            cardano_transactions_prover_max_hashes_allowed_by_request: 99,
184            ..RouterConfig::dummy()
185        };
186        let dependency_manager = initialize_dependencies!().await;
187
188        let response = request()
189            .method(method)
190            .path(path)
191            .reply(&setup_router(RouterState::new(
192                Arc::new(dependency_manager),
193                config,
194            )))
195            .await;
196
197        let response_body: AggregatorFeaturesMessage =
198            serde_json::from_slice(response.body()).unwrap();
199
200        assert_eq!(response.status(), StatusCode::OK);
201
202        assert_eq!(
203            response_body.capabilities.cardano_transactions_prover,
204            Some(CardanoTransactionsProverCapabilities {
205                max_hashes_allowed_by_request: 99
206            })
207        );
208
209        APISpec::verify_conformity(
210            APISpec::get_default_spec_file_from(crate::http_server::API_SPEC_LOCATION),
211            method,
212            path,
213            "application/json",
214            &Null,
215            &response,
216            &StatusCode::OK,
217        )
218        .unwrap();
219    }
220}