Skip to main content

auths_infra_http/
witness_client.rs

1use auths_core::ports::network::{NetworkError, WitnessClient};
2use auths_verifier::keri::Prefix;
3use std::future::Future;
4use std::time::Duration;
5
6use crate::default_client_builder;
7use crate::request::{
8    build_get_request, build_post_request, execute_request, parse_response_bytes,
9};
10
11/// HTTP-backed implementation of `WitnessClient`.
12///
13/// Communicates with KERI witness servers to submit events and
14/// query receipts.
15///
16/// Usage:
17/// ```ignore
18/// use auths_infra_http::HttpWitnessClient;
19///
20/// let client = HttpWitnessClient::new(std::time::Duration::from_secs(5));
21/// let receipt = client.submit_event("http://witness:3000", &event_bytes).await?;
22/// ```
23pub struct HttpWitnessClient {
24    client: reqwest::Client,
25}
26
27impl HttpWitnessClient {
28    // INVARIANT: reqwest builder with these settings cannot fail
29    #[allow(clippy::expect_used)]
30    pub fn new(timeout: Duration) -> Self {
31        Self {
32            client: default_client_builder()
33                .timeout(timeout)
34                .build()
35                .expect("failed to build reqwest client"),
36        }
37    }
38}
39
40impl WitnessClient for HttpWitnessClient {
41    fn submit_event(
42        &self,
43        endpoint: &str,
44        event: &[u8],
45    ) -> impl Future<Output = Result<Vec<u8>, NetworkError>> + Send {
46        let url = format!("{}/witness/events", endpoint.trim_end_matches('/'));
47        let request = build_post_request(&self.client, &url, event.to_vec());
48
49        async move {
50            let response = execute_request(request, endpoint).await?;
51            parse_response_bytes(response, &url).await
52        }
53    }
54
55    fn query_receipts(
56        &self,
57        endpoint: &str,
58        prefix: &Prefix,
59    ) -> impl Future<Output = Result<Vec<Vec<u8>>, NetworkError>> + Send {
60        let url = format!(
61            "{}/witness/{}/receipts",
62            endpoint.trim_end_matches('/'),
63            prefix
64        );
65        let request = build_get_request(&self.client, &url);
66
67        async move {
68            let response = execute_request(request, endpoint).await?;
69            let bytes = parse_response_bytes(response, &url).await?;
70            let receipts: Vec<Vec<u8>> =
71                serde_json::from_slice(&bytes).map_err(|e| NetworkError::InvalidResponse {
72                    detail: e.to_string(),
73                })?;
74            Ok(receipts)
75        }
76    }
77}