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