diff --git a/linera-client/src/config.rs b/linera-client/src/config.rs index e6c2d145a93e..8729e2777f8e 100644 --- a/linera-client/src/config.rs +++ b/linera-client/src/config.rs @@ -24,7 +24,7 @@ use linera_execution::{ use linera_rpc::config::{ ExporterServiceConfig, ValidatorInternalNetworkConfig, ValidatorPublicNetworkConfig, }; -use linera_storage::Storage; +use linera_storage::{NetworkDescription, Storage}; use serde::{Deserialize, Serialize}; #[derive(Debug, thiserror::Error)] @@ -35,6 +35,8 @@ pub enum Error { Chain(#[from] linera_chain::ChainError), #[error("persistence error: {0}")] Persistence(Box), + #[error("storage is already initialized: {0:?}")] + StorageIsAlreadyInitialized(NetworkDescription), } use crate::{ @@ -284,6 +286,13 @@ impl GenesisConfig { where S: Storage + Clone + Send + Sync + 'static, { + if let Some(description) = storage + .read_network_description() + .await + .map_err(linera_chain::ChainError::from)? + { + return Err(Error::StorageIsAlreadyInitialized(description)); + } let committee = self.create_committee(); let committees: BTreeMap<_, _> = [( Epoch::ZERO, @@ -308,6 +317,15 @@ impl GenesisConfig { let description = ChainDescription::new(origin, config, self.timestamp); storage.create_chain(description).await?; } + let network_description = NetworkDescription { + name: self.network_name.clone(), + genesis_config_hash: CryptoHash::new(self), + genesis_timestamp: self.timestamp, + }; + storage + .write_network_description(&network_description) + .await + .map_err(linera_chain::ChainError::from)?; Ok(()) } diff --git a/linera-core/src/node.rs b/linera-core/src/node.rs index 6dd34f5e9d5a..d4ae5a2cb6f8 100644 --- a/linera-core/src/node.rs +++ b/linera-core/src/node.rs @@ -21,6 +21,7 @@ use linera_chain::{ ChainError, }; use linera_execution::{committee::Committee, ExecutionError}; +use linera_storage::NetworkDescription; use linera_version::VersionInfo; use linera_views::views::ViewError; use serde::{Deserialize, Serialize}; @@ -92,8 +93,8 @@ pub trait ValidatorNode { /// Gets the version info for this validator node. async fn get_version_info(&self) -> Result; - /// Gets the network's genesis config hash. - async fn get_genesis_config_hash(&self) -> Result; + /// Gets the network's description. + async fn get_network_description(&self) -> Result; /// Subscribes to receiving notifications for a collection of chains. async fn subscribe(&self, chains: Vec) -> Result; diff --git a/linera-core/src/unit_tests/test_utils.rs b/linera-core/src/unit_tests/test_utils.rs index c9b394147e12..60e19acc0ce6 100644 --- a/linera-core/src/unit_tests/test_utils.rs +++ b/linera-core/src/unit_tests/test_utils.rs @@ -29,7 +29,7 @@ use linera_chain::{ }, }; use linera_execution::{committee::Committee, ResourceControlPolicy, WasmRuntime}; -use linera_storage::{DbStorage, Storage, TestClock}; +use linera_storage::{DbStorage, NetworkDescription, Storage, TestClock}; #[cfg(all(not(target_arch = "wasm32"), feature = "storage-service"))] use linera_storage_service::client::ServiceStoreClient; use linera_version::VersionInfo; @@ -173,8 +173,12 @@ where Ok(Default::default()) } - async fn get_genesis_config_hash(&self) -> Result { - Ok(CryptoHash::test_hash("genesis config")) + async fn get_network_description(&self) -> Result { + Ok(NetworkDescription { + name: "test network".to_string(), + genesis_config_hash: CryptoHash::test_hash("genesis config"), + genesis_timestamp: Timestamp::default(), + }) } async fn upload_blob(&self, content: BlobContent) -> Result { diff --git a/linera-rpc/proto/rpc.proto b/linera-rpc/proto/rpc.proto index 270710c739b3..fb86f3998b4a 100644 --- a/linera-rpc/proto/rpc.proto +++ b/linera-rpc/proto/rpc.proto @@ -64,8 +64,8 @@ service ValidatorNode { // Request the node's version info. rpc GetVersionInfo(google.protobuf.Empty) returns (VersionInfo); - // Request the genesis configuration hash of the network this node is part of. - rpc GetGenesisConfigHash(google.protobuf.Empty) returns (CryptoHash); + // Request the network description seen by this node. + rpc GetNetworkDescription(google.protobuf.Empty) returns (NetworkDescription); // Download a blob. rpc DownloadBlob(BlobId) returns (BlobContent); @@ -120,6 +120,12 @@ message VersionInfo { string wit_hash = 6; } +message NetworkDescription { + string name = 1; + CryptoHash genesis_config_hash = 2; + uint64 genesis_timestamp = 3; +} + // A request for client to subscribe to notifications for a given `ChainId` message SubscriptionRequest { repeated ChainId chain_ids = 1; diff --git a/linera-rpc/src/client.rs b/linera-rpc/src/client.rs index c8325156ca2b..c89f5b46dc10 100644 --- a/linera-rpc/src/client.rs +++ b/linera-rpc/src/client.rs @@ -160,12 +160,14 @@ impl ValidatorNode for Client { }) } - async fn get_genesis_config_hash(&self) -> Result { + async fn get_network_description( + &self, + ) -> Result { Ok(match self { - Client::Grpc(grpc_client) => grpc_client.get_genesis_config_hash().await?, + Client::Grpc(grpc_client) => grpc_client.get_network_description().await?, #[cfg(with_simple_network)] - Client::Simple(simple_client) => simple_client.get_genesis_config_hash().await?, + Client::Simple(simple_client) => simple_client.get_network_description().await?, }) } diff --git a/linera-rpc/src/grpc/client.rs b/linera-rpc/src/grpc/client.rs index be62bd889688..c23c882d9f6a 100644 --- a/linera-rpc/src/grpc/client.rs +++ b/linera-rpc/src/grpc/client.rs @@ -23,6 +23,7 @@ use linera_core::{ node::{CrossChainMessageDelivery, NodeError, NotificationStream, ValidatorNode}, worker::Notification, }; +use linera_storage::NetworkDescription; use linera_version::VersionInfo; use tonic::{Code, IntoRequest, Request, Status}; use tracing::{debug, error, info, instrument, warn}; @@ -346,9 +347,9 @@ impl ValidatorNode for GrpcClient { } #[instrument(target = "grpc_client", skip_all, err, fields(address = self.address))] - async fn get_genesis_config_hash(&self) -> Result { + async fn get_network_description(&self) -> Result { let req = (); - Ok(client_delegate!(self, get_genesis_config_hash, req)?.try_into()?) + Ok(client_delegate!(self, get_network_description, req)?.try_into()?) } #[instrument(target = "grpc_client", skip(self), err, fields(address = self.address))] diff --git a/linera-rpc/src/grpc/conversions.rs b/linera-rpc/src/grpc/conversions.rs index 717beb4e704c..eb6509494fba 100644 --- a/linera-rpc/src/grpc/conversions.rs +++ b/linera-rpc/src/grpc/conversions.rs @@ -142,6 +142,40 @@ impl From for linera_version::VersionInfo { } } +impl From for api::NetworkDescription { + fn from( + linera_storage::NetworkDescription { + name, + genesis_config_hash, + genesis_timestamp, + }: linera_storage::NetworkDescription, + ) -> Self { + Self { + name, + genesis_config_hash: Some(genesis_config_hash.into()), + genesis_timestamp: genesis_timestamp.micros(), + } + } +} + +impl TryFrom for linera_storage::NetworkDescription { + type Error = GrpcProtoConversionError; + + fn try_from( + api::NetworkDescription { + name, + genesis_config_hash, + genesis_timestamp, + }: api::NetworkDescription, + ) -> Result { + Ok(Self { + name, + genesis_config_hash: try_proto_convert(genesis_config_hash)?, + genesis_timestamp: genesis_timestamp.into(), + }) + } +} + impl TryFrom for api::Notification { type Error = GrpcProtoConversionError; diff --git a/linera-rpc/src/message.rs b/linera-rpc/src/message.rs index 96b06446801e..acc4edd5f8ef 100644 --- a/linera-rpc/src/message.rs +++ b/linera-rpc/src/message.rs @@ -15,6 +15,7 @@ use linera_core::{ data_types::{ChainInfoQuery, ChainInfoResponse, CrossChainRequest}, node::NodeError, }; +use linera_storage::NetworkDescription; use linera_version::VersionInfo; use serde::{Deserialize, Serialize}; @@ -42,14 +43,14 @@ pub enum RpcMessage { BlobLastUsedBy(Box), MissingBlobIds(Vec), VersionInfoQuery, - GenesisConfigHashQuery, + NetworkDescriptionQuery, // Outbound Vote(Box), ChainInfoResponse(Box), Error(Box), VersionInfoResponse(Box), - GenesisConfigHashResponse(Box), + NetworkDescriptionResponse(Box), UploadBlobResponse(Box), DownloadBlobResponse(Box), DownloadPendingBlobResponse(Box), @@ -84,8 +85,8 @@ impl RpcMessage { | ChainInfoResponse(_) | VersionInfoQuery | VersionInfoResponse(_) - | GenesisConfigHashQuery - | GenesisConfigHashResponse(_) + | NetworkDescriptionQuery + | NetworkDescriptionResponse(_) | UploadBlob(_) | UploadBlobResponse(_) | DownloadBlob(_) @@ -113,7 +114,7 @@ impl RpcMessage { match self { VersionInfoQuery - | GenesisConfigHashQuery + | NetworkDescriptionQuery | UploadBlob(_) | DownloadBlob(_) | DownloadConfirmedBlock(_) @@ -131,7 +132,7 @@ impl RpcMessage { | Error(_) | ChainInfoResponse(_) | VersionInfoResponse(_) - | GenesisConfigHashResponse(_) + | NetworkDescriptionResponse(_) | UploadBlobResponse(_) | DownloadPendingBlob(_) | DownloadPendingBlobResponse(_) @@ -206,13 +207,22 @@ impl TryFrom for CryptoHash { fn try_from(message: RpcMessage) -> Result { match message { RpcMessage::BlobLastUsedByResponse(hash) => Ok(*hash), - RpcMessage::GenesisConfigHashResponse(hash) => Ok(*hash), RpcMessage::Error(error) => Err(*error), _ => Err(NodeError::UnexpectedMessage), } } } +impl TryFrom for NetworkDescription { + type Error = NodeError; + fn try_from(message: RpcMessage) -> Result { + match message { + RpcMessage::NetworkDescriptionResponse(description) => Ok(*description), + _ => Err(NodeError::UnexpectedMessage), + } + } +} + impl TryFrom for Vec { type Error = NodeError; fn try_from(message: RpcMessage) -> Result { diff --git a/linera-rpc/src/simple/client.rs b/linera-rpc/src/simple/client.rs index 1ae302564d78..5318398f9bfa 100644 --- a/linera-rpc/src/simple/client.rs +++ b/linera-rpc/src/simple/client.rs @@ -21,6 +21,7 @@ use linera_core::{ data_types::{ChainInfoQuery, ChainInfoResponse}, node::{CrossChainMessageDelivery, NodeError, NotificationStream, ValidatorNode}, }; +use linera_storage::NetworkDescription; use linera_version::VersionInfo; use super::{codec, transport::TransportProtocol}; @@ -156,8 +157,8 @@ impl ValidatorNode for SimpleClient { self.query(RpcMessage::VersionInfoQuery).await } - async fn get_genesis_config_hash(&self) -> Result { - self.query(RpcMessage::GenesisConfigHashQuery).await + async fn get_network_description(&self) -> Result { + self.query(RpcMessage::NetworkDescriptionQuery).await } async fn upload_blob(&self, content: BlobContent) -> Result { diff --git a/linera-rpc/src/simple/server.rs b/linera-rpc/src/simple/server.rs index d71bf5728f25..8a644a833fd6 100644 --- a/linera-rpc/src/simple/server.rs +++ b/linera-rpc/src/simple/server.rs @@ -391,8 +391,8 @@ where | RpcMessage::Error(_) | RpcMessage::ChainInfoResponse(_) | RpcMessage::VersionInfoResponse(_) - | RpcMessage::GenesisConfigHashQuery - | RpcMessage::GenesisConfigHashResponse(_) + | RpcMessage::NetworkDescriptionQuery + | RpcMessage::NetworkDescriptionResponse(_) | RpcMessage::DownloadBlob(_) | RpcMessage::DownloadBlobResponse(_) | RpcMessage::DownloadPendingBlobResponse(_) diff --git a/linera-rpc/tests/snapshots/format__format.yaml.snap b/linera-rpc/tests/snapshots/format__format.yaml.snap index de519a6f3a61..43d7e0f642c0 100644 --- a/linera-rpc/tests/snapshots/format__format.yaml.snap +++ b/linera-rpc/tests/snapshots/format__format.yaml.snap @@ -636,6 +636,13 @@ ModuleId: TYPENAME: CryptoHash - vm_runtime: TYPENAME: VmRuntime +NetworkDescription: + STRUCT: + - name: STR + - genesis_config_hash: + TYPENAME: CryptoHash + - genesis_timestamp: + TYPENAME: Timestamp NodeError: ENUM: 0: @@ -984,7 +991,7 @@ RpcMessage: 14: VersionInfoQuery: UNIT 15: - GenesisConfigHashQuery: UNIT + NetworkDescriptionQuery: UNIT 16: Vote: NEWTYPE: @@ -1002,9 +1009,9 @@ RpcMessage: NEWTYPE: TYPENAME: VersionInfo 20: - GenesisConfigHashResponse: + NetworkDescriptionResponse: NEWTYPE: - TYPENAME: CryptoHash + TYPENAME: NetworkDescription 21: UploadBlobResponse: NEWTYPE: diff --git a/linera-service/src/linera/main.rs b/linera-service/src/linera/main.rs index 73c3d3000f07..f9c787570eeb 100644 --- a/linera-service/src/linera/main.rs +++ b/linera-service/src/linera/main.rs @@ -456,12 +456,15 @@ impl Runnable for Job { } let genesis_config_hash = context.wallet().genesis_config().hash(); - match node.get_genesis_config_hash().await { - Ok(hash) if hash == genesis_config_hash => {} - Ok(hash) => error!( - "Validator's genesis config hash {} does not match our own: {}.", - hash, genesis_config_hash - ), + match node.get_network_description().await { + Ok(description) => { + if description.genesis_config_hash != genesis_config_hash { + error!( + "Validator's genesis config hash {} does not match our own: {}.", + description.genesis_config_hash, genesis_config_hash + ); + } + } Err(error) => { error!( "Failed to get genesis config hash for validator {address}:\n{error}" @@ -631,16 +634,20 @@ impl Runnable for Job { ), } let genesis_config_hash = context.wallet().genesis_config().hash(); - match node.get_genesis_config_hash().await { - Ok(hash) if hash == genesis_config_hash => {} - Ok(hash) => bail!( - "Validator's genesis config hash {} does not match our own: {}.", - hash, - genesis_config_hash - ), - Err(error) => bail!( - "Failed to get genesis config hash for validator {public_key:?} at {address}:\n{error}" - ), + match node.get_network_description().await { + Ok(description) => { + if description.genesis_config_hash != genesis_config_hash { + error!( + "Validator's genesis config hash {} does not match our own: {}.", + description.genesis_config_hash, genesis_config_hash + ); + } + } + Err(error) => { + error!( + "Failed to get genesis config hash for validator {address}:\n{error}" + ) + } } } let chain_client = context diff --git a/linera-service/src/proxy/grpc.rs b/linera-service/src/proxy/grpc.rs index 39ee5f537cfe..43ed7a32a140 100644 --- a/linera-service/src/proxy/grpc.rs +++ b/linera-service/src/proxy/grpc.rs @@ -19,7 +19,6 @@ use anyhow::Result; use async_trait::async_trait; use futures::{future::BoxFuture, FutureExt as _}; use linera_base::identifiers::ChainId; -use linera_client::config::GenesisConfig; use linera_core::{notifier::ChannelNotifier, JoinSetExt as _}; use linera_rpc::{ config::{ @@ -33,8 +32,8 @@ use linera_rpc::{ validator_worker_client::ValidatorWorkerClient, BlobContent, BlobId, BlobIds, BlockProposal, Certificate, CertificatesBatchRequest, CertificatesBatchResponse, ChainInfoQuery, ChainInfoResult, CryptoHash, - HandlePendingBlobRequest, LiteCertificate, Notification, PendingBlobRequest, - PendingBlobResult, SubscriptionRequest, VersionInfo, + HandlePendingBlobRequest, LiteCertificate, NetworkDescription, Notification, + PendingBlobRequest, PendingBlobResult, SubscriptionRequest, VersionInfo, }, pool::GrpcConnectionPool, GrpcProtoConversionError, GrpcProxyable, GRPC_CHUNKED_MESSAGE_FILL_LIMIT, @@ -150,7 +149,6 @@ pub struct GrpcProxy(Arc>); struct GrpcProxyInner { public_config: ValidatorPublicNetworkConfig, internal_config: ValidatorInternalNetworkConfig, - genesis_config: GenesisConfig, worker_connection_pool: GrpcConnectionPool, notifier: ChannelNotifier>, tls: TlsConfig, @@ -164,7 +162,6 @@ where pub fn new( public_config: ValidatorPublicNetworkConfig, internal_config: ValidatorInternalNetworkConfig, - genesis_config: GenesisConfig, connect_timeout: Duration, timeout: Duration, tls: TlsConfig, @@ -173,7 +170,6 @@ where Self(Arc::new(GrpcProxyInner { public_config, internal_config, - genesis_config, worker_connection_pool: GrpcConnectionPool::default() .with_connect_timeout(connect_timeout) .with_timeout(timeout), @@ -470,11 +466,20 @@ where } #[instrument(skip_all, err(Display))] - async fn get_genesis_config_hash( + async fn get_network_description( &self, _request: Request<()>, - ) -> Result, Status> { - Ok(Response::new(self.0.genesis_config.hash().into())) + ) -> Result, Status> { + let description = self + .0 + .storage + .read_network_description() + .await + .map_err(Self::error_to_status)? + .ok_or(Status::not_found( + "Cannot find network description in the database", + ))?; + Ok(Response::new(description.into())) } #[instrument(skip_all, err(Display))] diff --git a/linera-service/src/proxy/main.rs b/linera-service/src/proxy/main.rs index 0aac808cfd6f..a45aa7ce57c9 100644 --- a/linera-service/src/proxy/main.rs +++ b/linera-service/src/proxy/main.rs @@ -5,7 +5,7 @@ use std::{net::SocketAddr, path::PathBuf, time::Duration}; -use anyhow::{bail, ensure, Result}; +use anyhow::{anyhow, bail, ensure, Result}; use async_trait::async_trait; use futures::{FutureExt as _, SinkExt, StreamExt}; use linera_base::listen_for_shutdown_signals; @@ -110,7 +110,6 @@ where struct ProxyContext { config: ValidatorServerConfig, - genesis_config: GenesisConfig, send_timeout: Duration, recv_timeout: Duration, } @@ -118,12 +117,10 @@ struct ProxyContext { impl ProxyContext { pub fn from_options(options: &ProxyOptions) -> Result { let config = util::read_json(&options.config_path)?; - let genesis_config = util::read_json(&options.genesis_config_path)?; Ok(Self { config, send_timeout: options.send_timeout, recv_timeout: options.recv_timeout, - genesis_config, }) } } @@ -159,7 +156,6 @@ where Self::Grpc(GrpcProxy::new( context.config.validator.network, context.config.internal_network, - context.genesis_config, context.send_timeout, context.recv_timeout, tls, @@ -179,7 +175,6 @@ where .validator .network .clone_with_protocol(public_transport), - genesis_config: context.genesis_config, send_timeout: context.send_timeout, recv_timeout: context.recv_timeout, storage, @@ -204,7 +199,6 @@ where { public_config: ValidatorPublicNetworkPreConfig, internal_config: ValidatorInternalNetworkPreConfig, - genesis_config: GenesisConfig, send_timeout: Duration, recv_timeout: Duration, storage: S, @@ -316,9 +310,16 @@ where linera_version::VersionInfo::default().into(), ))) } - GenesisConfigHashQuery => Ok(Some(RpcMessage::GenesisConfigHashResponse(Box::new( - self.genesis_config.hash(), - )))), + NetworkDescriptionQuery => { + let description = self + .storage + .read_network_description() + .await? + .ok_or(anyhow!("Cannot find network description in the database"))?; + Ok(Some(RpcMessage::NetworkDescriptionResponse(Box::new( + description, + )))) + } UploadBlob(content) => { let blob = Blob::new(*content); let id = blob.id(); @@ -356,7 +357,7 @@ where | Error(_) | ChainInfoResponse(_) | VersionInfoResponse(_) - | GenesisConfigHashResponse(_) + | NetworkDescriptionResponse(_) | DownloadBlobResponse(_) | DownloadPendingBlob(_) | DownloadPendingBlobResponse(_) diff --git a/linera-service/src/schema_export.rs b/linera-service/src/schema_export.rs index 2c4d07eb5045..837dd603f89d 100644 --- a/linera-service/src/schema_export.rs +++ b/linera-service/src/schema_export.rs @@ -30,7 +30,7 @@ use linera_core::{ use linera_execution::committee::Committee; use linera_sdk::linera_base_types::ValidatorPublicKey; use linera_service::node_service::NodeService; -use linera_storage::{DbStorage, Storage}; +use linera_storage::{DbStorage, NetworkDescription, Storage}; use linera_version::VersionInfo; use linera_views::memory::MemoryStore; @@ -104,7 +104,7 @@ impl ValidatorNode for DummyValidatorNode { Err(NodeError::UnexpectedMessage) } - async fn get_genesis_config_hash(&self) -> Result { + async fn get_network_description(&self) -> Result { Err(NodeError::UnexpectedMessage) } diff --git a/linera-storage/src/db_storage.rs b/linera-storage/src/db_storage.rs index 28d6eaf5e09b..1d1dc7ec4544 100644 --- a/linera-storage/src/db_storage.rs +++ b/linera-storage/src/db_storage.rs @@ -42,7 +42,7 @@ use { prometheus::{HistogramVec, IntCounterVec}, }; -use crate::{ChainRuntimeContext, Clock, Storage}; +use crate::{ChainRuntimeContext, Clock, NetworkDescription, Storage}; /// The metric counting how often a blob is tested for existence from storage #[cfg(with_metrics)] @@ -216,6 +216,28 @@ pub static WRITE_EVENT_COUNTER: LazyLock = LazyLock::new(|| { ) }); +/// The metric counting how often the network description is read from storage. +#[cfg(with_metrics)] +#[doc(hidden)] +pub static READ_NETWORK_DESCRIPTION: LazyLock = LazyLock::new(|| { + register_int_counter_vec( + "network_description", + "The metric counting how often the network description is read from storage", + &[], + ) +}); + +/// The metric counting how often the network description is written to storage. +#[cfg(with_metrics)] +#[doc(hidden)] +pub static WRITE_NETWORK_DESCRIPTION: LazyLock = LazyLock::new(|| { + register_int_counter_vec( + "write_event", + "The metric counting how often the network description is written to storage", + &[], + ) +}); + trait BatchExt { fn add_blob(&mut self, blob: &Blob) -> Result<(), ViewError>; @@ -225,6 +247,11 @@ trait BatchExt { -> Result<(), ViewError>; fn add_event(&mut self, event_id: EventId, value: Vec) -> Result<(), ViewError>; + + fn add_network_description( + &mut self, + information: &NetworkDescription, + ) -> Result<(), ViewError>; } impl BatchExt for Batch { @@ -263,6 +290,17 @@ impl BatchExt for Batch { self.put_key_value_bytes(event_key.to_vec(), value); Ok(()) } + + fn add_network_description( + &mut self, + information: &NetworkDescription, + ) -> Result<(), ViewError> { + #[cfg(with_metrics)] + WRITE_NETWORK_DESCRIPTION.with_label_values(&[]).inc(); + let key = bcs::to_bytes(&BaseKey::NetworkDescription)?; + self.put_key_value(key, information)?; + Ok(()) + } } /// Main implementation of the [`Storage`] trait. @@ -285,6 +323,7 @@ enum BaseKey { BlobState(BlobId), Event(EventId), BlockExporterState(u32), + NetworkDescription, } const INDEX_CHAIN_ID: u8 = 0; @@ -812,6 +851,24 @@ where self.write_batch(batch).await } + async fn read_network_description(&self) -> Result, ViewError> { + let key = bcs::to_bytes(&BaseKey::NetworkDescription)?; + let maybe_value = self.store.read_value(&key).await?; + #[cfg(with_metrics)] + READ_NETWORK_DESCRIPTION.with_label_values(&[]).inc(); + Ok(maybe_value) + } + + async fn write_network_description( + &self, + information: &NetworkDescription, + ) -> Result<(), ViewError> { + let mut batch = Batch::new(); + batch.add_network_description(information)?; + self.write_batch(batch).await?; + Ok(()) + } + fn wasm_runtime(&self) -> Option { self.wasm_runtime } diff --git a/linera-storage/src/lib.rs b/linera-storage/src/lib.rs index 9c8a05c190d5..4d9c9f16cd7b 100644 --- a/linera-storage/src/lib.rs +++ b/linera-storage/src/lib.rs @@ -39,6 +39,7 @@ use linera_views::{ context::Context, views::{RootView, ViewError}, }; +use serde::{Deserialize, Serialize}; #[cfg(with_testing)] pub use crate::db_storage::TestClock; @@ -173,6 +174,15 @@ pub trait Storage: Sized { events: impl IntoIterator)> + Send, ) -> Result<(), ViewError>; + /// Reads the network description. + async fn read_network_description(&self) -> Result, ViewError>; + + /// Writes the network description. + async fn write_network_description( + &self, + information: &NetworkDescription, + ) -> Result<(), ViewError>; + /// Initializes a chain in a simple way (used for testing and to create a genesis state). /// /// # Notes @@ -318,6 +328,16 @@ pub trait Storage: Sized { ) -> Result; } +/// A description of the current Linera network to be stored in every node's database. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(with_testing, derive(Eq, PartialEq))] +pub struct NetworkDescription { + pub name: String, + pub genesis_config_hash: CryptoHash, + pub genesis_timestamp: Timestamp, +} + +/// An implementation of `ExecutionRuntimeContext` suitable for the core protocol. #[derive(Clone)] pub struct ChainRuntimeContext { storage: S,