1use std::iter::IntoIterator;
6
7use linera_base::{
8 crypto::{AccountPublicKey, BcsSignable, CryptoHash, ValidatorPublicKey, ValidatorSecretKey},
9 data_types::{
10 Amount, Blob, ChainDescription, ChainOrigin, Epoch, InitialChainConfig, NetworkDescription,
11 Timestamp,
12 },
13 identifiers::ChainId,
14 ownership::ChainOwnership,
15};
16use linera_execution::{
17 committee::{Committee, ValidatorState},
18 ResourceControlPolicy,
19};
20use linera_rpc::config::{ValidatorInternalNetworkConfig, ValidatorPublicNetworkConfig};
21use linera_storage::Storage;
22use serde::{Deserialize, Serialize};
23
24#[derive(Debug, thiserror::Error)]
25pub enum Error {
26 #[error("I/O error: {0}")]
27 IoError(#[from] std::io::Error),
28 #[error("chain error: {0}")]
29 Chain(#[from] linera_chain::ChainError),
30 #[error("storage is already initialized: {0:?}")]
31 StorageIsAlreadyInitialized(Box<NetworkDescription>),
32 #[error("no admin chain configured")]
33 NoAdminChain,
34}
35
36#[derive(Clone, Debug, Serialize, Deserialize)]
38pub struct ValidatorConfig {
39 pub public_key: ValidatorPublicKey,
41 pub account_key: AccountPublicKey,
43 pub network: ValidatorPublicNetworkConfig,
45}
46
47#[derive(Serialize, Deserialize)]
49pub struct ValidatorServerConfig {
50 pub validator: ValidatorConfig,
51 pub validator_secret: ValidatorSecretKey,
52 pub internal_network: ValidatorInternalNetworkConfig,
53}
54
55#[derive(Debug, Default, Clone, Deserialize, Serialize)]
57pub struct CommitteeConfig {
58 pub validators: Vec<ValidatorConfig>,
59}
60
61impl CommitteeConfig {
62 pub fn into_committee(self, policy: ResourceControlPolicy) -> Committee {
63 let validators = self
64 .validators
65 .into_iter()
66 .map(|v| {
67 (
68 v.public_key,
69 ValidatorState {
70 network_address: v.network.to_string(),
71 votes: 100,
72 account_public_key: v.account_key,
73 },
74 )
75 })
76 .collect();
77 Committee::new(validators, policy)
78 }
79}
80
81#[derive(Clone, Debug, Serialize, Deserialize)]
82pub struct GenesisConfig {
83 pub committee: Committee,
84 pub timestamp: Timestamp,
85 pub chains: Vec<ChainDescription>,
86 pub network_name: String,
87}
88
89impl BcsSignable<'_> for GenesisConfig {}
90
91fn make_chain(
92 index: u32,
93 public_key: AccountPublicKey,
94 balance: Amount,
95 timestamp: Timestamp,
96) -> ChainDescription {
97 let origin = ChainOrigin::Root(index);
98 let config = InitialChainConfig {
99 application_permissions: Default::default(),
100 balance,
101 min_active_epoch: Epoch::ZERO,
102 max_active_epoch: Epoch::ZERO,
103 epoch: Epoch::ZERO,
104 ownership: ChainOwnership::single(public_key.into()),
105 };
106 ChainDescription::new(origin, config, timestamp)
107}
108
109impl GenesisConfig {
110 pub fn new(
112 committee: CommitteeConfig,
113 timestamp: Timestamp,
114 policy: ResourceControlPolicy,
115 network_name: String,
116 admin_public_key: AccountPublicKey,
117 admin_balance: Amount,
118 ) -> Self {
119 let committee = committee.into_committee(policy);
120 let admin_chain = make_chain(0, admin_public_key, admin_balance, timestamp);
121 Self {
122 committee,
123 timestamp,
124 chains: vec![admin_chain],
125 network_name,
126 }
127 }
128
129 pub fn add_root_chain(
130 &mut self,
131 public_key: AccountPublicKey,
132 balance: Amount,
133 ) -> ChainDescription {
134 let description = make_chain(
135 self.chains.len() as u32,
136 public_key,
137 balance,
138 self.timestamp,
139 );
140 self.chains.push(description.clone());
141 description
142 }
143
144 pub fn admin_chain_description(&self) -> &ChainDescription {
145 &self.chains[0]
146 }
147
148 pub fn admin_id(&self) -> ChainId {
149 self.admin_chain_description().id()
150 }
151
152 pub async fn initialize_storage<S>(&self, storage: &mut S) -> Result<(), Error>
153 where
154 S: Storage + Clone + 'static,
155 {
156 if let Some(description) = storage
157 .read_network_description()
158 .await
159 .map_err(linera_chain::ChainError::from)?
160 {
161 if description != self.network_description() {
162 tracing::error!(
164 current_network=?description,
165 new_network=?self.network_description(),
166 "storage already initialized"
167 );
168 return Err(Error::StorageIsAlreadyInitialized(Box::new(description)));
169 }
170 tracing::debug!(?description, "storage already initialized");
171 return Ok(());
172 }
173 let network_description = self.network_description();
174 storage
175 .write_blob(&self.committee_blob())
176 .await
177 .map_err(linera_chain::ChainError::from)?;
178 storage
179 .write_network_description(&network_description)
180 .await
181 .map_err(linera_chain::ChainError::from)?;
182 for description in &self.chains {
183 storage.create_chain(description.clone()).await?;
184 }
185 Ok(())
186 }
187
188 pub fn hash(&self) -> CryptoHash {
189 CryptoHash::new(self)
190 }
191
192 pub fn committee_blob(&self) -> Blob {
193 Blob::new_committee(
194 bcs::to_bytes(&self.committee).expect("serializing a committee should succeed"),
195 )
196 }
197
198 pub fn network_description(&self) -> NetworkDescription {
199 NetworkDescription {
200 name: self.network_name.clone(),
201 genesis_config_hash: CryptoHash::new(self),
202 genesis_timestamp: self.timestamp,
203 genesis_committee_blob_hash: self.committee_blob().id().hash,
204 admin_chain_id: self.admin_id(),
205 }
206 }
207}
208
209#[cfg(with_testing)]
210mod test {
211 use linera_base::data_types::Timestamp;
212 use linera_core::test_utils::{MemoryStorageBuilder, TestBuilder};
213 use linera_rpc::{
214 config::{NetworkProtocol, ValidatorPublicNetworkPreConfig},
215 simple::TransportProtocol,
216 };
217
218 use super::*;
219 use crate::config::{CommitteeConfig, GenesisConfig, ValidatorConfig};
220
221 impl GenesisConfig {
222 pub fn new_testing(builder: &TestBuilder<MemoryStorageBuilder>) -> Self {
224 let network = ValidatorPublicNetworkPreConfig {
225 protocol: NetworkProtocol::Simple(TransportProtocol::Tcp),
226 host: "localhost".to_string(),
227 port: 8080,
228 };
229 let validators = builder
230 .initial_committee
231 .validators()
232 .iter()
233 .map(|(public_key, state)| ValidatorConfig {
234 public_key: *public_key,
235 network: network.clone(),
236 account_key: state.account_public_key,
237 })
238 .collect();
239 let mut genesis_chains = builder.genesis_chains().into_iter();
240 let (admin_public_key, admin_balance) = genesis_chains
241 .next()
242 .expect("should have at least one chain");
243 let mut genesis_config = Self::new(
244 CommitteeConfig { validators },
245 Timestamp::from(0),
246 builder.initial_committee.policy().clone(),
247 "test network".to_string(),
248 admin_public_key,
249 admin_balance,
250 );
251 for (public_key, amount) in genesis_chains {
252 genesis_config.add_root_chain(public_key, amount);
253 }
254 genesis_config
255 }
256 }
257}