penumbra_sdk_distributions/component/
rpc.rs1use 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 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 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}