linera_core/
genesis_config.rs1use linera_base::{
6 crypto::{AccountPublicKey, BcsSignable, CryptoHash},
7 data_types::{
8 Amount, Blob, ChainDescription, ChainOrigin, Epoch, InitialChainConfig, NetworkDescription,
9 Timestamp,
10 },
11 identifiers::{AccountOwner, ChainId},
12 ownership::ChainOwnership,
13};
14use linera_execution::committee::Committee;
15use linera_storage::Storage;
16use serde::{Deserialize, Serialize};
17
18#[derive(Debug, thiserror::Error)]
20#[allow(missing_docs)]
21pub enum Error {
22 #[error("I/O error: {0}")]
23 IoError(#[from] std::io::Error),
24 #[error("chain error: {0}")]
25 Chain(#[from] linera_chain::ChainError),
26 #[error("storage is already initialized: {0:?}")]
27 StorageIsAlreadyInitialized(Box<NetworkDescription>),
28 #[error("no admin chain configured")]
29 NoAdminChain,
30}
31
32fn make_chain(
33 index: u32,
34 public_key: AccountPublicKey,
35 balance: Amount,
36 timestamp: Timestamp,
37) -> ChainDescription {
38 let origin = ChainOrigin::Root(index);
39 let config = InitialChainConfig {
40 application_permissions: Default::default(),
41 account: AccountOwner::CHAIN,
42 balance,
43 epoch: Epoch::ZERO,
44 ownership: ChainOwnership::single(public_key.into()),
45 };
46 ChainDescription::new(origin, config, timestamp)
47}
48
49#[derive(Clone, Debug, Serialize, Deserialize)]
51pub struct GenesisConfig {
52 pub committee: Committee,
54 pub timestamp: Timestamp,
56 pub chains: Vec<ChainDescription>,
58 pub network_name: String,
60}
61
62impl BcsSignable<'_> for GenesisConfig {}
63
64impl GenesisConfig {
65 pub fn new(
67 committee: Committee,
68 timestamp: Timestamp,
69 network_name: String,
70 admin_public_key: AccountPublicKey,
71 admin_balance: Amount,
72 ) -> Self {
73 let admin_chain = make_chain(0, admin_public_key, admin_balance, timestamp);
74 Self {
75 committee,
76 timestamp,
77 chains: vec![admin_chain],
78 network_name,
79 }
80 }
81
82 pub fn add_root_chain(
84 &mut self,
85 public_key: AccountPublicKey,
86 balance: Amount,
87 ) -> ChainDescription {
88 let description = make_chain(
89 u32::try_from(self.chains.len()).expect("more than u32::MAX genesis chains"),
90 public_key,
91 balance,
92 self.timestamp,
93 );
94 self.chains.push(description.clone());
95 description
96 }
97
98 pub fn admin_chain_description(&self) -> &ChainDescription {
100 &self.chains[0]
101 }
102
103 pub fn admin_chain_id(&self) -> ChainId {
105 self.admin_chain_description().id()
106 }
107
108 pub async fn initialize_storage<S>(&self, storage: &mut S) -> Result<(), Error>
110 where
111 S: Storage + Clone + 'static,
112 {
113 if let Some(description) = storage
114 .read_network_description()
115 .await
116 .map_err(linera_chain::ChainError::from)?
117 {
118 if description != self.network_description() {
119 tracing::error!(
120 current_network=?description,
121 new_network=?self.network_description(),
122 "storage already initialized"
123 );
124 return Err(Error::StorageIsAlreadyInitialized(Box::new(description)));
125 }
126 tracing::debug!(?description, "storage already initialized");
127 return Ok(());
128 }
129 let network_description = self.network_description();
130 storage
131 .write_blob(&self.committee_blob())
132 .await
133 .map_err(linera_chain::ChainError::from)?;
134 storage
135 .write_network_description(&network_description)
136 .await
137 .map_err(linera_chain::ChainError::from)?;
138 for description in &self.chains {
139 storage.create_chain(description.clone()).await?;
140 }
141 Ok(())
142 }
143
144 pub fn hash(&self) -> CryptoHash {
146 CryptoHash::new(self)
147 }
148
149 pub fn committee_blob(&self) -> Blob {
151 Blob::new_committee(
152 bcs::to_bytes(&self.committee).expect("serializing a committee should succeed"),
153 )
154 }
155
156 pub fn network_description(&self) -> NetworkDescription {
158 NetworkDescription {
159 name: self.network_name.clone(),
160 genesis_config_hash: CryptoHash::new(self),
161 genesis_timestamp: self.timestamp,
162 genesis_committee_blob_hash: self.committee_blob().id().hash,
163 admin_chain_id: self.admin_chain_id(),
164 }
165 }
166
167 #[cfg(with_testing)]
169 pub fn new_for_testing<B: crate::test_utils::StorageBuilder>(
170 builder: &crate::test_utils::TestBuilder<B>,
171 ) -> Self {
172 let mut genesis_chains = builder.genesis_chains().into_iter();
173 let (admin_public_key, admin_balance) = genesis_chains
174 .next()
175 .expect("should have at least one chain");
176 let mut genesis_config = Self::new(
177 builder.initial_committee.clone(),
178 Timestamp::from(0),
179 "test network".to_string(),
180 admin_public_key,
181 admin_balance,
182 );
183 for (public_key, amount) in genesis_chains {
184 genesis_config.add_root_chain(public_key, amount);
185 }
186 genesis_config
187 }
188}