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