linera_views/views/mod.rs
1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{fmt::Debug, future::Future, io::Write};
5
6use linera_base::crypto::CryptoHash;
7pub use linera_views_derive::{
8 ClonableView, CryptoHashRootView, CryptoHashView, HashableView, RootView, View,
9};
10use serde::Serialize;
11
12use crate::{batch::Batch, common::HasherOutput, ViewError};
13
14#[cfg(test)]
15#[path = "unit_tests/views.rs"]
16mod tests;
17
18/// The `RegisterView` implements a register for a single value.
19pub mod register_view;
20
21/// The `LazyRegisterView` implements a register for a single value with lazy loading.
22pub mod lazy_register_view;
23
24/// The `LogView` implements a log list that can be pushed.
25pub mod log_view;
26
27/// The `BucketQueueView` implements a queue that can push on the back and delete on the front and group data in buckets.
28pub mod bucket_queue_view;
29
30/// The `QueueView` implements a queue that can push on the back and delete on the front.
31pub mod queue_view;
32
33/// The `MapView` implements a map with ordered keys.
34pub mod map_view;
35
36/// The `SetView` implements a set with ordered entries.
37pub mod set_view;
38
39mod collection_entry;
40
41/// The `CollectionView` implements a map structure whose keys are ordered and the values are views.
42pub mod collection_view;
43
44/// The `ReentrantCollectionView` implements a map structure whose keys are ordered and the values are views with concurrent access.
45pub mod reentrant_collection_view;
46
47/// The implementation of a key-value store view.
48pub mod key_value_store_view;
49
50/// Wrapping a view to memoize hashing.
51pub mod hashable_wrapper;
52
53/// Wrapping a view to compute hash based on the history of modifications to the view.
54pub mod historical_hash_wrapper;
55
56/// The minimum value for the view tags. Values in `0..MIN_VIEW_TAG` are used for other purposes.
57pub const MIN_VIEW_TAG: u8 = 1;
58
59/// A view gives exclusive access to read and write the data stored at an underlying
60/// address in storage.
61#[cfg_attr(not(web), trait_variant::make(Send + Sync))]
62pub trait View: Sized {
63 /// The number of keys used for the initialization
64 const NUM_INIT_KEYS: usize;
65
66 /// The type of context stored in this view
67 type Context: crate::context::Context;
68
69 /// Obtains a mutable reference to the internal context.
70 fn context(&self) -> Self::Context;
71
72 /// Creates the keys needed for loading the view
73 fn pre_load(context: &Self::Context) -> Result<Vec<Vec<u8>>, ViewError>;
74
75 /// Loads a view from the values
76 fn post_load(context: Self::Context, values: &[Option<Vec<u8>>]) -> Result<Self, ViewError>;
77
78 /// Loads a view
79 fn load(context: Self::Context) -> impl Future<Output = Result<Self, ViewError>> {
80 async {
81 if Self::NUM_INIT_KEYS == 0 {
82 Self::post_load(context, &[])
83 } else {
84 use crate::{context::Context, store::ReadableKeyValueStore};
85 let keys = Self::pre_load(&context)?;
86 let values = context.store().read_multi_values_bytes(&keys).await?;
87 Self::post_load(context, &values)
88 }
89 }
90 }
91
92 /// Discards all pending changes. After that `flush` should have no effect to storage.
93 fn rollback(&mut self);
94
95 /// Returns [`true`] if flushing this view would result in changes to the persistent storage.
96 async fn has_pending_changes(&self) -> bool;
97
98 /// Clears the view. That can be seen as resetting to default. If the clear is followed
99 /// by a flush then all the relevant data is removed on the storage.
100 fn clear(&mut self);
101
102 /// Computes the batch of operations to persist changes to storage without modifying the view.
103 /// Crash-resistant storage implementations accumulate the desired changes in the `batch` variable.
104 /// The returned boolean indicates whether the operation removes the view or not.
105 fn pre_save(&self, batch: &mut Batch) -> Result<bool, ViewError>;
106
107 /// Updates the view state after the batch has been executed in the database.
108 /// This should be called after `pre_save` and after the batch has been successfully written to storage.
109 /// This leaves the view in a clean state with no pending changes.
110 ///
111 /// May panic if `pre_save` was not called right before on `self`.
112 fn post_save(&mut self);
113
114 /// Builds a trivial view that is already deleted
115 fn new(context: Self::Context) -> Result<Self, ViewError> {
116 let values = vec![None; Self::NUM_INIT_KEYS];
117 let mut view = Self::post_load(context, &values)?;
118 view.clear();
119 Ok(view)
120 }
121}
122
123/// A view which can have its context replaced.
124pub trait ReplaceContext<C: crate::context::Context>: View {
125 /// The type returned after replacing the context.
126 type Target: View<Context = C>;
127
128 /// Returns a view with a replaced context.
129 async fn with_context(&mut self, ctx: impl FnOnce(&Self::Context) -> C + Clone)
130 -> Self::Target;
131}
132
133/// A view that supports hashing its values.
134#[cfg_attr(not(web), trait_variant::make(Send))]
135pub trait HashableView: View {
136 /// How to compute hashes.
137 type Hasher: Hasher;
138
139 /// Computes the hash of the values.
140 ///
141 /// Implementations do not need to include a type tag. However, the usual precautions
142 /// to enforce collision resistance must be applied (e.g. including the length of a
143 /// collection of values).
144 async fn hash(&self) -> Result<<Self::Hasher as Hasher>::Output, ViewError>;
145
146 /// Same as `hash` but guaranteed to be wait-free.
147 async fn hash_mut(&mut self) -> Result<<Self::Hasher as Hasher>::Output, ViewError>;
148}
149
150/// The requirement for the hasher type in [`HashableView`].
151pub trait Hasher: Default + Write + Send + Sync + 'static {
152 /// The output type.
153 type Output: Debug + Clone + Eq + AsRef<[u8]> + 'static;
154
155 /// Finishes the hashing process and returns its output.
156 fn finalize(self) -> Self::Output;
157
158 /// Serializes a value with BCS and includes it in the hash.
159 fn update_with_bcs_bytes(&mut self, value: &impl Serialize) -> Result<(), ViewError> {
160 bcs::serialize_into(self, value)?;
161 Ok(())
162 }
163
164 /// Includes bytes in the hash.
165 fn update_with_bytes(&mut self, value: &[u8]) -> Result<(), ViewError> {
166 self.write_all(value)?;
167 Ok(())
168 }
169}
170
171impl Hasher for sha3::Sha3_256 {
172 type Output = HasherOutput;
173
174 fn finalize(self) -> Self::Output {
175 <sha3::Sha3_256 as sha3::Digest>::finalize(self)
176 }
177}
178
179/// A [`View`] whose staged modifications can be saved in storage.
180#[cfg_attr(not(web), trait_variant::make(Send))]
181pub trait RootView: View {
182 /// Saves the root view to the database context
183 async fn save(&mut self) -> Result<(), ViewError>;
184
185 /// Saves the root view to the database context and then drops it without calling `post_save`.
186 async fn save_and_drop(self) -> Result<(), ViewError>;
187}
188
189/// A [`View`] that also supports crypto hash
190#[cfg_attr(not(web), trait_variant::make(Send))]
191pub trait CryptoHashView: HashableView {
192 /// Computing the hash and attributing the type to it. May require locking.
193 async fn crypto_hash(&self) -> Result<CryptoHash, ViewError>;
194
195 /// Same as `crypto_hash` but guaranteed to be wait-free.
196 async fn crypto_hash_mut(&mut self) -> Result<CryptoHash, ViewError>;
197}
198
199/// A [`RootView`] that also supports crypto hash
200#[cfg_attr(not(web), trait_variant::make(Send))]
201pub trait CryptoHashRootView: RootView + CryptoHashView {}
202
203/// A view that can be shared (unsafely) by cloning it.
204///
205/// Note: Calling `flush` on any of the shared views will break the other views. Therefore,
206/// cloning views is only safe if `flush` only ever happens after all the copies but one
207/// have been dropped.
208pub trait ClonableView: View {
209 /// Creates a clone of this view, sharing the underlying storage context but prone to
210 /// data races which can corrupt the view state.
211 fn clone_unchecked(&mut self) -> Result<Self, ViewError>;
212}