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