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::{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/// 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    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    /// Creates a `GenesisConfig` with the first chain being the admin chain.
130    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/// The configuration file for the linera-exporter.
209#[derive(Serialize, Deserialize, Debug, Clone)]
210pub struct BlockExporterConfig {
211    /// The server configuration for the linera-exporter.
212    pub service_config: ExporterServiceConfig,
213
214    /// The configuration file for the export destinations.
215    #[serde(default)]
216    pub destination_config: DestinationConfig,
217
218    /// Identity for the block exporter state.
219    pub id: u32,
220}
221
222/// Configuration file for the exports.
223#[derive(Serialize, Deserialize, Debug, Clone, Default)]
224pub struct DestinationConfig {
225    /// The destination URIs to export to.
226    pub destinations: Vec<Destination>,
227}
228
229// Each destination has an ID and a configuration.
230pub type DestinationId = u16;
231
232/// The uri to provide export services to.
233#[allow(dead_code)]
234#[derive(Serialize, Deserialize, Debug, Clone)]
235pub struct Destination {
236    /// The host name of the target destination (IP or hostname).
237    pub endpoint: String,
238    /// The port number of the target destination.
239    pub port: u16,
240}