penumbra_sdk_distributions/component/
rpc.rs

1use cnidarium::Storage;
2use penumbra_sdk_num::Amount;
3use penumbra_sdk_proto::core::component::distributions::v1::{
4    self as pb, distributions_service_server::DistributionsService,
5};
6use penumbra_sdk_sct::component::clock::EpochRead;
7
8use crate::component::StateReadExt;
9
10pub struct Server {
11    storage: Storage,
12}
13
14impl Server {
15    pub fn new(storage: Storage) -> Self {
16        Self { storage }
17    }
18}
19
20#[tonic::async_trait]
21impl DistributionsService for Server {
22    async fn current_lqt_pool_size(
23        &self,
24        _request: tonic::Request<pb::CurrentLqtPoolSizeRequest>,
25    ) -> Result<tonic::Response<pb::CurrentLqtPoolSizeResponse>, tonic::Status> {
26        // Retrieve latest state snapshot.
27        let state = self.storage.latest_snapshot();
28
29        let current_block_height = state.get_block_height().await.map_err(|e| {
30            tonic::Status::internal(format!("failed to get current block height: {}", e))
31        })?;
32        let current_epoch = state
33            .get_current_epoch()
34            .await
35            .map_err(|e| tonic::Status::internal(format!("failed to get current epoch: {}", e)))?;
36        let epoch_length = current_block_height
37            .checked_sub(current_epoch.start_height)
38            .unwrap_or_else(|| panic!("epoch start height is greater than current block height (epoch_start={}, current_height={}", current_epoch.start_height, current_block_height));
39
40        let lqt_block_reward_rate = state
41            .get_distributions_params()
42            .await
43            .map_err(|e| {
44                tonic::Status::internal(format!("failed to get distributions parameters: {}", e))
45            })?
46            .liquidity_tournament_incentive_per_block as u64;
47
48        let current_lqt_pool_size = lqt_block_reward_rate
49            .checked_mul(epoch_length as u64)
50            .expect("infallible unless issuance is pathological");
51
52        Ok(tonic::Response::new(pb::CurrentLqtPoolSizeResponse {
53            epoch_index: current_epoch.index,
54            pool_size: Some(Amount::from(current_lqt_pool_size).into()),
55        }))
56    }
57
58    async fn lqt_pool_size_by_epoch(
59        &self,
60        request: tonic::Request<pb::LqtPoolSizeByEpochRequest>,
61    ) -> Result<tonic::Response<pb::LqtPoolSizeByEpochResponse>, tonic::Status> {
62        // Retrieve latest state snapshot.
63        let state = self.storage.latest_snapshot();
64        let epoch_index = request.into_inner().epoch;
65        let amount = state
66            .get_lqt_reward_issuance_for_epoch(epoch_index)
67            .await
68            .ok_or_else(|| {
69                tonic::Status::not_found(format!(
70                    "failed to retrieve LQT issuance for epoch {} from non-verifiable storage",
71                    epoch_index,
72                ))
73            })?;
74
75        Ok(tonic::Response::new(pb::LqtPoolSizeByEpochResponse {
76            epoch_index,
77            pool_size: Some(amount.into()),
78        }))
79    }
80}