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::{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/// The public configuration of a validator.
48#[derive(Clone, Debug, Serialize, Deserialize)]
49pub struct ValidatorConfig {
50    /// The public key of the validator.
51    pub public_key: ValidatorPublicKey,
52    /// The account key of the validator.
53    pub account_key: AccountPublicKey,
54    /// The network configuration for the validator.
55    pub network: ValidatorPublicNetworkConfig,
56}
57
58/// The private configuration of a validator service.
59#[derive(Serialize, Deserialize)]
60pub struct ValidatorServerConfig {
61    pub validator: ValidatorConfig,
62    pub validator_secret: ValidatorSecretKey,
63    pub internal_network: ValidatorInternalNetworkConfig,
64}
65
66/// The (public) configuration for all validators.
67#[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    /// Creates a `GenesisConfig` with the first chain being the admin chain.
122    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}