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_persistent as persistent;
21use linera_rpc::config::{ValidatorInternalNetworkConfig, ValidatorPublicNetworkConfig};
22use linera_storage::Storage;
23use serde::{Deserialize, Serialize};
24
25#[derive(Debug, thiserror::Error)]
26pub enum Error {
27 #[error("I/O error: {0}")]
28 IoError(#[from] std::io::Error),
29 #[error("chain error: {0}")]
30 Chain(#[from] linera_chain::ChainError),
31 #[error("persistence error: {0}")]
32 Persistence(Box<dyn std::error::Error + Send + Sync>),
33 #[error("storage is already initialized: {0:?}")]
34 StorageIsAlreadyInitialized(Box<NetworkDescription>),
35 #[error("no admin chain configured")]
36 NoAdminChain,
37}
38
39use crate::util;
40
41util::impl_from_dynamic!(Error:Persistence, persistent::memory::Error);
42#[cfg(web)]
43util::impl_from_dynamic!(Error:Persistence, persistent::indexed_db::Error);
44#[cfg(feature = "fs")]
45util::impl_from_dynamic!(Error:Persistence, persistent::file::Error);
46
47#[derive(Clone, Debug, Serialize, Deserialize)]
49pub struct ValidatorConfig {
50 pub public_key: ValidatorPublicKey,
52 pub account_key: AccountPublicKey,
54 pub network: ValidatorPublicNetworkConfig,
56}
57
58#[derive(Serialize, Deserialize)]
60pub struct ValidatorServerConfig {
61 pub validator: ValidatorConfig,
62 pub validator_secret: ValidatorSecretKey,
63 pub internal_network: ValidatorInternalNetworkConfig,
64}
65
66#[derive(Debug, Default, Clone, Deserialize, Serialize)]
68pub struct CommitteeConfig {
69 pub validators: Vec<ValidatorConfig>,
70}
71
72impl CommitteeConfig {
73 pub fn into_committee(self, policy: ResourceControlPolicy) -> Committee {
74 let validators = self
75 .validators
76 .into_iter()
77 .map(|v| {
78 (
79 v.public_key,
80 ValidatorState {
81 network_address: v.network.to_string(),
82 votes: 100,
83 account_public_key: v.account_key,
84 },
85 )
86 })
87 .collect();
88 Committee::new(validators, policy)
89 }
90}
91
92#[derive(Clone, Debug, Serialize, Deserialize)]
93pub struct GenesisConfig {
94 pub committee: Committee,
95 pub timestamp: Timestamp,
96 pub chains: Vec<ChainDescription>,
97 pub network_name: String,
98}
99
100impl BcsSignable<'_> for GenesisConfig {}
101
102fn make_chain(
103 index: u32,
104 public_key: AccountPublicKey,
105 balance: Amount,
106 timestamp: Timestamp,
107) -> ChainDescription {
108 let origin = ChainOrigin::Root(index);
109 let config = InitialChainConfig {
110 application_permissions: Default::default(),
111 balance,
112 min_active_epoch: Epoch::ZERO,
113 max_active_epoch: Epoch::ZERO,
114 epoch: Epoch::ZERO,
115 ownership: ChainOwnership::single(public_key.into()),
116 };
117 ChainDescription::new(origin, config, timestamp)
118}
119
120impl GenesisConfig {
121 pub fn new(
123 committee: CommitteeConfig,
124 timestamp: Timestamp,
125 policy: ResourceControlPolicy,
126 network_name: String,
127 admin_public_key: AccountPublicKey,
128 admin_balance: Amount,
129 ) -> Self {
130 let committee = committee.into_committee(policy);
131 let admin_chain = make_chain(0, admin_public_key, admin_balance, timestamp);
132 Self {
133 committee,
134 timestamp,
135 chains: vec![admin_chain],
136 network_name,
137 }
138 }
139
140 pub fn add_root_chain(
141 &mut self,
142 public_key: AccountPublicKey,
143 balance: Amount,
144 ) -> ChainDescription {
145 let description = make_chain(
146 self.chains.len() as u32,
147 public_key,
148 balance,
149 self.timestamp,
150 );
151 self.chains.push(description.clone());
152 description
153 }
154
155 pub fn admin_chain_description(&self) -> &ChainDescription {
156 &self.chains[0]
157 }
158
159 pub fn admin_id(&self) -> ChainId {
160 self.admin_chain_description().id()
161 }
162
163 pub async fn initialize_storage<S>(&self, storage: &mut S) -> Result<(), Error>
164 where
165 S: Storage + Clone + Send + Sync + 'static,
166 {
167 if let Some(description) = storage
168 .read_network_description()
169 .await
170 .map_err(linera_chain::ChainError::from)?
171 {
172 return Err(Error::StorageIsAlreadyInitialized(Box::new(description)));
173 }
174 let network_description = self.network_description();
175 storage
176 .write_blob(&self.committee_blob())
177 .await
178 .map_err(linera_chain::ChainError::from)?;
179 storage
180 .write_network_description(&network_description)
181 .await
182 .map_err(linera_chain::ChainError::from)?;
183 for description in &self.chains {
184 storage.create_chain(description.clone()).await?;
185 }
186 Ok(())
187 }
188
189 pub fn hash(&self) -> CryptoHash {
190 CryptoHash::new(self)
191 }
192
193 pub fn committee_blob(&self) -> Blob {
194 Blob::new_committee(
195 bcs::to_bytes(&self.committee).expect("serializing a committee should succeed"),
196 )
197 }
198
199 pub fn network_description(&self) -> NetworkDescription {
200 NetworkDescription {
201 name: self.network_name.clone(),
202 genesis_config_hash: CryptoHash::new(self),
203 genesis_timestamp: self.timestamp,
204 genesis_committee_blob_hash: self.committee_blob().id().hash,
205 admin_chain_id: self.admin_id(),
206 }
207 }
208}