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;
9mod migration;
10
11use std::sync::Arc as StdArc;
12
13use async_trait::async_trait;
14use itertools::Itertools;
15use linera_base::{
16    crypto::CryptoHash,
17    data_types::{
18        ApplicationDescription, Blob, BlockHeight, ChainDescription, CompressedBytecode, Epoch,
19        NetworkDescription, TimeDelta, Timestamp,
20    },
21    identifiers::{ApplicationId, BlobId, BlobType, ChainId, EventId, IndexAndEvent, StreamId},
22    time::Duration,
23    vm::VmRuntime,
24};
25pub use linera_cache::{Arc, DEFAULT_CLEANUP_INTERVAL_SECS};
26use linera_chain::{
27    types::{ConfirmedBlock, ConfirmedBlockCertificate},
28    ChainError, ChainStateView,
29};
30use linera_execution::{
31    committee::Committee, system::EPOCH_STREAM_NAME, BlobState, ExecutionError,
32    ExecutionRuntimeConfig, ExecutionRuntimeContext, SharedCommittees, TransactionTracker,
33    UserContractCode, UserServiceCode, WasmRuntime,
34};
35#[cfg(with_revm)]
36use linera_execution::{
37    evm::revm::{EvmContractModule, EvmServiceModule},
38    EvmRuntime,
39};
40#[cfg(with_wasm_runtime)]
41use linera_execution::{WasmContractModule, WasmServiceModule};
42use linera_views::{context::Context, views::RootView, ViewError};
43
44#[cfg(with_metrics)]
45pub use crate::db_storage::metrics;
46#[cfg(with_testing)]
47pub use crate::db_storage::TestClock;
48pub use crate::db_storage::{
49    ChainStatesFirstAssignment, DbStorage, RootKey, StorageCacheConfig, StorageCaches, WallClock,
50};
51
52/// The default namespace to be used when none is specified
53pub const DEFAULT_NAMESPACE: &str = "table_linera";
54
55/// Communicate with a persistent storage using the "views" abstraction.
56#[cfg_attr(not(web), async_trait)]
57#[cfg_attr(web, async_trait(?Send))]
58pub trait Storage: linera_base::util::traits::AutoTraits + Sized {
59    /// The low-level storage implementation in use by the core protocol (chain workers etc).
60    type Context: Context<Extra = ChainRuntimeContext<Self>> + Clone + 'static;
61
62    /// The clock type being used.
63    type Clock: Clock + Clone + Send + Sync;
64
65    /// The low-level storage implementation in use by the block exporter.
66    type BlockExporterContext: Context<Extra = u32> + Clone;
67
68    /// Returns the current wall clock time.
69    fn clock(&self) -> &Self::Clock;
70
71    /// Returns the thread pool used to offload blocking work.
72    fn thread_pool(&self) -> &StdArc<linera_execution::ThreadPool>;
73
74    /// Loads the view of a chain state.
75    ///
76    /// # Notes
77    ///
78    /// Each time this method is called, a new [`ChainStateView`] is created. If there are multiple
79    /// instances of the same chain active at any given moment, they will race to access persistent
80    /// storage. This can lead to invalid states and data corruption.
81    async fn load_chain(&self, id: ChainId) -> Result<ChainStateView<Self::Context>, ViewError>;
82
83    /// Tests the existence of a blob with the given blob ID.
84    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
85
86    /// Returns what blobs from the input are missing from storage.
87    async fn missing_blobs(&self, blob_ids: &[BlobId]) -> Result<Vec<BlobId>, ViewError>;
88
89    /// Tests existence of a blob state with the given blob ID.
90    async fn contains_blob_state(&self, blob_id: BlobId) -> Result<bool, ViewError>;
91
92    /// Reads the hashed certificate value with the given hash.
93    async fn read_confirmed_block(
94        &self,
95        hash: CryptoHash,
96    ) -> Result<Option<Arc<ConfirmedBlock>>, ViewError>;
97
98    /// Reads a number of confirmed blocks by their hashes.
99    async fn read_confirmed_blocks<I: IntoIterator<Item = CryptoHash> + Send>(
100        &self,
101        hashes: I,
102    ) -> Result<Vec<Option<Arc<ConfirmedBlock>>>, ViewError>;
103
104    /// Reads the blob with the given blob ID.
105    async fn read_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError>;
106
107    /// Reads the blobs with the given blob IDs.
108    async fn read_blobs(&self, blob_ids: &[BlobId]) -> Result<Vec<Option<Arc<Blob>>>, ViewError>;
109
110    /// Reads the blob state with the given blob ID.
111    async fn read_blob_state(&self, blob_id: BlobId) -> Result<Option<BlobState>, ViewError>;
112
113    /// Reads the blob states with the given blob IDs.
114    async fn read_blob_states(
115        &self,
116        blob_ids: &[BlobId],
117    ) -> Result<Vec<Option<BlobState>>, ViewError>;
118
119    /// Writes the given blob.
120    async fn write_blob(&self, blob: &Blob) -> Result<(), ViewError>;
121
122    /// Writes blobs and certificate
123    async fn write_blobs_and_certificate(
124        &self,
125        blobs: &[Blob],
126        certificate: &ConfirmedBlockCertificate,
127    ) -> Result<(), ViewError>;
128
129    /// Writes the given blobs, but only if they already have a blob state. Returns `true` for the
130    /// blobs that were written.
131    async fn maybe_write_blobs(&self, blobs: &[Blob]) -> Result<Vec<bool>, ViewError>;
132
133    /// Attempts to write the given blob state. Returns the latest `Epoch` to have used this blob.
134    async fn maybe_write_blob_states(
135        &self,
136        blob_ids: &[BlobId],
137        blob_state: BlobState,
138    ) -> Result<(), ViewError>;
139
140    /// Writes several blobs.
141    async fn write_blobs(&self, blobs: &[Blob]) -> Result<(), ViewError>;
142
143    /// Tests existence of the certificate with the given hash.
144    async fn contains_certificate(&self, hash: CryptoHash) -> Result<bool, ViewError>;
145
146    /// Inserts a certificate into the in-memory dedup cache and returns the
147    /// canonical [`Arc`]. If the cache already holds an `Arc` for this hash,
148    /// the passed-in `certificate` is dropped and the existing `Arc` is
149    /// returned. This must be used (rather than `Arc::new`) for any
150    /// freshly-constructed [`ConfirmedBlockCertificate`] that should
151    /// participate in the "one allocation per content" invariant.
152    fn cache_certificate(
153        &self,
154        certificate: ConfirmedBlockCertificate,
155    ) -> Arc<ConfirmedBlockCertificate>;
156
157    /// Inserts a blob into the in-memory dedup cache and returns the canonical
158    /// [`Arc`]. If the cache already holds an `Arc` for this blob ID, the
159    /// passed-in `blob` is dropped and the existing `Arc` is returned. This
160    /// must be used (rather than `Arc::new`) for any freshly-constructed
161    /// [`Blob`] that should participate in the "one allocation per content"
162    /// invariant.
163    fn cache_blob(&self, blob: Blob) -> Arc<Blob>;
164
165    /// Inserts a confirmed block into the in-memory dedup cache and returns
166    /// the canonical [`Arc`]. If the cache already holds an `Arc` for this
167    /// hash, the passed-in `block` is dropped and the existing `Arc` is
168    /// returned. This must be used (rather than `Arc::new`) for any
169    /// freshly-constructed [`ConfirmedBlock`] that should participate in the
170    /// "one allocation per content" invariant.
171    fn cache_confirmed_block(&self, block: ConfirmedBlock) -> Arc<ConfirmedBlock>;
172
173    /// Reads the certificate with the given hash.
174    async fn read_certificate(
175        &self,
176        hash: CryptoHash,
177    ) -> Result<Option<Arc<ConfirmedBlockCertificate>>, ViewError>;
178
179    /// Reads a number of certificates
180    async fn read_certificates(
181        &self,
182        hashes: &[CryptoHash],
183    ) -> Result<Vec<Option<Arc<ConfirmedBlockCertificate>>>, ViewError>;
184
185    /// Reads raw certificate bytes by hashes.
186    ///
187    /// Returns a vector where each element corresponds to the input hash.
188    /// Elements are `None` if no certificate exists for that hash.
189    /// Each found certificate is returned as `Some((lite_certificate_bytes, confirmed_block_bytes))`.
190    async fn read_certificates_raw(
191        &self,
192        hashes: &[CryptoHash],
193    ) -> Result<Vec<Option<Arc<(Vec<u8>, Vec<u8>)>>>, ViewError>;
194
195    /// Reads certificates by heights for a given chain.
196    /// Returns a vector where each element corresponds to the input height.
197    /// Elements are `None` if no certificate exists at that height.
198    async fn read_certificates_by_heights(
199        &self,
200        chain_id: ChainId,
201        heights: &[BlockHeight],
202    ) -> Result<Vec<Option<Arc<ConfirmedBlockCertificate>>>, ViewError>;
203
204    /// Reads raw certificates by heights for a given chain.
205    /// Returns a vector where each element corresponds to the input height.
206    /// Elements are `None` if no certificate exists at that height.
207    /// Each found certificate is returned as a tuple of (lite_certificate_bytes, confirmed_block_bytes).
208    async fn read_certificates_by_heights_raw(
209        &self,
210        chain_id: ChainId,
211        heights: &[BlockHeight],
212    ) -> Result<Vec<Option<Arc<(Vec<u8>, Vec<u8>)>>>, ViewError>;
213
214    /// Returns a vector of certificate hashes for the requested chain and heights.
215    /// The resulting vector maintains the order of the input `heights` argument.
216    /// Elements are `None` if no certificate exists at that height.
217    async fn read_certificate_hashes_by_heights(
218        &self,
219        chain_id: ChainId,
220        heights: &[BlockHeight],
221    ) -> Result<Vec<Option<CryptoHash>>, ViewError>;
222
223    /// Writes certificate height index entries for a given chain.
224    /// This is used to populate the height->hash index when certificates are found
225    /// via alternative methods (e.g., from chain state).
226    async fn write_certificate_height_indices(
227        &self,
228        chain_id: ChainId,
229        indices: &[(BlockHeight, CryptoHash)],
230    ) -> Result<(), ViewError>;
231
232    /// Reads the event with the given ID.
233    async fn read_event(&self, id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError>;
234
235    /// Tests existence of the event with the given ID.
236    async fn contains_event(&self, id: EventId) -> Result<bool, ViewError>;
237
238    /// Lists all the events from a starting index
239    async fn read_events_from_index(
240        &self,
241        chain_id: &ChainId,
242        stream_id: &StreamId,
243        start_index: u32,
244    ) -> Result<Vec<IndexAndEvent>, ViewError>;
245
246    /// Writes a vector of events.
247    async fn write_events(
248        &self,
249        events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
250    ) -> Result<(), ViewError>;
251
252    /// Reads the network description.
253    async fn read_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
254
255    /// Writes the network description.
256    async fn write_network_description(
257        &self,
258        information: &NetworkDescription,
259    ) -> Result<(), ViewError>;
260
261    /// Returns the process-global cache of committees by epoch.
262    fn shared_committees(&self) -> &SharedCommittees;
263
264    /// Returns the committee for `epoch`, consulting the shared cache first
265    /// and, on a miss, loading it from the `NewCommittee` event on the admin
266    /// chain (or from the genesis committee blob for epoch 0) plus the
267    /// committee blob. Returns `Ok(None)` if the network description, event,
268    /// or blob is not yet available locally.
269    async fn get_or_load_committee(
270        &self,
271        epoch: Epoch,
272    ) -> Result<Option<StdArc<Committee>>, ViewError> {
273        if let Some(committee) = self.shared_committees().get(epoch) {
274            return Ok(Some(committee));
275        }
276        let Some(net_description) = self.read_network_description().await? else {
277            tracing::warn!(
278                ?epoch,
279                "get_or_load_committee: NetworkDescription missing in storage"
280            );
281            return Ok(None);
282        };
283        let blob_hash = if epoch.0 == 0 {
284            net_description.genesis_committee_blob_hash
285        } else {
286            let event_id = EventId {
287                chain_id: net_description.admin_chain_id,
288                stream_id: StreamId::system(EPOCH_STREAM_NAME),
289                index: epoch.0,
290            };
291            match self.read_event(event_id.clone()).await? {
292                Some(bytes) => bcs::from_bytes(&bytes)?,
293                None => {
294                    tracing::warn!(
295                        ?epoch,
296                        ?event_id,
297                        "get_or_load_committee: NewCommittee event missing in storage"
298                    );
299                    return Ok(None);
300                }
301            }
302        };
303        let blob_id = BlobId::new(blob_hash, BlobType::Committee);
304        let Some(blob) = self.read_blob(blob_id).await? else {
305            tracing::warn!(
306                ?epoch,
307                ?blob_id,
308                "get_or_load_committee: committee blob missing in storage"
309            );
310            return Ok(None);
311        };
312        let committee: Committee = bcs::from_bytes(blob.bytes())?;
313        Ok(Some(
314            self.shared_committees()
315                .insert(epoch, StdArc::new(committee)),
316        ))
317    }
318
319    /// Initializes a chain in a simple way (used for testing and to create a genesis state).
320    ///
321    /// # Notes
322    ///
323    /// This method creates a new [`ChainStateView`] instance. If there are multiple instances of
324    /// the same chain active at any given moment, they will race to access persistent storage.
325    /// This can lead to invalid states and data corruption.
326    async fn create_chain(&self, description: ChainDescription) -> Result<(), ChainError>
327    where
328        ChainRuntimeContext<Self>: ExecutionRuntimeContext,
329    {
330        let id = description.id();
331        // Store the description blob.
332        self.write_blob(&Blob::new_chain_description(&description))
333            .await?;
334        let mut chain = self.load_chain(id).await?;
335        assert!(
336            !chain.is_active().await?,
337            "Attempting to create a chain twice"
338        );
339        let current_time = self.clock().current_time();
340        chain.initialize_if_needed(current_time).await?;
341        chain.save().await?;
342        Ok(())
343    }
344
345    /// Selects the WebAssembly runtime to use for applications (if any).
346    fn wasm_runtime(&self) -> Option<WasmRuntime>;
347
348    /// Creates a [`UserContractCode`] instance using the bytecode in storage referenced
349    /// by the `application_description`.
350    async fn load_contract(
351        &self,
352        application_description: &ApplicationDescription,
353        txn_tracker: &TransactionTracker,
354    ) -> Result<UserContractCode, ExecutionError> {
355        let contract_bytecode_blob_id = application_description.contract_bytecode_blob_id();
356        let content = match txn_tracker.get_blob_content(&contract_bytecode_blob_id) {
357            Some(content) => content.clone(),
358            None => self
359                .read_blob(contract_bytecode_blob_id)
360                .await?
361                .ok_or(ExecutionError::BlobsNotFound(vec![
362                    contract_bytecode_blob_id,
363                ]))?
364                .content()
365                .clone(),
366        };
367        let compressed_contract_bytecode = CompressedBytecode {
368            compressed_bytes: content.into_arc_bytes(),
369        };
370        #[cfg_attr(not(any(with_wasm_runtime, with_revm)), allow(unused_variables))]
371        let contract_bytecode = self
372            .thread_pool()
373            .run_send((), move |()| async move {
374                compressed_contract_bytecode.decompress()
375            })
376            .await
377            .await??;
378        match application_description.module_id.vm_runtime {
379            VmRuntime::Wasm => {
380                cfg_if::cfg_if! {
381                    if #[cfg(with_wasm_runtime)] {
382                        let Some(wasm_runtime) = self.wasm_runtime() else {
383                            panic!("A Wasm runtime is required to load user applications.");
384                        };
385                        Ok(WasmContractModule::new(contract_bytecode, wasm_runtime)
386                           .await?
387                           .into())
388                    } else {
389                        panic!(
390                            "A Wasm runtime is required to load user applications. \
391                             Please enable the `wasmer` or the `wasmtime` feature flags \
392                             when compiling `linera-storage`."
393                        );
394                    }
395                }
396            }
397            VmRuntime::Evm => {
398                cfg_if::cfg_if! {
399                    if #[cfg(with_revm)] {
400                        let evm_runtime = EvmRuntime::Revm;
401                        Ok(EvmContractModule::new(contract_bytecode, evm_runtime)?
402                           .into())
403                    } else {
404                        panic!(
405                            "An Evm runtime is required to load user applications. \
406                             Please enable the `revm` feature flag \
407                             when compiling `linera-storage`."
408                        );
409                    }
410                }
411            }
412        }
413    }
414
415    /// Creates a [`linera-sdk::UserContract`] instance using the bytecode in storage referenced
416    /// by the `application_description`.
417    async fn load_service(
418        &self,
419        application_description: &ApplicationDescription,
420        txn_tracker: &TransactionTracker,
421    ) -> Result<UserServiceCode, ExecutionError> {
422        let service_bytecode_blob_id = application_description.service_bytecode_blob_id();
423        let content = match txn_tracker.get_blob_content(&service_bytecode_blob_id) {
424            Some(content) => content.clone(),
425            None => self
426                .read_blob(service_bytecode_blob_id)
427                .await?
428                .ok_or(ExecutionError::BlobsNotFound(vec![
429                    service_bytecode_blob_id,
430                ]))?
431                .content()
432                .clone(),
433        };
434        let compressed_service_bytecode = CompressedBytecode {
435            compressed_bytes: content.into_arc_bytes(),
436        };
437        #[cfg_attr(not(any(with_wasm_runtime, with_revm)), allow(unused_variables))]
438        let service_bytecode = self
439            .thread_pool()
440            .run_send((), move |()| async move {
441                compressed_service_bytecode.decompress()
442            })
443            .await
444            .await??;
445        match application_description.module_id.vm_runtime {
446            VmRuntime::Wasm => {
447                cfg_if::cfg_if! {
448                    if #[cfg(with_wasm_runtime)] {
449                        let Some(wasm_runtime) = self.wasm_runtime() else {
450                            panic!("A Wasm runtime is required to load user applications.");
451                        };
452                        Ok(WasmServiceModule::new(service_bytecode, wasm_runtime)
453                           .await?
454                           .into())
455                    } else {
456                        panic!(
457                            "A Wasm runtime is required to load user applications. \
458                             Please enable the `wasmer` or the `wasmtime` feature flags \
459                             when compiling `linera-storage`."
460                        );
461                    }
462                }
463            }
464            VmRuntime::Evm => {
465                cfg_if::cfg_if! {
466                    if #[cfg(with_revm)] {
467                        let evm_runtime = EvmRuntime::Revm;
468                        Ok(EvmServiceModule::new(service_bytecode, evm_runtime)?
469                           .into())
470                    } else {
471                        panic!(
472                            "An Evm runtime is required to load user applications. \
473                             Please enable the `revm` feature flag \
474                             when compiling `linera-storage`."
475                        );
476                    }
477                }
478            }
479        }
480    }
481
482    /// Returns the storage context used by the block exporter with the given ID.
483    async fn block_exporter_context(
484        &self,
485        block_exporter_id: u32,
486    ) -> Result<Self::BlockExporterContext, ViewError>;
487}
488
489/// The result of processing the obtained read certificates.
490pub enum ResultReadCertificates {
491    /// All requested certificates were found and successfully read.
492    Certificates(Vec<ConfirmedBlockCertificate>),
493    /// The hashes for which no certificate could be read.
494    InvalidHashes(Vec<CryptoHash>),
495}
496
497impl ResultReadCertificates {
498    /// Creating the processed read certificates.
499    pub fn new(
500        certificates: Vec<Option<Arc<ConfirmedBlockCertificate>>>,
501        hashes: Vec<CryptoHash>,
502    ) -> Self {
503        let (certificates, invalid_hashes) = certificates
504            .into_iter()
505            .zip(hashes)
506            .partition_map::<Vec<_>, Vec<_>, _, _, _>(|(certificate, hash)| match certificate {
507                Some(cert) => itertools::Either::Left(Arc::unwrap_or_clone(cert)),
508                None => itertools::Either::Right(hash),
509            });
510        if invalid_hashes.is_empty() {
511            Self::Certificates(certificates)
512        } else {
513            Self::InvalidHashes(invalid_hashes)
514        }
515    }
516}
517
518/// An implementation of `ExecutionRuntimeContext` suitable for the core protocol.
519#[derive(Clone)]
520pub struct ChainRuntimeContext<S> {
521    storage: S,
522    chain_id: ChainId,
523    thread_pool: StdArc<linera_execution::ThreadPool>,
524    execution_runtime_config: ExecutionRuntimeConfig,
525    user_contracts: StdArc<papaya::HashMap<ApplicationId, UserContractCode>>,
526    user_services: StdArc<papaya::HashMap<ApplicationId, UserServiceCode>>,
527}
528
529#[cfg_attr(not(web), async_trait)]
530#[cfg_attr(web, async_trait(?Send))]
531impl<S: Storage> ExecutionRuntimeContext for ChainRuntimeContext<S> {
532    fn chain_id(&self) -> ChainId {
533        self.chain_id
534    }
535
536    fn thread_pool(&self) -> &StdArc<linera_execution::ThreadPool> {
537        &self.thread_pool
538    }
539
540    fn execution_runtime_config(&self) -> linera_execution::ExecutionRuntimeConfig {
541        self.execution_runtime_config
542    }
543
544    fn user_contracts(&self) -> &StdArc<papaya::HashMap<ApplicationId, UserContractCode>> {
545        &self.user_contracts
546    }
547
548    fn user_services(&self) -> &StdArc<papaya::HashMap<ApplicationId, UserServiceCode>> {
549        &self.user_services
550    }
551
552    async fn get_user_contract(
553        &self,
554        description: &ApplicationDescription,
555        txn_tracker: &TransactionTracker,
556    ) -> Result<UserContractCode, ExecutionError> {
557        let application_id = description.into();
558        let pinned = self.user_contracts.pin_owned();
559        if let Some(contract) = pinned.get(&application_id) {
560            return Ok(contract.clone());
561        }
562        let contract = self.storage.load_contract(description, txn_tracker).await?;
563        pinned.insert(application_id, contract.clone());
564        Ok(contract)
565    }
566
567    async fn get_user_service(
568        &self,
569        description: &ApplicationDescription,
570        txn_tracker: &TransactionTracker,
571    ) -> Result<UserServiceCode, ExecutionError> {
572        let application_id = description.into();
573        let pinned = self.user_services.pin_owned();
574        if let Some(service) = pinned.get(&application_id) {
575            return Ok(service.clone());
576        }
577        let service = self.storage.load_service(description, txn_tracker).await?;
578        pinned.insert(application_id, service.clone());
579        Ok(service)
580    }
581
582    async fn get_blob(&self, blob_id: BlobId) -> Result<Option<StdArc<Blob>>, ViewError> {
583        Ok(self.storage.read_blob(blob_id).await?.map(Arc::into_std))
584    }
585
586    async fn get_event(&self, event_id: EventId) -> Result<Option<StdArc<Vec<u8>>>, ViewError> {
587        Ok(self.storage.read_event(event_id).await?.map(Arc::into_std))
588    }
589
590    async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
591        self.storage.read_network_description().await
592    }
593
594    async fn get_or_load_committee(
595        &self,
596        epoch: Epoch,
597    ) -> Result<Option<StdArc<Committee>>, ViewError> {
598        self.storage.get_or_load_committee(epoch).await
599    }
600
601    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
602        self.storage.contains_blob(blob_id).await
603    }
604
605    async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
606        self.storage.contains_event(event_id).await
607    }
608
609    #[cfg(with_testing)]
610    async fn add_blobs(
611        &self,
612        blobs: impl IntoIterator<Item = Blob> + Send,
613    ) -> Result<(), ViewError> {
614        let blobs = Vec::from_iter(blobs);
615        self.storage.write_blobs(&blobs).await
616    }
617
618    #[cfg(with_testing)]
619    async fn add_events(
620        &self,
621        events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
622    ) -> Result<(), ViewError> {
623        self.storage.write_events(events).await
624    }
625}
626
627/// A clock that can be used to get the current `Timestamp`.
628#[cfg_attr(not(web), async_trait)]
629#[cfg_attr(web, async_trait(?Send))]
630pub trait Clock {
631    /// Returns the current time.
632    fn current_time(&self) -> Timestamp;
633
634    /// Waits until the given timestamp is reached.
635    async fn sleep_until(&self, timestamp: Timestamp);
636
637    /// Waits for the given duration, measured against this clock.
638    ///
639    /// Unlike [`linera_base::time::timer::sleep`], this honors a simulated clock (e.g. a test
640    /// clock), so callers that sleep through it can be driven deterministically in virtual time.
641    async fn sleep_for(&self, duration: Duration) {
642        self.sleep_until(
643            self.current_time()
644                .saturating_add(TimeDelta::from_duration(duration)),
645        )
646        .await
647    }
648}