Skip to main content

espresso_api/
handlers.rs

1//! Shared handler functions for API endpoints,
2//! used by both Axum and Tonic APIs.
3
4use serialization_api::v2::*;
5
6use crate::{
7    error::ApiError,
8    v2::{ConsensusApi, DataApi, RewardApi},
9};
10
11pub async fn get_reward_claim_input<S>(
12    state: &S,
13    request: GetRewardClaimInputRequest,
14) -> Result<RewardClaimInput, ApiError>
15where
16    S: RewardApi,
17{
18    let address_string = request.address.clone();
19
20    let address = state
21        .deserialize_address(&request.address)
22        .map_err(ApiError::BadRequest)?;
23
24    let result = state
25        .get_reward_claim_input(address)
26        .await
27        .map_err(ApiError::Internal)?;
28
29    state
30        .serialize_reward_claim_input(&address_string, &result)
31        .map_err(ApiError::Internal)
32}
33
34pub async fn get_reward_balance<S>(
35    state: &S,
36    request: GetRewardBalanceRequest,
37) -> Result<RewardBalance, ApiError>
38where
39    S: RewardApi,
40{
41    let address = state
42        .deserialize_address(&request.address)
43        .map_err(ApiError::BadRequest)?;
44
45    let result = state
46        .get_reward_balance(address)
47        .await
48        .map_err(ApiError::Internal)?;
49
50    state
51        .serialize_reward_balance(&result)
52        .map_err(ApiError::Internal)
53}
54
55pub async fn get_reward_account_proof<S>(
56    state: &S,
57    request: GetRewardAccountProofRequest,
58) -> Result<RewardAccountQueryDataV2, ApiError>
59where
60    S: RewardApi,
61{
62    let address = state
63        .deserialize_address(&request.address)
64        .map_err(ApiError::BadRequest)?;
65
66    let result = state
67        .get_reward_account_proof(address)
68        .await
69        .map_err(ApiError::Internal)?;
70
71    state
72        .serialize_reward_account_query_data(&result)
73        .map_err(ApiError::Internal)
74}
75
76pub async fn get_reward_balances<S>(
77    state: &S,
78    request: GetRewardBalancesRequest,
79) -> Result<RewardBalances, ApiError>
80where
81    S: RewardApi,
82{
83    let result = state
84        .get_reward_balances(request.height, request.offset, request.limit)
85        .await
86        .map_err(ApiError::Internal)?;
87
88    state
89        .serialize_reward_balances(&result)
90        .map_err(ApiError::Internal)
91}
92
93pub async fn get_reward_merkle_tree_v2<S>(
94    state: &S,
95    request: GetRewardMerkleTreeRequest,
96) -> Result<RewardMerkleTreeV2Data, ApiError>
97where
98    S: RewardApi,
99{
100    let result = state
101        .get_reward_merkle_tree_v2(request.height)
102        .await
103        .map_err(ApiError::Internal)?;
104
105    state
106        .serialize_reward_merkle_tree_data(&result)
107        .map_err(ApiError::Internal)
108}
109
110// Data API handlers
111
112pub async fn get_namespace_proof<S>(
113    state: &S,
114    request: GetNamespaceProofRequest,
115) -> Result<GetNamespaceProofResponse, ApiError>
116where
117    S: DataApi,
118{
119    use get_namespace_proof_response::Response;
120
121    match (request.block, request.first, request.last) {
122        (Some(block), None, None) => {
123            let result = state
124                .get_namespace_proof(request.namespace_id, block)
125                .await
126                .map_err(ApiError::Internal)?;
127
128            let serialized = state
129                .serialize_namespace_proof(&result)
130                .map_err(ApiError::Internal)?;
131
132            Ok(GetNamespaceProofResponse {
133                response: Some(Response::Single(serialized)),
134            })
135        },
136        (None, Some(first), Some(last)) => {
137            // Range is inclusive on both ends (first and last)
138            // Internal API expects exclusive end, so add 1
139            let proofs = state
140                .get_namespace_proof_range(request.namespace_id, first, last + 1)
141                .await
142                .map_err(ApiError::Internal)?;
143
144            let serialized_proofs = proofs
145                .iter()
146                .map(|proof| state.serialize_namespace_proof(proof))
147                .collect::<Result<Vec<_>, _>>()
148                .map_err(ApiError::Internal)?;
149
150            Ok(GetNamespaceProofResponse {
151                response: Some(Response::Range(NamespaceProofRangeResponse {
152                    proofs: serialized_proofs,
153                })),
154            })
155        },
156        _ => Err(ApiError::BadRequest(anyhow::anyhow!(
157            "Must specify either 'block' or both 'first' and 'last' query parameters"
158        ))),
159    }
160}
161
162pub async fn get_incorrect_encoding_proof<S>(
163    state: &S,
164    request: GetIncorrectEncodingProofRequest,
165) -> Result<IncorrectEncodingProofResponse, ApiError>
166where
167    S: DataApi,
168{
169    let result = state
170        .get_incorrect_encoding_proof(request.namespace_id, request.block_height)
171        .await
172        .map_err(ApiError::Internal)?;
173
174    state
175        .serialize_incorrect_encoding_proof(&result)
176        .map_err(ApiError::Internal)
177}
178
179// Consensus API handlers
180
181pub async fn get_state_certificate<S>(
182    state: &S,
183    request: GetStateCertificateRequest,
184) -> Result<StateCertificateResponse, ApiError>
185where
186    S: ConsensusApi,
187{
188    let result = state
189        .get_state_certificate(request.epoch)
190        .await
191        .map_err(ApiError::Internal)?;
192
193    state
194        .serialize_state_certificate(&result)
195        .map_err(ApiError::Internal)
196}
197
198pub async fn get_stake_table<S>(
199    state: &S,
200    request: GetStakeTableRequest,
201) -> Result<StakeTableResponse, ApiError>
202where
203    S: ConsensusApi,
204{
205    let result = state
206        .get_stake_table(request.epoch)
207        .await
208        .map_err(ApiError::Internal)?;
209
210    state
211        .serialize_stake_table(&result)
212        .map_err(ApiError::Internal)
213}