Skip to main content

linera_storage/
lib.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module defines the storage abstractions for individual chains and certificates.
5
6#![deny(missing_docs)]
7
8mod db_storage;
9
10use std::sync::Arc as StdArc;
11
12use async_trait::async_trait;
13use itertools::Itertools;
14use linera_base::{
15    crypto::CryptoHash,
16    data_types::{
17        ApplicationDescription, Blob, BlockHeight, ChainDescription, CompressedBytecode, Epoch,
18        NetworkDescription, TimeDelta, Timestamp,
19    },
20    identifiers::{ApplicationId, BlobId, BlobType, ChainId, EventId, IndexAndEvent, StreamId},
21    time::Duration,
22    vm::VmRuntime,
23};
24pub use linera_cache::{Arc, DEFAULT_CLEANUP_INTERVAL_SECS};
25use linera_chain::{
26    types::{ConfirmedBlock, ConfirmedBlockCertificate},
27    ChainError, ChainStateView,
28};
29use linera_execution::{
30    committee::Committee, BlobState, ExecutionError, ExecutionRuntimeConfig,
31    ExecutionRuntimeContext, SharedCommittees, TransactionTracker, UserContractCode,
32    UserServiceCode, WasmRuntime,
33};
34#[cfg(with_revm)]
35use linera_execution::{
36    evm::revm::{EvmContractModule, EvmServiceModule},
37    EvmRuntime,
38};
39#[cfg(with_wasm_runtime)]
40use linera_execution::{WasmContractModule, WasmServiceModule};
41use linera_views::{context::Context, views::RootView, ViewError};
42
43#[cfg(with_metrics)]
44pub use crate::db_storage::metrics;
45pub use crate::db_storage::{
46    ChainStatesFirstAssignment, DbStorage, RootKey, StorageCacheConfig, StorageCaches, WallClock,
47};
48#[cfg(with_testing)]
49pub use crate::db_storage::{TestClock, DEFAULT_STORAGE_CACHE_CONFIG};
50
51/// The default namespace to be used when none is specified
52pub const DEFAULT_NAMESPACE: &str = "default";
53
54/// Communicate with a persistent storage using the "views" abstraction.
55#[cfg_attr(not(web), async_trait)]
56#[cfg_attr(web, async_trait(?Send))]
57pub trait Storage: linera_base::util::traits::AutoTraits + Sized {
58    /// The low-level storage implementation in use by the core protocol (chain workers etc).
59    type Context: Context<Extra = ChainRuntimeContext<Self>> + Clone + 'static;
60
61    /// The clock type being used.
62    type Clock: Clock + Clone + Send + Sync;
63
64    /// The low-level storage implementation in use by the block exporter.
65    type BlockExporterContext: Context<Extra = u32> + Clone;
66
67    /// Returns the current wall clock time.
68    fn clock(&self) -> &Self::Clock;
69
70    /// Returns the thread pool used to run blocking work such as bytecode decompression.
71    fn thread_pool(&self) -> &StdArc<linera_execution::ThreadPool>;
72
73    /// Loads the view of a chain state.
74    ///
75    /// # Notes
76    ///
77    /// Each time this method is called, a new [`ChainStateView`] is created. If there are multiple
78    /// instances of the same chain active at any given moment, they will race to access persistent
79    /// storage. This can lead to invalid states and data corruption.
80    async fn load_chain(&self, id: ChainId) -> Result<ChainStateView<Self::Context>, ViewError>;
81
82    /// Tests the existence of a blob with the given blob ID.
83    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
84
85    /// Returns what blobs from the input are missing from storage.
86    async fn missing_blobs(&self, blob_ids: &[BlobId]) -> Result<Vec<BlobId>, ViewError>;
87
88    /// Tests existence of a blob state with the given blob ID.
89    async fn contains_blob_state(&self, blob_id: BlobId) -> Result<bool, ViewError>;
90
91    /// Reads the hashed certificate value with the given hash.
92    async fn read_confirmed_block(
93        &self,
94        hash: CryptoHash,
95    ) -> Result<Option<Arc<ConfirmedBlock>>, ViewError>;
96
97    /// Reads a number of confirmed blocks by their hashes.
98    async fn read_confirmed_blocks<I: IntoIterator<Item = CryptoHash> + Send>(
99        &self,
100        hashes: I,
101    ) -> Result<Vec<Option<Arc<ConfirmedBlock>>>, ViewError>;
102
103    /// Reads the blob with the given blob ID.
104    async fn read_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError>;
105
106    /// Reads the blobs with the given blob IDs.
107    async fn read_blobs(&self, blob_ids: &[BlobId]) -> Result<Vec<Option<Arc<Blob>>>, ViewError>;
108
109    /// Reads the blob state with the given blob ID.
110    async fn read_blob_state(&self, blob_id: BlobId) -> Result<Option<BlobState>, ViewError>;
111
112    /// Reads the blob states with the given blob IDs.
113    async fn read_blob_states(
114        &self,
115        blob_ids: &[BlobId],
116    ) -> Result<Vec<Option<BlobState>>, ViewError>;
117
118    /// Writes the given blob.
119    async fn write_blob(&self, blob: &Blob) -> Result<(), ViewError>;
120
121    /// Writes blobs and certificate
122    async fn write_blobs_and_certificate(
123        &self,
124        blobs: &[Blob],
125        certificate: &ConfirmedBlockCertificate,
126    ) -> Result<(), ViewError>;
127
128    /// Writes the given blobs, but only if they already have a blob state. Returns `true` for the
129    /// blobs that were written.
130    async fn maybe_write_blobs(&self, blobs: &[Blob]) -> Result<Vec<bool>, ViewError>;
131
132    /// Attempts to write the given blob state. Returns the latest `Epoch` to have used this blob.
133    async fn maybe_write_blob_states(
134        &self,
135        blob_ids: &[BlobId],
136        blob_state: BlobState,
137    ) -> Result<(), ViewError>;
138
139    /// Writes several blobs.
140    async fn write_blobs(&self, blobs: &[Blob]) -> Result<(), ViewError>;
141
142    /// Tests existence of the certificate with the given hash.
143    async fn contains_certificate(&self, hash: CryptoHash) -> Result<bool, ViewError>;
144
145    /// Inserts a certificate into the in-memory dedup cache and returns the
146    /// canonical [`Arc`]. If the cache already holds an `Arc` for this hash,
147    /// the passed-in `certificate` is dropped and the existing `Arc` is
148    /// returned. This must be used (rather than `Arc::new`) for any
149    /// freshly-constructed [`ConfirmedBlockCertificate`] that should
150    /// participate in the "one allocation per content" invariant.
151    fn cache_certificate(
152        &self,
153        certificate: ConfirmedBlockCertificate,
154    ) -> Arc<ConfirmedBlockCertificate>;
155
156    /// Inserts a blob into the in-memory dedup cache and returns the canonical
157    /// [`Arc`]. If the cache already holds an `Arc` for this blob ID, the
158    /// passed-in `blob` is dropped and the existing `Arc` is returned. This
159    /// must be used (rather than `Arc::new`) for any freshly-constructed
160    /// [`Blob`] that should participate in the "one allocation per content"
161    /// invariant.
162    fn cache_blob(&self, blob: Blob) -> Arc<Blob>;
163
164    /// Inserts a confirmed block into the in-memory dedup cache and returns
165    /// the canonical [`Arc`]. If the cache already holds an `Arc` for this
166    /// hash, the passed-in `block` is dropped and the existing `Arc` is
167    /// returned. This must be used (rather than `Arc::new`) for any
168    /// freshly-constructed [`ConfirmedBlock`] that should participate in the
169    /// "one allocation per content" invariant.
170    fn cache_confirmed_block(&self, block: ConfirmedBlock) -> Arc<ConfirmedBlock>;
171
172    /// Reads the certificate with the given hash.
173    async fn read_certificate(
174        &self,
175        hash: CryptoHash,
176    ) -> Result<Option<Arc<ConfirmedBlockCertificate>>, ViewError>;
177
178    /// Reads a number of certificates
179    async fn read_certificates(
180        &self,
181        hashes: &[CryptoHash],
182    ) -> Result<Vec<Option<Arc<ConfirmedBlockCertificate>>>, ViewError>;
183
184    /// Reads raw certificate bytes by hashes.
185    ///
186    /// Returns a vector where each element corresponds to the input hash.
187    /// Elements are `None` if no certificate exists for that hash.
188    /// Each found certificate is returned as `Some((lite_certificate_bytes, confirmed_block_bytes))`.
189    async fn read_certificates_raw(
190        &self,
191        hashes: &[CryptoHash],
192    ) -> Result<Vec<Option<Arc<(Vec<u8>, Vec<u8>)>>>, ViewError>;
193
194    /// Reads certificates by heights for a given chain.
195    /// Returns a vector where each element corresponds to the input height.
196    /// Elements are `None` if no certificate exists at that height.
197    async fn read_certificates_by_heights(
198        &self,
199        chain_id: ChainId,
200        heights: &[BlockHeight],
201    ) -> Result<Vec<Option<Arc<ConfirmedBlockCertificate>>>, ViewError>;
202
203    /// Reads raw certificates by heights for a given chain.
204    /// Returns a vector where each element corresponds to the input height.
205    /// Elements are `None` if no certificate exists at that height.
206    /// Each found certificate is returned as a tuple of (lite_certificate_bytes, confirmed_block_bytes).
207    async fn read_certificates_by_heights_raw(
208        &self,
209        chain_id: ChainId,
210        heights: &[BlockHeight],
211    ) -> Result<Vec<Option<Arc<(Vec<u8>, Vec<u8>)>>>, ViewError>;
212
213    /// Returns a vector of certificate hashes for the requested chain and heights.
214    /// The resulting vector maintains the order of the input `heights` argument.
215    /// Elements are `None` if no certificate exists at that height.
216    async fn read_certificate_hashes_by_heights(
217        &self,
218        chain_id: ChainId,
219        heights: &[BlockHeight],
220    ) -> Result<Vec<Option<CryptoHash>>, ViewError>;
221
222    /// Looks up the block heights where the given events were published.
223    /// Returns `None` for events that are not in the index.
224    async fn read_event_block_heights(
225        &self,
226        event_ids: &[EventId],
227    ) -> Result<Vec<Option<BlockHeight>>, ViewError>;
228
229    /// Reads the event with the given ID.
230    async fn read_event(&self, id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError>;
231
232    /// Tests existence of the event with the given ID.
233    async fn contains_event(&self, id: EventId) -> Result<bool, ViewError>;
234
235    /// Lists all the events from a starting index
236    async fn read_events_from_index(
237        &self,
238        chain_id: &ChainId,
239        stream_id: &StreamId,
240        start_index: u32,
241    ) -> Result<Vec<IndexAndEvent>, ViewError>;
242
243    /// Writes a vector of events.
244    async fn write_events(
245        &self,
246        events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
247    ) -> Result<(), ViewError>;
248
249    /// Reads the network description.
250    async fn read_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
251
252    /// Writes the network description.
253    async fn write_network_description(
254        &self,
255        information: &NetworkDescription,
256    ) -> Result<(), ViewError>;
257
258    /// Initializes a chain in a simple way (used for testing and to create a genesis state).
259    ///
260    /// # Notes
261    ///
262    /// This method creates a new [`ChainStateView`] instance. If there are multiple instances of
263    /// the same chain active at any given moment, they will race to access persistent storage.
264    /// This can lead to invalid states and data corruption.
265    async fn create_chain(&self, description: ChainDescription) -> Result<(), ChainError>
266    where
267        ChainRuntimeContext<Self>: ExecutionRuntimeContext,
268    {
269        let id = description.id();
270        // Store the description blob and a `Genesis` blob state for it. The blob
271        // is not published by any block, so its provenance is the genesis config
272        // rather than a particular `(chain_id, block_height)`.
273        let description_blob = Blob::new_chain_description(&description);
274        let description_blob_id = description_blob.id();
275        self.write_blob(&description_blob).await?;
276        self.maybe_write_blob_states(&[description_blob_id], BlobState::GENESIS)
277            .await?;
278        let mut chain = self.load_chain(id).await?;
279        assert!(
280            !chain.is_active().await?,
281            "Attempting to create a chain twice"
282        );
283        let current_time = self.clock().current_time();
284        chain.initialize_if_needed(current_time).await?;
285        chain.save().await?;
286        Ok(())
287    }
288
289    /// Selects the WebAssembly runtime to use for applications (if any).
290    fn wasm_runtime(&self) -> Option<WasmRuntime>;
291
292    /// Creates a [`UserContractCode`] instance using the bytecode in storage referenced
293    /// by the `application_description`.
294    async fn load_contract(
295        &self,
296        application_description: &ApplicationDescription,
297        txn_tracker: &TransactionTracker,
298    ) -> Result<UserContractCode, ExecutionError> {
299        let contract_bytecode_blob_id = application_description.contract_bytecode_blob_id();
300        let content = match txn_tracker.get_blob_content(&contract_bytecode_blob_id) {
301            Some(content) => content.clone(),
302            None => self
303                .read_blob(contract_bytecode_blob_id)
304                .await?
305                .ok_or(ExecutionError::BlobsNotFound(vec![
306                    contract_bytecode_blob_id,
307                ]))?
308                .content()
309                .clone(),
310        };
311        let compressed_contract_bytecode = CompressedBytecode {
312            compressed_bytes: content.into_arc_bytes(),
313        };
314        #[cfg_attr(not(any(with_wasm_runtime, with_revm)), allow(unused_variables))]
315        let contract_bytecode = self
316            .thread_pool()
317            .run_send((), move |()| async move {
318                compressed_contract_bytecode.decompress()
319            })
320            .await
321            .await??;
322        match application_description.module_id.vm_runtime {
323            VmRuntime::Wasm => {
324                cfg_if::cfg_if! {
325                    if #[cfg(with_wasm_runtime)] {
326                        let Some(wasm_runtime) = self.wasm_runtime() else {
327                            panic!("A Wasm runtime is required to load user applications.");
328                        };
329                        Ok(WasmContractModule::new(contract_bytecode, wasm_runtime)
330                           .await?
331                           .into())
332                    } else {
333                        panic!(
334                            "A Wasm runtime is required to load user applications. \
335                             Please enable the `wasmer` or the `wasmtime` feature flags \
336                             when compiling `linera-storage`."
337                        );
338                    }
339                }
340            }
341            VmRuntime::Evm => {
342                cfg_if::cfg_if! {
343                    if #[cfg(with_revm)] {
344                        let evm_runtime = EvmRuntime::Revm;
345                        Ok(EvmContractModule::new(contract_bytecode, evm_runtime)?
346                           .into())
347                    } else {
348                        panic!(
349                            "An Evm runtime is required to load user applications. \
350                             Please enable the `revm` feature flag \
351                             when compiling `linera-storage`."
352                        );
353                    }
354                }
355            }
356        }
357    }
358
359    /// Creates a [`UserServiceCode`] instance using the bytecode in storage referenced
360    /// by the `application_description`.
361    async fn load_service(
362        &self,
363        application_description: &ApplicationDescription,
364        txn_tracker: &TransactionTracker,
365    ) -> Result<UserServiceCode, ExecutionError> {
366        let service_bytecode_blob_id = application_description.service_bytecode_blob_id();
367        let content = match txn_tracker.get_blob_content(&service_bytecode_blob_id) {
368            Some(content) => content.clone(),
369            None => self
370                .read_blob(service_bytecode_blob_id)
371                .await?
372                .ok_or(ExecutionError::BlobsNotFound(vec![
373                    service_bytecode_blob_id,
374                ]))?
375                .content()
376                .clone(),
377        };
378        let compressed_service_bytecode = CompressedBytecode {
379            compressed_bytes: content.into_arc_bytes(),
380        };
381        #[cfg_attr(not(any(with_wasm_runtime, with_revm)), allow(unused_variables))]
382        let service_bytecode = self
383            .thread_pool()
384            .run_send((), move |()| async move {
385                compressed_service_bytecode.decompress()
386            })
387            .await
388            .await??;
389        match application_description.module_id.vm_runtime {
390            VmRuntime::Wasm => {
391                cfg_if::cfg_if! {
392                    if #[cfg(with_wasm_runtime)] {
393                        let Some(wasm_runtime) = self.wasm_runtime() else {
394                            panic!("A Wasm runtime is required to load user applications.");
395                        };
396                        Ok(WasmServiceModule::new(service_bytecode, wasm_runtime)
397                           .await?
398                           .into())
399                    } else {
400                        panic!(
401                            "A Wasm runtime is required to load user applications. \
402                             Please enable the `wasmer` or the `wasmtime` feature flags \
403                             when compiling `linera-storage`."
404                        );
405                    }
406                }
407            }
408            VmRuntime::Evm => {
409                cfg_if::cfg_if! {
410                    if #[cfg(with_revm)] {
411                        let evm_runtime = EvmRuntime::Revm;
412                        Ok(EvmServiceModule::new(service_bytecode, evm_runtime)?
413                           .into())
414                    } else {
415                        panic!(
416                            "An Evm runtime is required to load user applications. \
417                             Please enable the `revm` feature flag \
418                             when compiling `linera-storage`."
419                        );
420                    }
421                }
422            }
423        }
424    }
425
426    /// Returns the storage context used by the block exporter with the given ID.
427    async fn block_exporter_context(
428        &self,
429        block_exporter_id: u32,
430    ) -> Result<Self::BlockExporterContext, ViewError>;
431
432    /// Returns the process-wide committee cache shared by all chains.
433    fn shared_committees(&self) -> &SharedCommittees;
434
435    /// Returns the committee whose serialized form hashes to `hash`, loading it
436    /// from the blob store on cache miss.
437    async fn get_or_load_committee_by_hash(
438        &self,
439        hash: CryptoHash,
440    ) -> Result<StdArc<Committee>, ExecutionError> {
441        if let Some(committee) = self.shared_committees().get(hash) {
442            return Ok(committee);
443        }
444        let blob_id = BlobId::new(hash, BlobType::Committee);
445        let blob = self
446            .read_blob(blob_id)
447            .await?
448            .ok_or(ExecutionError::BlobsNotFound(vec![blob_id]))?;
449        let committee = bcs::from_bytes(blob.bytes())?;
450        Ok(self
451            .shared_committees()
452            .insert(hash, StdArc::new(committee)))
453    }
454
455    /// Returns whether the given epoch's committee has been revoked, i.e. whether the
456    /// admin chain has written a `REMOVED_EPOCH_STREAM` event for it.
457    async fn is_epoch_revoked(&self, epoch: Epoch) -> Result<bool, ExecutionError> {
458        let net_desc = self
459            .read_network_description()
460            .await?
461            .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
462        let event_id = EventId {
463            chain_id: net_desc.admin_chain_id,
464            stream_id: StreamId::system(linera_execution::system::REMOVED_EPOCH_STREAM_NAME),
465            index: epoch.0,
466        };
467        Ok(self.contains_event(event_id).await?)
468    }
469
470    /// Returns the committee that signs blocks in the given epoch, looking up its blob
471    /// hash via the admin chain's epoch event stream (or the genesis committee for
472    /// epoch 0). Returns `Ok(None)` if the corresponding event has not been written
473    /// to local storage yet.
474    async fn committee_for_epoch(
475        &self,
476        epoch: Epoch,
477    ) -> Result<Option<StdArc<Committee>>, ExecutionError> {
478        let blob_hash = if epoch == Epoch::ZERO {
479            self.read_network_description()
480                .await?
481                .ok_or(ExecutionError::NoNetworkDescriptionFound)?
482                .genesis_committee_blob_hash
483        } else {
484            let net_desc = self
485                .read_network_description()
486                .await?
487                .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
488            let event_id = EventId {
489                chain_id: net_desc.admin_chain_id,
490                stream_id: StreamId::system(linera_execution::system::EPOCH_STREAM_NAME),
491                index: epoch.0,
492            };
493            let Some(bytes) = self.read_event(event_id).await? else {
494                return Ok(None);
495            };
496            let event_data: linera_execution::system::EpochEventData = bcs::from_bytes(&bytes)?;
497            event_data.blob_hash
498        };
499        Ok(Some(self.get_or_load_committee_by_hash(blob_hash).await?))
500    }
501
502    /// Lists the blob IDs in storage.
503    async fn list_blob_ids(&self) -> Result<Vec<BlobId>, ViewError>;
504
505    /// Lists the chain IDs in storage.
506    async fn list_chain_ids(&self) -> Result<Vec<ChainId>, ViewError>;
507
508    /// Lists the event IDs in storage.
509    async fn list_event_ids(&self) -> Result<Vec<EventId>, ViewError>;
510}
511
512/// The result of processing the obtained read certificates.
513pub enum ResultReadCertificates {
514    /// All requested certificates were found.
515    Certificates(Vec<ConfirmedBlockCertificate>),
516    /// Some hashes did not correspond to a stored certificate.
517    InvalidHashes(Vec<CryptoHash>),
518}
519
520impl ResultReadCertificates {
521    /// Creating the processed read certificates.
522    pub fn new(
523        certificates: Vec<Option<Arc<ConfirmedBlockCertificate>>>,
524        hashes: Vec<CryptoHash>,
525    ) -> Self {
526        let (certificates, invalid_hashes) = certificates
527            .into_iter()
528            .zip(hashes)
529            .partition_map::<Vec<_>, Vec<_>, _, _, _>(|(certificate, hash)| match certificate {
530                Some(cert) => itertools::Either::Left(Arc::unwrap_or_clone(cert)),
531                None => itertools::Either::Right(hash),
532            });
533        if invalid_hashes.is_empty() {
534            Self::Certificates(certificates)
535        } else {
536            Self::InvalidHashes(invalid_hashes)
537        }
538    }
539}
540
541/// An implementation of `ExecutionRuntimeContext` suitable for the core protocol.
542#[derive(Clone)]
543pub struct ChainRuntimeContext<S> {
544    storage: S,
545    chain_id: ChainId,
546    thread_pool: StdArc<linera_execution::ThreadPool>,
547    execution_runtime_config: ExecutionRuntimeConfig,
548    user_contracts: StdArc<papaya::HashMap<ApplicationId, UserContractCode>>,
549    user_services: StdArc<papaya::HashMap<ApplicationId, UserServiceCode>>,
550}
551
552#[cfg_attr(not(web), async_trait)]
553#[cfg_attr(web, async_trait(?Send))]
554impl<S: Storage> ExecutionRuntimeContext for ChainRuntimeContext<S> {
555    fn chain_id(&self) -> ChainId {
556        self.chain_id
557    }
558
559    fn thread_pool(&self) -> &StdArc<linera_execution::ThreadPool> {
560        &self.thread_pool
561    }
562
563    fn execution_runtime_config(&self) -> linera_execution::ExecutionRuntimeConfig {
564        self.execution_runtime_config
565    }
566
567    fn user_contracts(&self) -> &StdArc<papaya::HashMap<ApplicationId, UserContractCode>> {
568        &self.user_contracts
569    }
570
571    fn user_services(&self) -> &StdArc<papaya::HashMap<ApplicationId, UserServiceCode>> {
572        &self.user_services
573    }
574
575    async fn get_user_contract(
576        &self,
577        description: &ApplicationDescription,
578        txn_tracker: &TransactionTracker,
579    ) -> Result<UserContractCode, ExecutionError> {
580        let application_id = description.into();
581        let pinned = self.user_contracts.pin_owned();
582        if let Some(contract) = pinned.get(&application_id) {
583            return Ok(contract.clone());
584        }
585        let contract = self.storage.load_contract(description, txn_tracker).await?;
586        pinned.insert(application_id, contract.clone());
587        Ok(contract)
588    }
589
590    async fn get_user_service(
591        &self,
592        description: &ApplicationDescription,
593        txn_tracker: &TransactionTracker,
594    ) -> Result<UserServiceCode, ExecutionError> {
595        let application_id = description.into();
596        let pinned = self.user_services.pin_owned();
597        if let Some(service) = pinned.get(&application_id) {
598            return Ok(service.clone());
599        }
600        let service = self.storage.load_service(description, txn_tracker).await?;
601        pinned.insert(application_id, service.clone());
602        Ok(service)
603    }
604
605    async fn get_blob(&self, blob_id: BlobId) -> Result<Option<StdArc<Blob>>, ViewError> {
606        Ok(self.storage.read_blob(blob_id).await?.map(Arc::into_std))
607    }
608
609    async fn get_event(&self, event_id: EventId) -> Result<Option<StdArc<Vec<u8>>>, ViewError> {
610        Ok(self.storage.read_event(event_id).await?.map(Arc::into_std))
611    }
612
613    async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
614        self.storage.read_network_description().await
615    }
616
617    async fn get_or_load_committee_by_hash(
618        &self,
619        hash: CryptoHash,
620    ) -> Result<StdArc<Committee>, ExecutionError> {
621        self.storage.get_or_load_committee_by_hash(hash).await
622    }
623
624    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
625        self.storage.contains_blob(blob_id).await
626    }
627
628    async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
629        self.storage.contains_event(event_id).await
630    }
631
632    #[cfg(with_testing)]
633    async fn add_blobs(
634        &self,
635        blobs: impl IntoIterator<Item = Blob> + Send,
636    ) -> Result<(), ViewError> {
637        let blobs = Vec::from_iter(blobs);
638        self.storage.write_blobs(&blobs).await
639    }
640
641    #[cfg(with_testing)]
642    async fn add_events(
643        &self,
644        events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
645    ) -> Result<(), ViewError> {
646        self.storage.write_events(events).await
647    }
648}
649
650/// A clock that can be used to get the current `Timestamp`.
651#[cfg_attr(not(web), async_trait)]
652#[cfg_attr(web, async_trait(?Send))]
653pub trait Clock {
654    /// Returns the current time.
655    fn current_time(&self) -> Timestamp;
656
657    /// Waits until the given timestamp is reached.
658    async fn sleep_until(&self, timestamp: Timestamp);
659
660    /// Waits for the given duration, measured against this clock.
661    ///
662    /// Unlike [`linera_base::time::timer::sleep`], this honors a simulated clock (e.g. a test
663    /// clock), so callers that sleep through it can be driven deterministically in virtual time.
664    async fn sleep_for(&self, duration: Duration) {
665        self.sleep_until(
666            self.current_time()
667                .saturating_add(TimeDelta::from_duration(duration)),
668        )
669        .await
670    }
671}
672
673#[cfg(test)]
674mod tests {
675    use std::collections::BTreeMap;
676
677    use linera_base::{
678        crypto::{AccountPublicKey, CryptoHash},
679        data_types::{
680            Amount, ApplicationPermissions, Blob, BlockHeight, ChainDescription, ChainOrigin,
681            Epoch, InitialChainConfig, NetworkDescription, Round, Timestamp,
682        },
683        identifiers::{BlobId, BlobType, ChainId, EventId, StreamId},
684        ownership::ChainOwnership,
685    };
686    use linera_chain::{
687        block::{Block, ConfirmedBlock},
688        data_types::{BlockExecutionOutcome, ProposedBlock},
689    };
690    use linera_execution::{BlobOrigin, BlobState};
691    #[cfg(feature = "scylladb")]
692    use linera_views::scylla_db::ScyllaDbDatabase;
693    use linera_views::{memory::MemoryDatabase, ViewError};
694    use test_case::test_case;
695
696    use super::*;
697    use crate::db_storage::DbStorage;
698
699    /// Generic test function to test Storage trait features
700    async fn test_storage_chain_exporter<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
701    where
702        S::Context: Send + Sync,
703    {
704        // Test clock functionality
705        let _current_time = storage.clock().current_time();
706        let test_chain_id = ChainId(CryptoHash::test_hash("test_chain"));
707
708        // Test loading a chain (this creates a chain state view)
709        let _chain_view = storage.load_chain(test_chain_id).await?;
710
711        // Test block exporter context
712        let _block_exporter_context = storage.block_exporter_context(0).await?;
713        Ok(())
714    }
715
716    async fn test_storage_blob<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
717    where
718        S::Context: Send + Sync,
719    {
720        // Create test blobs
721        let chain_description = ChainDescription::new(
722            ChainOrigin::Root(0),
723            InitialChainConfig {
724                ownership: ChainOwnership::single(AccountPublicKey::test_key(0).into()),
725                epoch: Epoch::ZERO,
726                balance: Amount::ZERO,
727                application_permissions: ApplicationPermissions::default(),
728            },
729            Timestamp::from(0),
730        );
731
732        let test_blob1 = Blob::new_chain_description(&chain_description);
733        let test_blob2 = Blob::new_data(vec![10, 20, 30]);
734        let test_blob3 = Blob::new_data(vec![40, 50, 60]);
735
736        // Testing blobs existence
737        let blob_id1 = test_blob1.id();
738        let blob_id2 = test_blob2.id();
739        let blob_id3 = test_blob3.id();
740
741        // Test blob existence before writing
742        assert!(!storage.contains_blob(blob_id1).await?);
743        assert!(!storage.contains_blob(blob_id2).await?);
744        assert!(!storage.contains_blob(blob_id3).await?);
745
746        // Test single blob write
747        storage.write_blob(&test_blob1).await?;
748        assert!(storage.contains_blob(blob_id1).await?);
749
750        // Test multiple blob write (write_blobs)
751        storage
752            .write_blobs(&[test_blob2.clone(), test_blob3.clone()])
753            .await?;
754        assert!(storage.contains_blob(blob_id2).await?);
755        assert!(storage.contains_blob(blob_id3).await?);
756
757        // Test single blob read
758        let read_blob = storage.read_blob(blob_id1).await?;
759        assert_eq!(read_blob.as_deref(), Some(&test_blob1));
760
761        // Test multiple blob read (read_blobs)
762        let blob_ids = vec![blob_id1, blob_id2, blob_id3];
763        let read_blobs = storage.read_blobs(&blob_ids).await?;
764        assert_eq!(read_blobs.len(), 3);
765
766        // Verify each blob was read correctly
767        assert_eq!(read_blobs[0].as_deref(), Some(&test_blob1));
768        assert_eq!(read_blobs[1].as_deref(), Some(&test_blob2));
769        assert_eq!(read_blobs[2].as_deref(), Some(&test_blob3));
770
771        // Test missing blobs detection
772        let missing_blob_id = BlobId::new(CryptoHash::test_hash("missing"), BlobType::Data);
773        let missing_blobs = storage.missing_blobs(&[blob_id1, missing_blob_id]).await?;
774        assert_eq!(missing_blobs, vec![missing_blob_id]);
775
776        // Test maybe_write_blobs (should return false as blobs don't have blob states yet)
777        let write_results = storage
778            .maybe_write_blobs(std::slice::from_ref(&test_blob1))
779            .await?;
780        assert_eq!(write_results, vec![false]);
781
782        // Test blob state operations
783        let blob_state1 = BlobState {
784            origin: BlobOrigin::Published {
785                chain_id: ChainId(CryptoHash::test_hash("chain1")),
786                block_height: BlockHeight(0),
787            },
788            last_used_by: None,
789            epoch: Some(Epoch::ZERO),
790        };
791        let blob_state2 = BlobState {
792            origin: BlobOrigin::Published {
793                chain_id: ChainId(CryptoHash::test_hash("chain2")),
794                block_height: BlockHeight(1),
795            },
796            last_used_by: Some(CryptoHash::test_hash("cert")),
797            epoch: Some(Epoch::from(1)),
798        };
799
800        // Test blob state existence before writing
801        assert!(!storage.contains_blob_state(blob_id1).await?);
802        assert!(!storage.contains_blob_state(blob_id2).await?);
803
804        // Test blob state writing
805        storage
806            .maybe_write_blob_states(&[blob_id1], blob_state1.clone())
807            .await?;
808        storage
809            .maybe_write_blob_states(&[blob_id2], blob_state2.clone())
810            .await?;
811
812        // Test blob state existence after writing
813        assert!(storage.contains_blob_state(blob_id1).await?);
814        assert!(storage.contains_blob_state(blob_id2).await?);
815
816        // Test single blob state read
817        let read_blob_state = storage.read_blob_state(blob_id1).await?;
818        assert_eq!(read_blob_state, Some(blob_state1.clone()));
819
820        // Test multiple blob state read (read_blob_states)
821        let read_blob_states = storage.read_blob_states(&[blob_id1, blob_id2]).await?;
822        assert_eq!(read_blob_states.len(), 2);
823
824        // Verify blob states
825        assert_eq!(read_blob_states[0], Some(blob_state1));
826        assert_eq!(read_blob_states[1], Some(blob_state2));
827
828        // Test maybe_write_blobs now that blob states exist (should return true)
829        let write_results = storage
830            .maybe_write_blobs(std::slice::from_ref(&test_blob1))
831            .await?;
832        assert_eq!(write_results, vec![true]);
833
834        Ok(())
835    }
836
837    async fn test_storage_certificate<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
838    where
839        S::Context: Send + Sync,
840    {
841        let cert_hash = CryptoHash::test_hash("certificate");
842
843        // Test certificate existence (should be false initially)
844        assert!(!storage.contains_certificate(cert_hash).await?);
845
846        // Test reading non-existent certificate
847        assert!(storage.read_certificate(cert_hash).await?.is_none());
848
849        // Test reading multiple certificates
850        let cert_hashes = vec![cert_hash, CryptoHash::test_hash("cert2")];
851        let certs_result = storage.read_certificates(&cert_hashes).await?;
852        assert_eq!(certs_result.len(), 2);
853        assert!(certs_result[0].is_none());
854        assert!(certs_result[1].is_none());
855
856        // Test raw certificate reading
857        let raw_certs_result = storage.read_certificates_raw(&cert_hashes).await?;
858        assert!(raw_certs_result.iter().all(|cert| cert.is_none())); // No certificates exist
859
860        // Test confirmed block reading
861        let block_hash = CryptoHash::test_hash("block");
862        let block_result = storage.read_confirmed_block(block_hash).await?;
863        assert!(block_result.is_none());
864
865        // Test write_blobs_and_certificate functionality
866        // Create test blobs
867        let test_blob1 = Blob::new_data(vec![1, 2, 3]);
868        let test_blob2 = Blob::new_data(vec![4, 5, 6]);
869        let blobs = vec![test_blob1, test_blob2];
870
871        // Create a test certificate using the working pattern from linera-indexer tests
872        let chain_id = ChainId(CryptoHash::test_hash("test_chain_cert"));
873
874        // Create a minimal proposed block (genesis block)
875        let proposed_block = ProposedBlock {
876            epoch: Epoch::ZERO,
877            chain_id,
878            transactions: vec![],
879            previous_block_hash: None,
880            height: BlockHeight::ZERO,
881            authenticated_owner: None,
882            timestamp: Timestamp::default(),
883        };
884
885        // Create a minimal block execution outcome with proper BTreeMap types
886        let outcome = BlockExecutionOutcome {
887            messages: vec![],
888            state_hash: CryptoHash::default(),
889            oracle_responses: vec![],
890            events: vec![],
891            blobs: vec![],
892            operation_results: vec![],
893            previous_event_blocks: BTreeMap::new(),
894            previous_message_blocks: BTreeMap::new(),
895        };
896
897        let block = Block::new(proposed_block, outcome);
898        let confirmed_block = ConfirmedBlock::new(block);
899        let certificate = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
900
901        // Test writing blobs and certificate together
902        storage
903            .write_blobs_and_certificate(&blobs, &certificate)
904            .await?;
905
906        // Verify the certificate was written
907        let cert_hash = certificate.hash();
908        assert!(storage.contains_certificate(cert_hash).await?);
909
910        // Verify the certificate can be read back
911        let read_certificate = storage.read_certificate(cert_hash).await?;
912        assert!(read_certificate.is_some());
913        assert_eq!(read_certificate.unwrap().hash(), cert_hash);
914
915        // Verify the blobs were written
916        for blob in &blobs {
917            assert!(storage.contains_blob(blob.id()).await?);
918        }
919
920        Ok(())
921    }
922
923    async fn test_storage_event<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
924    where
925        S::Context: Send + Sync,
926    {
927        let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
928        let stream_id = StreamId::system("test_stream");
929
930        // Test multiple events
931        let event_id1 = EventId {
932            chain_id,
933            stream_id: stream_id.clone(),
934            index: 0,
935        };
936        let event_id2 = EventId {
937            chain_id,
938            stream_id: stream_id.clone(),
939            index: 1,
940        };
941        let event_id3 = EventId {
942            chain_id,
943            stream_id: stream_id.clone(),
944            index: 2,
945        };
946
947        let event_data1 = vec![1, 2, 3];
948        let event_data2 = vec![4, 5, 6];
949        let event_data3 = vec![7, 8, 9];
950
951        // Test event existence before writing
952        assert!(!storage.contains_event(event_id1.clone()).await?);
953        assert!(!storage.contains_event(event_id2.clone()).await?);
954
955        // Write multiple events
956        storage
957            .write_events([
958                (event_id1.clone(), event_data1.clone()),
959                (event_id2.clone(), event_data2.clone()),
960                (event_id3.clone(), event_data3.clone()),
961            ])
962            .await?;
963
964        // Test event existence after writing
965        assert!(storage.contains_event(event_id1.clone()).await?);
966        assert!(storage.contains_event(event_id2.clone()).await?);
967        assert!(storage.contains_event(event_id3.clone()).await?);
968
969        // Test individual event reading
970        let read_event1 = storage.read_event(event_id1).await?;
971        assert_eq!(read_event1.as_deref(), Some(&event_data1));
972
973        let read_event2 = storage.read_event(event_id2).await?;
974        assert_eq!(read_event2.as_deref(), Some(&event_data2));
975
976        // Test reading events from index
977        let events_from_index = storage
978            .read_events_from_index(&chain_id, &stream_id, 1)
979            .await?;
980        assert!(events_from_index.len() >= 2); // Should contain events at index 1 and 2
981        Ok(())
982    }
983
984    async fn test_storage_network_description<S: Storage + Sync>(
985        storage: &S,
986    ) -> Result<(), ViewError>
987    where
988        S::Context: Send + Sync,
989    {
990        let admin_chain_id = ChainId(CryptoHash::test_hash("test_chain_second"));
991
992        let network_desc = NetworkDescription {
993            name: "test_network".to_string(),
994            genesis_config_hash: CryptoHash::test_hash("genesis_config"),
995            genesis_timestamp: Timestamp::from(0),
996            genesis_committee_blob_hash: CryptoHash::test_hash("committee"),
997            admin_chain_id,
998        };
999
1000        // Test reading non-existent network description
1001        assert!(storage.read_network_description().await?.is_none());
1002
1003        // Write network description
1004        storage.write_network_description(&network_desc).await?;
1005
1006        // Test reading existing network description
1007        let read_desc = storage.read_network_description().await?;
1008        assert_eq!(read_desc, Some(network_desc));
1009
1010        Ok(())
1011    }
1012
1013    /// Generic test function to test Storage trait features
1014    #[test_case(DbStorage::<MemoryDatabase, _>::make_test_storage(None).await; "memory")]
1015    #[cfg_attr(feature = "scylladb", test_case(DbStorage::<ScyllaDbDatabase, _>::make_test_storage(None).await; "scylla_db"))]
1016    #[test_log::test(tokio::test)]
1017    async fn test_storage_features<S: Storage + Sync>(storage: S) -> Result<(), ViewError>
1018    where
1019        S::Context: Send + Sync,
1020    {
1021        test_storage_chain_exporter(&storage).await?;
1022        test_storage_blob(&storage).await?;
1023        test_storage_certificate(&storage).await?;
1024        test_storage_event(&storage).await?;
1025        test_storage_network_description(&storage).await?;
1026        Ok(())
1027    }
1028}