linera_views/views/
mod.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{fmt::Debug, io::Write};
5
6use async_trait::async_trait;
7use linera_base::{crypto::CryptoHash, data_types::ArithmeticError, identifiers::BlobId};
8pub use linera_views_derive::{
9    ClonableView, CryptoHashRootView, CryptoHashView, HashableView, RootView, View,
10};
11use serde::Serialize;
12use thiserror::Error;
13
14use crate::{batch::Batch, common::HasherOutput};
15
16#[cfg(test)]
17#[path = "unit_tests/views.rs"]
18mod tests;
19
20/// The `RegisterView` implements a register for a single value.
21pub mod register_view;
22
23/// The `LogView` implements a log list that can be pushed.
24pub mod log_view;
25
26/// The `BucketQueueView` implements a queue that can push on the back and delete on the front and group data in buckets.
27pub mod bucket_queue_view;
28
29/// The `QueueView` implements a queue that can push on the back and delete on the front.
30pub mod queue_view;
31
32/// The `MapView` implements a map with ordered keys.
33pub mod map_view;
34
35/// The `SetView` implements a set with ordered entries.
36pub mod set_view;
37
38/// The `CollectionView` implements a map structure whose keys are ordered and the values are views.
39pub mod collection_view;
40
41/// The `ReentrantCollectionView` implements a map structure whose keys are ordered and the values are views with concurrent access.
42pub mod reentrant_collection_view;
43
44/// The implementation of a key-value store view.
45pub mod key_value_store_view;
46
47/// Wrapping a view to compute a hash.
48pub mod hashable_wrapper;
49
50/// The minimum value for the view tags. Values in 0..MIN_VIEW_TAG are used for other purposes.
51pub const MIN_VIEW_TAG: u8 = 1;
52
53/// A view gives exclusive access to read and write the data stored at an underlying
54/// address in storage.
55#[async_trait]
56pub trait View<C>: Sized {
57    /// The number of keys used for the initialization
58    const NUM_INIT_KEYS: usize;
59
60    /// Obtains a mutable reference to the internal context.
61    fn context(&self) -> &C;
62
63    /// Creates the keys needed for loading the view
64    fn pre_load(context: &C) -> Result<Vec<Vec<u8>>, ViewError>;
65
66    /// Loads a view from the values
67    fn post_load(context: C, values: &[Option<Vec<u8>>]) -> Result<Self, ViewError>;
68
69    /// Loads a view
70    async fn load(context: C) -> Result<Self, ViewError>;
71
72    /// Discards all pending changes. After that `flush` should have no effect to storage.
73    fn rollback(&mut self);
74
75    /// Returns [`true`] if flushing this view would result in changes to the persistent storage.
76    async fn has_pending_changes(&self) -> bool;
77
78    /// Clears the view. That can be seen as resetting to default. If the clear is followed
79    /// by a flush then all the relevant data is removed on the storage.
80    fn clear(&mut self);
81
82    /// Persists changes to storage. This leaves the view still usable and is essentially neutral to the
83    /// program running. Crash-resistant storage implementations are expected to accumulate the desired
84    /// changes in the `batch` variable first. If the view is dropped without calling `flush`, staged
85    /// changes are simply lost.
86    /// The returned boolean indicates whether the operation removes the view or not.
87    fn flush(&mut self, batch: &mut Batch) -> Result<bool, ViewError>;
88
89    /// Builds a trivial view that is already deleted
90    fn new(context: C) -> Result<Self, ViewError> {
91        let values = vec![None; Self::NUM_INIT_KEYS];
92        let mut view = Self::post_load(context, &values)?;
93        view.clear();
94        Ok(view)
95    }
96}
97
98/// Main error type for the crate.
99#[derive(Error, Debug)]
100pub enum ViewError {
101    /// An error occurred during BCS serialization.
102    #[error("failed to serialize value to calculate its hash")]
103    Serialization(#[from] bcs::Error),
104
105    /// We failed to acquire an entry in a CollectionView or a ReentrantCollectionView.
106    #[error("trying to access a collection view or reentrant collection view while some entries are still being accessed")]
107    CannotAcquireCollectionEntry,
108
109    /// Input output error.
110    #[error("IO error")]
111    Io(#[from] std::io::Error),
112
113    /// Arithmetic error
114    #[error("Arithmetic error")]
115    ArithmeticError(#[from] ArithmeticError),
116
117    /// An error happened while trying to lock.
118    #[error("Failed to lock collection entry: {0:?}")]
119    TryLockError(Vec<u8>),
120
121    /// Tokio errors can happen while joining.
122    #[error("Panic in sub-task: {0}")]
123    TokioJoinError(#[from] tokio::task::JoinError),
124
125    /// Errors within the context can occur and are presented as ViewError.
126    #[error("Storage operation error in {backend}: {error}")]
127    StoreError {
128        /// backend can be e.g. RocksDB / DynamoDB / Memory / etc.
129        backend: String,
130        /// error is the specific problem that occurred within that context
131        error: String,
132    },
133
134    /// The key must not be too long
135    #[error("The key must not be too long")]
136    KeyTooLong,
137
138    /// FIXME(#148): This belongs to a future `linera_storage::StoreError`.
139    #[error("Entry does not exist in memory: {0}")]
140    NotFound(String),
141
142    /// The database is corrupt: Entries don't have the expected hash.
143    #[error("Inconsistent database entries")]
144    InconsistentEntries,
145
146    /// The database is corrupt: Some entries are missing
147    #[error("Missing database entries")]
148    MissingEntries,
149
150    /// The values are incoherent.
151    #[error("Post load values error")]
152    PostLoadValuesError,
153
154    /// The value is too large for the client
155    #[error("The value is too large for the client")]
156    TooLargeValue,
157
158    /// Blob not found when trying to read it.
159    #[error("Blob not found on storage read: {0}")]
160    BlobNotFoundOnRead(BlobId),
161}
162
163impl ViewError {
164    /// Creates a `NotFound` error with the given message and key.
165    pub fn not_found<T: Debug>(msg: &str, key: T) -> ViewError {
166        ViewError::NotFound(format!("{} {:?}", msg, key))
167    }
168}
169
170/// A view that supports hashing its values.
171#[async_trait]
172pub trait HashableView<C>: View<C> {
173    /// How to compute hashes.
174    type Hasher: Hasher;
175
176    /// Computes the hash of the values.
177    ///
178    /// Implementations do not need to include a type tag. However, the usual precautions
179    /// to enforce collision resistance must be applied (e.g. including the length of a
180    /// collection of values).
181    async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError>;
182
183    /// Computes the hash of the values.
184    ///
185    /// Implementations do not need to include a type tag. However, the usual precautions
186    /// to enforce collision resistance must be applied (e.g. including the length of a
187    /// collection of values).
188    async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError>;
189}
190
191/// The requirement for the hasher type in [`HashableView`].
192pub trait Hasher: Default + Write + Send + Sync + 'static {
193    /// The output type.
194    type Output: Debug + Clone + Eq + AsRef<[u8]> + 'static;
195
196    /// Finishes the hashing process and returns its output.
197    fn finalize(self) -> Self::Output;
198
199    /// Serializes a value with BCS and includes it in the hash.
200    fn update_with_bcs_bytes(&mut self, value: &impl Serialize) -> Result<(), ViewError> {
201        bcs::serialize_into(self, value)?;
202        Ok(())
203    }
204
205    /// Includes bytes in the hash.
206    fn update_with_bytes(&mut self, value: &[u8]) -> Result<(), ViewError> {
207        self.write_all(value)?;
208        Ok(())
209    }
210}
211
212impl Hasher for sha3::Sha3_256 {
213    type Output = HasherOutput;
214
215    fn finalize(self) -> Self::Output {
216        <sha3::Sha3_256 as sha3::Digest>::finalize(self)
217    }
218}
219
220/// A [`View`] whose staged modifications can be saved in storage.
221#[async_trait]
222pub trait RootView<C>: View<C> {
223    /// Saves the root view to the database context
224    async fn save(&mut self) -> Result<(), ViewError>;
225}
226
227/// A [`View`] that also supports crypto hash
228#[async_trait]
229pub trait CryptoHashView<C>: HashableView<C> {
230    /// Computing the hash and attributing the type to it.
231    async fn crypto_hash(&self) -> Result<CryptoHash, ViewError>;
232
233    /// Computing the hash and attributing the type to it.
234    async fn crypto_hash_mut(&mut self) -> Result<CryptoHash, ViewError>;
235}
236
237/// A [`RootView`] that also supports crypto hash
238#[async_trait]
239pub trait CryptoHashRootView<C>: RootView<C> + CryptoHashView<C> {}
240
241/// A [`ClonableView`] supports being shared (unsafely) by cloning it.
242///
243/// Sharing is unsafe because by having two view instances for the same data, they may have invalid
244/// state if both are used for writing.
245///
246/// Sharing the view is guaranteed to not cause data races if only one of the shared view instances
247/// is used for writing at any given point in time.
248pub trait ClonableView<C>: View<C> {
249    /// Creates a clone of this view, sharing the underlying storage context but prone to
250    /// data races which can corrupt the view state.
251    fn clone_unchecked(&mut self) -> Result<Self, ViewError>;
252}