linera_client/
config.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use 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/// The public configuration of a validator.
50#[derive(Clone, Debug, Serialize, Deserialize)]
51pub struct ValidatorConfig {
52    /// The public key of the validator.
53    pub public_key: ValidatorPublicKey,
54    /// The account key of the validator.
55    pub account_key: AccountPublicKey,
56    /// The network configuration for the validator.
57    pub network: ValidatorPublicNetworkConfig,
58}
59
60/// The private configuration of a validator service.
61#[derive(Serialize, Deserialize)]
62pub struct ValidatorServerConfig {
63    pub validator: ValidatorConfig,
64    pub validator_secret: ValidatorSecretKey,
65    pub internal_network: ValidatorInternalNetworkConfig,
66}
67
68/// The (public) configuration for all validators.
69#[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    /// Creates a `GenesisConfig` with the first chain being the admin chain.
124    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/// The configuration file for the linera-exporter.
213#[derive(Serialize, Deserialize, Debug, Clone)]
214pub struct BlockExporterConfig {
215    /// Identity for the block exporter state.
216    pub id: u32,
217
218    /// The server configuration for the linera-exporter.
219    pub service_config: ExporterServiceConfig,
220
221    /// The configuration file for the export destinations.
222    #[serde(default)]
223    pub destination_config: DestinationConfig,
224
225    /// The configuration file to impose various limits
226    /// on the resources used by the linera-exporter.
227    #[serde(default)]
228    pub limits: LimitsConfig,
229}
230
231/// Configuration file for the exports.
232#[derive(Serialize, Deserialize, Debug, Clone, Default)]
233pub struct DestinationConfig {
234    /// The destination URIs to export to.
235    pub destinations: Vec<Destination>,
236}
237
238// Each destination has an ID and a configuration.
239pub type DestinationId = u16;
240
241/// The uri to provide export services to.
242#[derive(Serialize, Deserialize, Debug, Clone)]
243pub struct Destination {
244    /// The gRPC network protocol.
245    pub tls: TlsConfig,
246    /// The host name of the target destination (IP or hostname).
247    pub endpoint: String,
248    /// The port number of the target destination.
249    pub port: u16,
250    /// The description for the gRPC based destination.
251    /// Discriminates the export mode and the client to use.
252    pub kind: DestinationKind,
253}
254
255/// The description for the gRPC based destination.
256/// Discriminates the export mode and the client to use.
257#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy)]
258pub enum DestinationKind {
259    /// The indexer description.
260    Indexer,
261    /// The validator description.
262    Validator,
263}
264
265/// The configuration file to impose various limits
266/// on the resources used by the linera-exporter.
267#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
268pub struct LimitsConfig {
269    /// Time period in milliseconds between periodic persistence
270    /// to the shared storage.
271    pub persistence_period_ms: u32,
272    /// Maximum size of the work queue i.e. maximum number
273    /// of blocks queued up for exports per destination.
274    pub work_queue_size: u16,
275    /// Maximum weight of the blob cache in megabytes.
276    pub blob_cache_weight_mb: u16,
277    /// Estimated number of elements for the blob cache.
278    pub blob_cache_items_capacity: u16,
279    /// Maximum weight of the block cache in megabytes.
280    pub block_cache_weight_mb: u16,
281    /// Estimated number of elements for the block cache.
282    pub block_cache_items_capacity: u16,
283    /// Maximum weight in megabytes for the combined
284    /// cache, consisting of small miscellaneous items.
285    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}