penumbra_sdk_app/
genesis.rs

1use penumbra_sdk_auction::genesis::Content as AuctionContent;
2use penumbra_sdk_community_pool::genesis::Content as CommunityPoolContent;
3use penumbra_sdk_dex::genesis::Content as DexContent;
4use penumbra_sdk_distributions::genesis::Content as DistributionsContent;
5use penumbra_sdk_fee::genesis::Content as FeeContent;
6use penumbra_sdk_funding::genesis::Content as FundingContent;
7use penumbra_sdk_governance::genesis::Content as GovernanceContent;
8use penumbra_sdk_ibc::genesis::Content as IBCContent;
9use penumbra_sdk_proto::{penumbra::core::app::v1 as pb, DomainType};
10use penumbra_sdk_sct::genesis::Content as SctContent;
11use penumbra_sdk_shielded_pool::genesis::Content as ShieldedPoolContent;
12use penumbra_sdk_stake::genesis::Content as StakeContent;
13use serde::{Deserialize, Serialize};
14
15/// The application state at genesis.
16#[derive(Deserialize, Serialize, Debug, Clone)]
17#[serde(try_from = "pb::GenesisAppState", into = "pb::GenesisAppState")]
18#[allow(clippy::large_enum_variant)]
19pub enum AppState {
20    /// The application state at genesis.
21    Content(Content),
22    /// The checkpointed application state at genesis, contains a free-form hash.
23    Checkpoint(Vec<u8>),
24}
25
26impl AppState {
27    pub fn content(&self) -> Option<&Content> {
28        match self {
29            AppState::Content(content) => Some(content),
30            _ => None,
31        }
32    }
33}
34
35#[derive(Deserialize, Serialize, Debug, Clone, Default)]
36#[serde(try_from = "pb::GenesisContent", into = "pb::GenesisContent")]
37pub struct Content {
38    /// The chain ID.
39    pub chain_id: String,
40    /// Community Pool module genesis state.
41    pub community_pool_content: CommunityPoolContent,
42    /// Distributions module genesis state.
43    pub distributions_content: DistributionsContent,
44    /// Fee module genesis state.
45    pub fee_content: FeeContent,
46    /// Funding module genesis state.
47    pub funding_content: FundingContent,
48    /// Governance module genesis state.
49    pub governance_content: GovernanceContent,
50    /// IBC module genesis state.
51    pub ibc_content: IBCContent,
52    // Sct module genesis state.
53    pub sct_content: SctContent,
54    /// Shielded pool module genesis state.
55    pub shielded_pool_content: ShieldedPoolContent,
56    /// Stake module genesis state.
57    pub stake_content: StakeContent,
58    /// Dex component genesis state.
59    pub dex_content: DexContent,
60    /// Auction component genesis state.
61    pub auction_content: AuctionContent,
62}
63
64impl DomainType for Content {
65    type Proto = pb::GenesisContent;
66}
67
68impl Default for AppState {
69    fn default() -> Self {
70        Self::Content(Default::default())
71    }
72}
73
74impl From<AppState> for pb::GenesisAppState {
75    fn from(a: AppState) -> Self {
76        let genesis_state = match a {
77            AppState::Content(c) => {
78                pb::genesis_app_state::GenesisAppState::GenesisContent(c.into())
79            }
80            AppState::Checkpoint(h) => pb::genesis_app_state::GenesisAppState::GenesisCheckpoint(h),
81        };
82
83        pb::GenesisAppState {
84            genesis_app_state: Some(genesis_state),
85        }
86    }
87}
88
89impl From<Content> for pb::GenesisContent {
90    fn from(genesis: Content) -> Self {
91        pb::GenesisContent {
92            chain_id: genesis.chain_id,
93            auction_content: Some(genesis.auction_content.into()),
94            community_pool_content: Some(genesis.community_pool_content.into()),
95            distributions_content: Some(genesis.distributions_content.into()),
96            fee_content: Some(genesis.fee_content.into()),
97            funding_content: Some(genesis.funding_content.into()),
98            governance_content: Some(genesis.governance_content.into()),
99            ibc_content: Some(genesis.ibc_content.into()),
100            sct_content: Some(genesis.sct_content.into()),
101            shielded_pool_content: Some(genesis.shielded_pool_content.into()),
102            stake_content: Some(genesis.stake_content.into()),
103            dex_content: Some(genesis.dex_content.into()),
104        }
105    }
106}
107
108impl TryFrom<pb::GenesisAppState> for AppState {
109    type Error = anyhow::Error;
110
111    fn try_from(msg: pb::GenesisAppState) -> Result<Self, Self::Error> {
112        let state = msg
113            .genesis_app_state
114            .ok_or_else(|| anyhow::anyhow!("missing genesis_app_state field in proto"))?;
115        match state {
116            pb::genesis_app_state::GenesisAppState::GenesisContent(c) => {
117                Ok(AppState::Content(c.try_into()?))
118            }
119            pb::genesis_app_state::GenesisAppState::GenesisCheckpoint(h) => {
120                Ok(AppState::Checkpoint(h))
121            }
122        }
123    }
124}
125
126impl TryFrom<pb::GenesisContent> for Content {
127    type Error = anyhow::Error;
128
129    fn try_from(msg: pb::GenesisContent) -> Result<Self, Self::Error> {
130        Ok(Content {
131            chain_id: msg.chain_id,
132            auction_content: msg
133                .auction_content
134                .ok_or_else(|| anyhow::anyhow!("proto response missing Auction content"))?
135                .try_into()?,
136            community_pool_content: msg
137                .community_pool_content
138                .ok_or_else(|| anyhow::anyhow!("proto response missing Community Pool content"))?
139                .try_into()?,
140            distributions_content: msg
141                .distributions_content
142                .ok_or_else(|| anyhow::anyhow!("proto response missing distributions content"))?
143                .try_into()?,
144            governance_content: msg
145                .governance_content
146                .ok_or_else(|| anyhow::anyhow!("proto response missing governance content"))?
147                .try_into()?,
148            fee_content: msg
149                .fee_content
150                .ok_or_else(|| anyhow::anyhow!("proto response missing fee content"))?
151                .try_into()?,
152            funding_content: msg
153                .funding_content
154                .ok_or_else(|| anyhow::anyhow!("proto response missing funding content"))?
155                .try_into()?,
156            ibc_content: msg
157                .ibc_content
158                .ok_or_else(|| anyhow::anyhow!("proto response missing ibc content"))?
159                .try_into()?,
160            sct_content: msg
161                .sct_content
162                .ok_or_else(|| anyhow::anyhow!("proto response missing sct content"))?
163                .try_into()?,
164            shielded_pool_content: msg
165                .shielded_pool_content
166                .ok_or_else(|| anyhow::anyhow!("proto response missing shielded pool content"))?
167                .try_into()?,
168            stake_content: msg
169                .stake_content
170                .ok_or_else(|| anyhow::anyhow!("proto response missing stake content"))?
171                .try_into()?,
172            dex_content: msg
173                .dex_content
174                .ok_or_else(|| anyhow::anyhow!("proto response missing dex content"))?
175                .try_into()?,
176        })
177    }
178}
179
180impl DomainType for AppState {
181    type Proto = pb::GenesisAppState;
182}
183
184impl Content {
185    pub fn with_chain_id(self, chain_id: String) -> Self {
186        Self { chain_id, ..self }
187    }
188
189    pub fn with_epoch_duration(self, epoch_duration: u64) -> Self {
190        Self {
191            sct_content: penumbra_sdk_sct::genesis::Content {
192                sct_params: penumbra_sdk_sct::params::SctParameters { epoch_duration },
193            },
194            ..self
195        }
196    }
197
198    pub fn with_unbonding_delay(self, unbonding_delay: u64) -> Self {
199        Self {
200            stake_content: penumbra_sdk_stake::genesis::Content {
201                stake_params: penumbra_sdk_stake::params::StakeParameters {
202                    unbonding_delay,
203                    ..self.stake_content.stake_params
204                },
205                ..self.stake_content
206            },
207            ..self
208        }
209    }
210}
211
212#[cfg(test)]
213mod test {
214    use super::*;
215    /// Check that the default implementation of contains zero validators,
216    /// requiring validators to be passed in out of band. N.B. there's also a
217    /// `validators` field in the [`tendermint::Genesis`] struct, which we don't use,
218    /// preferring the AppState definition instead.
219    #[test]
220    fn check_validator_defaults() -> anyhow::Result<()> {
221        let a = Content {
222            ..Default::default()
223        };
224        assert!(a.stake_content.validators.is_empty());
225        Ok(())
226    }
227}