penumbra_sdk_sct/component/
rpc.rs1use cnidarium::Storage;
2use pbjson_types::Timestamp;
3use penumbra_sdk_proto::core::component::sct::v1::query_service_server::QueryService;
4use penumbra_sdk_proto::core::component::sct::v1::{
5 AnchorByHeightRequest, AnchorByHeightResponse, EpochByHeightRequest, EpochByHeightResponse,
6 TimestampByHeightRequest, TimestampByHeightResponse,
7};
8use tonic::Status;
9use tracing::instrument;
10
11use super::clock::EpochRead;
12use super::tree::SctRead;
13
14pub struct Server {
16 storage: Storage,
17}
18
19impl Server {
20 pub fn new(storage: Storage) -> Self {
21 Self { storage }
22 }
23}
24
25#[tonic::async_trait]
26impl QueryService for Server {
27 #[instrument(skip(self, request))]
28 async fn epoch_by_height(
29 &self,
30 request: tonic::Request<EpochByHeightRequest>,
31 ) -> Result<tonic::Response<EpochByHeightResponse>, Status> {
32 let state = self.storage.latest_snapshot();
33
34 let epoch = state
35 .get_epoch_by_height(request.get_ref().height)
36 .await
37 .map_err(|e| tonic::Status::unknown(format!("could not get epoch for height: {e}")))?;
38
39 Ok(tonic::Response::new(EpochByHeightResponse {
40 epoch: Some(epoch.into()),
41 }))
42 }
43
44 #[instrument(skip(self, request))]
45 async fn anchor_by_height(
46 &self,
47 request: tonic::Request<AnchorByHeightRequest>,
48 ) -> Result<tonic::Response<AnchorByHeightResponse>, Status> {
49 let state = self.storage.latest_snapshot();
50
51 let height = request.get_ref().height;
52 let anchor = state.get_anchor_by_height(height).await.map_err(|e| {
53 tonic::Status::unknown(format!("could not get anchor for height {height}: {e}"))
54 })?;
55
56 Ok(tonic::Response::new(AnchorByHeightResponse {
57 anchor: anchor.map(Into::into),
58 }))
59 }
60
61 #[instrument(skip(self, request))]
62 async fn timestamp_by_height(
63 &self,
64 request: tonic::Request<TimestampByHeightRequest>,
65 ) -> Result<tonic::Response<TimestampByHeightResponse>, Status> {
66 let state = self.storage.latest_snapshot();
67
68 let height = request.get_ref().height;
69 let block_time = state.get_block_timestamp(height).await.map_err(|e| {
70 tonic::Status::unknown(format!("could not get timestamp for height {height}: {e}"))
71 })?;
72 let timestamp = chrono::DateTime::parse_from_rfc3339(block_time.to_rfc3339().as_str())
73 .expect("timestamp should roundtrip to string");
74
75 Ok(tonic::Response::new(TimestampByHeightResponse {
76 timestamp: Some(Timestamp {
77 seconds: timestamp.timestamp(),
78 nanos: timestamp.timestamp_subsec_nanos() as i32,
79 }),
80 }))
81 }
82}