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::{
22 ExporterServiceConfig, TlsConfig, 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(Box<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 index: u32,
106 public_key: AccountPublicKey,
107 balance: Amount,
108 timestamp: Timestamp,
109) -> ChainDescription {
110 let origin = ChainOrigin::Root(index);
111 let config = InitialChainConfig {
112 application_permissions: Default::default(),
113 balance,
114 min_active_epoch: Epoch::ZERO,
115 max_active_epoch: Epoch::ZERO,
116 epoch: Epoch::ZERO,
117 ownership: ChainOwnership::single(public_key.into()),
118 };
119 ChainDescription::new(origin, config, timestamp)
120}
121
122impl GenesisConfig {
123 pub fn new(
125 committee: CommitteeConfig,
126 timestamp: Timestamp,
127 policy: ResourceControlPolicy,
128 network_name: String,
129 admin_public_key: AccountPublicKey,
130 admin_balance: Amount,
131 ) -> Self {
132 let committee = committee.into_committee(policy);
133 let admin_chain = make_chain(0, admin_public_key, admin_balance, timestamp);
134 Self {
135 committee,
136 timestamp,
137 chains: vec![admin_chain],
138 network_name,
139 }
140 }
141
142 pub fn add_root_chain(
143 &mut self,
144 public_key: AccountPublicKey,
145 balance: Amount,
146 ) -> ChainDescription {
147 let description = make_chain(
148 self.chains.len() as u32,
149 public_key,
150 balance,
151 self.timestamp,
152 );
153 self.chains.push(description.clone());
154 description
155 }
156
157 pub fn admin_chain_description(&self) -> &ChainDescription {
158 &self.chains[0]
159 }
160
161 pub fn admin_id(&self) -> ChainId {
162 self.admin_chain_description().id()
163 }
164
165 pub async fn initialize_storage<S>(&self, storage: &mut S) -> Result<(), Error>
166 where
167 S: Storage + Clone + Send + Sync + 'static,
168 {
169 if let Some(description) = storage
170 .read_network_description()
171 .await
172 .map_err(linera_chain::ChainError::from)?
173 {
174 return Err(Error::StorageIsAlreadyInitialized(Box::new(description)));
175 }
176 let network_description = self.network_description();
177 storage
178 .write_blob(&self.committee_blob())
179 .await
180 .map_err(linera_chain::ChainError::from)?;
181 storage
182 .write_network_description(&network_description)
183 .await
184 .map_err(linera_chain::ChainError::from)?;
185 for description in &self.chains {
186 storage.create_chain(description.clone()).await?;
187 }
188 Ok(())
189 }
190
191 pub fn hash(&self) -> CryptoHash {
192 CryptoHash::new(self)
193 }
194
195 pub fn committee_blob(&self) -> Blob {
196 Blob::new_committee(
197 bcs::to_bytes(&self.committee).expect("serializing a committee should succeed"),
198 )
199 }
200
201 pub fn network_description(&self) -> NetworkDescription {
202 NetworkDescription {
203 name: self.network_name.clone(),
204 genesis_config_hash: CryptoHash::new(self),
205 genesis_timestamp: self.timestamp,
206 genesis_committee_blob_hash: self.committee_blob().id().hash,
207 admin_chain_id: self.admin_id(),
208 }
209 }
210}
211
212#[derive(Serialize, Deserialize, Debug, Clone)]
214pub struct BlockExporterConfig {
215 pub id: u32,
217
218 pub service_config: ExporterServiceConfig,
220
221 #[serde(default)]
223 pub destination_config: DestinationConfig,
224
225 #[serde(default)]
228 pub limits: LimitsConfig,
229}
230
231#[derive(Serialize, Deserialize, Debug, Clone, Default)]
233pub struct DestinationConfig {
234 pub destinations: Vec<Destination>,
236}
237
238pub type DestinationId = u16;
240
241#[derive(Serialize, Deserialize, Debug, Clone)]
243pub struct Destination {
244 pub tls: TlsConfig,
246 pub endpoint: String,
248 pub port: u16,
250 pub kind: DestinationKind,
253}
254
255#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy)]
258pub enum DestinationKind {
259 Indexer,
261 Validator,
263}
264
265#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
268pub struct LimitsConfig {
269 pub persistence_period_ms: u32,
272 pub work_queue_size: u16,
275 pub blob_cache_weight_mb: u16,
277 pub blob_cache_items_capacity: u16,
279 pub block_cache_weight_mb: u16,
281 pub block_cache_items_capacity: u16,
283 pub auxiliary_cache_size_mb: u16,
286}
287
288impl Default for LimitsConfig {
289 fn default() -> Self {
290 Self {
291 persistence_period_ms: 299 * 1000,
292 work_queue_size: 256,
293 blob_cache_weight_mb: 1024,
294 blob_cache_items_capacity: 8192,
295 block_cache_weight_mb: 1024,
296 block_cache_items_capacity: 8192,
297 auxiliary_cache_size_mb: 1024,
298 }
299 }
300}
301
302impl Destination {
303 pub fn address(&self) -> String {
304 match self.kind {
305 DestinationKind::Indexer => {
306 let tls = match self.tls {
307 TlsConfig::ClearText => "http",
308 TlsConfig::Tls => "https",
309 };
310
311 format!("{}://{}:{}", tls, self.endpoint, self.port)
312 }
313
314 DestinationKind::Validator => {
315 format!("{}:{}:{}", "grpc", self.endpoint, self.port)
316 }
317 }
318 }
319}