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