penumbra_sdk_sct/component/
sct.rs

1use std::sync::Arc;
2
3use anyhow::{anyhow, Result};
4use async_trait::async_trait;
5use cnidarium::{StateRead, StateWrite};
6use cnidarium_component::Component;
7use penumbra_sdk_proto::{StateReadProto, StateWriteProto};
8use tendermint::v0_37::abci;
9use tracing::instrument;
10
11use crate::{epoch::Epoch, genesis, params::SctParameters, state_key};
12
13use super::clock::EpochManager;
14
15pub struct Sct {}
16
17#[async_trait]
18impl Component for Sct {
19    type AppState = genesis::Content;
20
21    #[instrument(name = "sct_component", skip(state, app_state))]
22    async fn init_chain<S: StateWrite>(mut state: S, app_state: Option<&Self::AppState>) {
23        match app_state {
24            Some(genesis) => {
25                state.put_sct_params(genesis.sct_params.clone());
26                state.put_block_height(0);
27                state.put_epoch_by_height(
28                    0,
29                    Epoch {
30                        index: 0,
31                        start_height: 0,
32                    },
33                );
34
35                // We need to set the epoch for the first block as well, since we set
36                // the epoch by height in end_block, and end_block isn't called after init_chain.
37                state.put_epoch_by_height(
38                    1,
39                    Epoch {
40                        index: 0,
41                        start_height: 0,
42                    },
43                );
44            }
45            None => { /* no-op until an upgrade occurs */ }
46        }
47    }
48
49    #[instrument(name = "sct_component", skip(state, begin_block))]
50    async fn begin_block<S: StateWrite + 'static>(
51        state: &mut Arc<S>,
52        begin_block: &abci::request::BeginBlock,
53    ) {
54        let state = Arc::get_mut(state).expect("there's only one reference to the state");
55        state.put_block_height(begin_block.header.height.into());
56        state.put_block_timestamp(begin_block.header.height.into(), begin_block.header.time);
57    }
58
59    #[instrument(name = "sct_component", skip(_state, _end_block))]
60    async fn end_block<S: StateWrite + 'static>(
61        _state: &mut Arc<S>,
62        _end_block: &abci::request::EndBlock,
63    ) {
64    }
65
66    #[instrument(name = "sct_component", skip(_state))]
67    async fn end_epoch<S: StateWrite + 'static>(_state: &mut Arc<S>) -> anyhow::Result<()> {
68        Ok(())
69    }
70}
71
72/// This trait provides read access to configuration data for the component.
73#[async_trait]
74pub trait StateReadExt: StateRead {
75    /// Gets the SCT parameters from the state.
76    async fn get_sct_params(&self) -> Result<SctParameters> {
77        self.get(state_key::config::sct_params())
78            .await?
79            .ok_or_else(|| anyhow!("Missing SctParameters"))
80    }
81
82    /// Fetch the epoch duration parameter (measured in blocks).
83    ///
84    /// # Errors
85    /// Returns an error if the Sct parameters are missing.
86    async fn get_epoch_duration_parameter(&self) -> Result<u64> {
87        self.get_sct_params()
88            .await
89            .map(|params| params.epoch_duration)
90    }
91}
92
93impl<T: StateRead + ?Sized> StateReadExt for T {}
94
95/// This trait provides write access to configuration data for the component.
96#[async_trait]
97pub trait StateWriteExt: StateWrite {
98    fn put_sct_params(&mut self, params: SctParameters) {
99        self.put(state_key::config::sct_params().to_string(), params);
100    }
101}
102
103impl<T: StateWrite + ?Sized> StateWriteExt for T {}