Skip to main content

espresso_api/
tonic.rs

1//! gRPC service implementation
2//!
3//! Thin wrapper around shared handler functions from the handlers module.
4//! All business logic is in handlers, this just adapts to the tonic interface.
5
6use serialization_api::v2::{
7    GetIncorrectEncodingProofRequest, GetNamespaceProofRequest, GetNamespaceProofResponse,
8    GetRewardAccountProofRequest, GetRewardBalanceRequest, GetRewardBalancesRequest,
9    GetRewardClaimInputRequest, GetRewardMerkleTreeRequest, GetStakeTableRequest,
10    GetStateCertificateRequest, IncorrectEncodingProofResponse, RewardAccountQueryDataV2,
11    RewardBalance, RewardBalances, RewardClaimInput, RewardMerkleTreeV2Data, StakeTableResponse,
12    StateCertificateResponse,
13};
14use tonic::{Request, Response, Status};
15
16use crate::{
17    error::ApiError,
18    handlers,
19    proto::{
20        consensus_service_server::{ConsensusService, ConsensusServiceServer},
21        data_service_server::{DataService, DataServiceServer},
22        reward_service_server::{RewardService, RewardServiceServer},
23    },
24    v2,
25};
26
27/// Convert ApiError to tonic::Status with proper status code mapping
28fn map_error(err: ApiError) -> Status {
29    match err {
30        ApiError::BadRequest(e) => Status::invalid_argument(e.to_string()),
31        ApiError::NotFound(e) => Status::not_found(e.to_string()),
32        ApiError::Internal(e) => Status::internal(e.to_string()),
33    }
34}
35
36/// gRPC reward service implementation wrapping a RewardApi implementation
37pub struct RewardServiceImpl<S> {
38    state: S,
39}
40
41impl<S> RewardServiceImpl<S> {
42    pub fn new(state: S) -> Self {
43        Self { state }
44    }
45}
46
47#[tonic::async_trait]
48impl<S> RewardService for RewardServiceImpl<S>
49where
50    S: v2::RewardApi + Send + Sync + 'static,
51{
52    async fn get_reward_claim_input(
53        &self,
54        request: Request<GetRewardClaimInputRequest>,
55    ) -> Result<Response<RewardClaimInput>, Status> {
56        handlers::get_reward_claim_input(&self.state, request.into_inner())
57            .await
58            .map(Response::new)
59            .map_err(map_error)
60    }
61
62    async fn get_reward_balance(
63        &self,
64        request: Request<GetRewardBalanceRequest>,
65    ) -> Result<Response<RewardBalance>, Status> {
66        handlers::get_reward_balance(&self.state, request.into_inner())
67            .await
68            .map(Response::new)
69            .map_err(map_error)
70    }
71
72    async fn get_reward_account_proof(
73        &self,
74        request: Request<GetRewardAccountProofRequest>,
75    ) -> Result<Response<RewardAccountQueryDataV2>, Status> {
76        handlers::get_reward_account_proof(&self.state, request.into_inner())
77            .await
78            .map(Response::new)
79            .map_err(map_error)
80    }
81
82    async fn get_reward_balances(
83        &self,
84        request: Request<GetRewardBalancesRequest>,
85    ) -> Result<Response<RewardBalances>, Status> {
86        handlers::get_reward_balances(&self.state, request.into_inner())
87            .await
88            .map(Response::new)
89            .map_err(map_error)
90    }
91
92    async fn get_reward_merkle_tree_v2(
93        &self,
94        request: Request<GetRewardMerkleTreeRequest>,
95    ) -> Result<Response<RewardMerkleTreeV2Data>, Status> {
96        handlers::get_reward_merkle_tree_v2(&self.state, request.into_inner())
97            .await
98            .map(Response::new)
99            .map_err(map_error)
100    }
101}
102
103/// Create the reward gRPC service
104pub fn create_reward_service<S>(state: S) -> RewardServiceServer<RewardServiceImpl<S>>
105where
106    S: v2::RewardApi + Send + Sync + Clone + 'static,
107{
108    RewardServiceServer::new(RewardServiceImpl::new(state))
109}
110
111/// gRPC data service implementation wrapping a DataApi implementation
112pub struct DataServiceImpl<S> {
113    state: S,
114}
115
116impl<S> DataServiceImpl<S> {
117    #[allow(dead_code)]
118    pub fn new(state: S) -> Self {
119        Self { state }
120    }
121}
122
123#[tonic::async_trait]
124impl<S> DataService for DataServiceImpl<S>
125where
126    S: v2::DataApi + Send + Sync + 'static,
127{
128    async fn get_namespace_proof(
129        &self,
130        request: Request<GetNamespaceProofRequest>,
131    ) -> Result<Response<GetNamespaceProofResponse>, Status> {
132        handlers::get_namespace_proof(&self.state, request.into_inner())
133            .await
134            .map(Response::new)
135            .map_err(map_error)
136    }
137
138    async fn get_incorrect_encoding_proof(
139        &self,
140        request: Request<GetIncorrectEncodingProofRequest>,
141    ) -> Result<Response<IncorrectEncodingProofResponse>, Status> {
142        handlers::get_incorrect_encoding_proof(&self.state, request.into_inner())
143            .await
144            .map(Response::new)
145            .map_err(map_error)
146    }
147}
148
149/// Create the data gRPC service
150#[allow(dead_code)]
151pub fn create_data_service<S>(state: S) -> DataServiceServer<DataServiceImpl<S>>
152where
153    S: v2::DataApi + Send + Sync + Clone + 'static,
154{
155    DataServiceServer::new(DataServiceImpl::new(state))
156}
157
158/// gRPC consensus service implementation wrapping a ConsensusApi implementation
159pub struct ConsensusServiceImpl<S> {
160    state: S,
161}
162
163impl<S> ConsensusServiceImpl<S> {
164    #[allow(dead_code)]
165    pub fn new(state: S) -> Self {
166        Self { state }
167    }
168}
169
170#[tonic::async_trait]
171impl<S> ConsensusService for ConsensusServiceImpl<S>
172where
173    S: v2::ConsensusApi + Send + Sync + 'static,
174{
175    async fn get_state_certificate(
176        &self,
177        request: Request<GetStateCertificateRequest>,
178    ) -> Result<Response<StateCertificateResponse>, Status> {
179        handlers::get_state_certificate(&self.state, request.into_inner())
180            .await
181            .map(Response::new)
182            .map_err(map_error)
183    }
184
185    async fn get_stake_table(
186        &self,
187        request: Request<GetStakeTableRequest>,
188    ) -> Result<Response<StakeTableResponse>, Status> {
189        handlers::get_stake_table(&self.state, request.into_inner())
190            .await
191            .map(Response::new)
192            .map_err(map_error)
193    }
194}
195
196/// Create the consensus gRPC service
197#[allow(dead_code)]
198pub fn create_consensus_service<S>(state: S) -> ConsensusServiceServer<ConsensusServiceImpl<S>>
199where
200    S: v2::ConsensusApi + Send + Sync + Clone + 'static,
201{
202    ConsensusServiceServer::new(ConsensusServiceImpl::new(state))
203}