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::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 balance,
42 epoch: Epoch::ZERO,
43 ownership: ChainOwnership::single(public_key.into()),
44 };
45 ChainDescription::new(origin, config, timestamp)
46}
47
48#[derive(Clone, Debug, Serialize, Deserialize)]
50pub struct GenesisConfig {
51 pub committee: Committee,
53 pub timestamp: Timestamp,
55 pub chains: Vec<ChainDescription>,
57 pub network_name: String,
59}
60
61impl BcsSignable<'_> for GenesisConfig {}
62
63impl GenesisConfig {
64 pub fn new(
66 committee: Committee,
67 timestamp: Timestamp,
68 network_name: String,
69 admin_public_key: AccountPublicKey,
70 admin_balance: Amount,
71 ) -> Self {
72 let admin_chain = make_chain(0, admin_public_key, admin_balance, timestamp);
73 Self {
74 committee,
75 timestamp,
76 chains: vec![admin_chain],
77 network_name,
78 }
79 }
80
81 pub fn add_root_chain(
83 &mut self,
84 public_key: AccountPublicKey,
85 balance: Amount,
86 ) -> ChainDescription {
87 let description = make_chain(
88 u32::try_from(self.chains.len()).expect("more than u32::MAX genesis chains"),
89 public_key,
90 balance,
91 self.timestamp,
92 );
93 self.chains.push(description.clone());
94 description
95 }
96
97 pub fn admin_chain_description(&self) -> &ChainDescription {
99 &self.chains[0]
100 }
101
102 pub fn admin_chain_id(&self) -> ChainId {
104 self.admin_chain_description().id()
105 }
106
107 pub async fn initialize_storage<S>(&self, storage: &mut S) -> Result<(), Error>
109 where
110 S: Storage + Clone + 'static,
111 {
112 if let Some(description) = storage
113 .read_network_description()
114 .await
115 .map_err(linera_chain::ChainError::from)?
116 {
117 if description != self.network_description() {
118 tracing::error!(
119 current_network=?description,
120 new_network=?self.network_description(),
121 "storage already initialized"
122 );
123 return Err(Error::StorageIsAlreadyInitialized(Box::new(description)));
124 }
125 tracing::debug!(?description, "storage already initialized");
126 return Ok(());
127 }
128 let network_description = self.network_description();
129 storage
130 .write_blob(&self.committee_blob())
131 .await
132 .map_err(linera_chain::ChainError::from)?;
133 storage
134 .write_network_description(&network_description)
135 .await
136 .map_err(linera_chain::ChainError::from)?;
137 for description in &self.chains {
138 storage.create_chain(description.clone()).await?;
139 }
140 Ok(())
141 }
142
143 pub fn hash(&self) -> CryptoHash {
145 CryptoHash::new(self)
146 }
147
148 pub fn committee_blob(&self) -> Blob {
150 Blob::new_committee(
151 bcs::to_bytes(&self.committee).expect("serializing a committee should succeed"),
152 )
153 }
154
155 pub fn network_description(&self) -> NetworkDescription {
157 NetworkDescription {
158 name: self.network_name.clone(),
159 genesis_config_hash: CryptoHash::new(self),
160 genesis_timestamp: self.timestamp,
161 genesis_committee_blob_hash: self.committee_blob().id().hash,
162 admin_chain_id: self.admin_chain_id(),
163 }
164 }
165
166 #[cfg(with_testing)]
168 pub fn new_for_testing<B: crate::test_utils::StorageBuilder>(
169 builder: &crate::test_utils::TestBuilder<B>,
170 ) -> Self {
171 let mut genesis_chains = builder.genesis_chains().into_iter();
172 let (admin_public_key, admin_balance) = genesis_chains
173 .next()
174 .expect("should have at least one chain");
175 let mut genesis_config = Self::new(
176 builder.initial_committee.clone(),
177 Timestamp::from(0),
178 "test network".to_string(),
179 admin_public_key,
180 admin_balance,
181 );
182 for (public_key, amount) in genesis_chains {
183 genesis_config.add_root_chain(public_key, amount);
184 }
185 genesis_config
186 }
187}