Skip to main content

linera_views/
context.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use custom_debug_derive::Debug;
5use linera_base::hex_debug;
6use serde::{de::DeserializeOwned, Serialize};
7
8use crate::{
9    batch::DeletePrefixExpander,
10    memory::MemoryStore,
11    store::{
12        KeyValueDatabase, KeyValueStoreError, ReadableKeyValueStore, WithError,
13        WritableKeyValueStore,
14    },
15    views::MIN_VIEW_TAG,
16};
17
18/// A wrapper over `Vec<u8>` with functions for using it as a key prefix.
19#[derive(Default, Debug, Clone, derive_more::From)]
20pub struct BaseKey {
21    /// The byte value of the key prefix.
22    #[from]
23    #[debug(with = "hex_debug")]
24    pub bytes: Vec<u8>,
25}
26
27impl BaseKey {
28    /// Concatenates the base key and tag.
29    pub fn base_tag(&self, tag: u8) -> Vec<u8> {
30        assert!(tag >= MIN_VIEW_TAG, "tag should be at least MIN_VIEW_TAG");
31        let mut key = Vec::with_capacity(self.bytes.len() + 1);
32        key.extend_from_slice(&self.bytes);
33        key.push(tag);
34        key
35    }
36
37    /// Concatenates the base key, tag and index.
38    pub fn base_tag_index(&self, tag: u8, index: &[u8]) -> Vec<u8> {
39        assert!(tag >= MIN_VIEW_TAG, "tag should be at least MIN_VIEW_TAG");
40        let mut key = Vec::with_capacity(self.bytes.len() + 1 + index.len());
41        key.extend_from_slice(&self.bytes);
42        key.push(tag);
43        key.extend_from_slice(index);
44        key
45    }
46
47    /// Concatenates the base key and index.
48    pub fn base_index(&self, index: &[u8]) -> Vec<u8> {
49        let mut key = Vec::with_capacity(self.bytes.len() + index.len());
50        key.extend_from_slice(&self.bytes);
51        key.extend_from_slice(index);
52        key
53    }
54
55    /// Obtains the `Vec<u8>` key from the key by serialization and using the `base_key`.
56    pub fn derive_tag_key<I: Serialize>(&self, tag: u8, index: &I) -> Result<Vec<u8>, bcs::Error> {
57        assert!(tag >= MIN_VIEW_TAG, "tag should be at least MIN_VIEW_TAG");
58        let mut key = self.base_tag(tag);
59        bcs::serialize_into(&mut key, index)?;
60        Ok(key)
61    }
62
63    /// Returns this key with a number of final bytes trimmed.
64    fn trimmed_key(&self, n: usize) -> Result<Vec<u8>, bcs::Error> {
65        if self.bytes.len() < n {
66            return Err(bcs::Error::Custom(format!(
67                "attempted to trim {} bytes from key of length {}",
68                n,
69                self.bytes.len()
70            )));
71        }
72        Ok(self.bytes[0..self.bytes.len() - n].to_vec())
73    }
74
75    /// Obtains the short `Vec<u8>` key from the key by serialization.
76    pub fn derive_short_key<I: Serialize + ?Sized>(index: &I) -> Result<Vec<u8>, bcs::Error> {
77        bcs::to_bytes(index)
78    }
79
80    /// Deserialize `bytes` into type `Item`.
81    pub fn deserialize_value<Item: DeserializeOwned>(bytes: &[u8]) -> Result<Item, bcs::Error> {
82        bcs::from_bytes(bytes)
83    }
84}
85
86/// The context in which a view is operated. Typically, this includes the client to
87/// connect to the database and the address of the current entry.
88#[cfg_attr(not(web), trait_variant::make(Send + Sync))]
89pub trait Context: Clone
90where
91    crate::ViewError: From<Self::Error>,
92{
93    /// The type of the key-value store used by this context.
94    type Store: ReadableKeyValueStore + WritableKeyValueStore + WithError<Error = Self::Error>;
95
96    /// User-provided data to be carried along.
97    type Extra: Clone + linera_base::util::traits::AutoTraits;
98
99    /// The type of errors that may be returned by operations on the `Store`, a
100    /// convenience alias for `<Self::Store as WithError>::Error`.
101    type Error: KeyValueStoreError;
102
103    /// Getter for the store.
104    fn store(&self) -> &Self::Store;
105
106    /// Getter for the user-provided data.
107    fn extra(&self) -> &Self::Extra;
108
109    /// Getter for the address of the base key.
110    fn base_key(&self) -> &BaseKey;
111
112    /// Mutable getter for the address of the base key.
113    fn base_key_mut(&mut self) -> &mut BaseKey;
114
115    /// Obtains a similar [`Context`] implementation with a different base key.
116    fn clone_with_base_key(&self, base_key: Vec<u8>) -> Self {
117        let mut context = self.clone();
118        context.base_key_mut().bytes = base_key;
119        context
120    }
121
122    /// Obtains a similar [`Context`] implementation with the last `n` bytes of the base
123    /// key trimmed.
124    fn clone_with_trimmed_key(&self, n: usize) -> Self {
125        let mut context = self.clone();
126        let key = context.base_key().trimmed_key(n).unwrap();
127        context.base_key_mut().bytes = key;
128        context
129    }
130}
131
132/// A context which can't be used to read or write data, only used for caching views.
133#[derive(Debug, Default, Clone)]
134pub struct InactiveContext(pub BaseKey);
135
136impl Context for InactiveContext {
137    type Store = crate::store::inactive_store::InactiveStore;
138    type Extra = ();
139
140    type Error = crate::store::inactive_store::InactiveStoreError;
141
142    fn store(&self) -> &Self::Store {
143        &crate::store::inactive_store::InactiveStore
144    }
145
146    fn extra(&self) -> &Self::Extra {
147        &()
148    }
149
150    fn base_key(&self) -> &BaseKey {
151        &self.0
152    }
153
154    fn base_key_mut(&mut self) -> &mut BaseKey {
155        &mut self.0
156    }
157}
158
159/// Implementation of the [`Context`] trait on top of a DB client implementing
160/// [`crate::store::KeyValueStore`].
161#[derive(Debug, Default, Clone)]
162pub struct ViewContext<E, S> {
163    /// The DB client that is shared between views.
164    store: S,
165    /// The base key for the context.
166    base_key: BaseKey,
167    /// User-defined data attached to the view.
168    extra: E,
169}
170
171impl<E, S> ViewContext<E, S>
172where
173    S: ReadableKeyValueStore + WritableKeyValueStore,
174{
175    /// Creates a context suitable for a root view over the partition at `root_key`,
176    /// which is opened in exclusive mode. If the journal is non-empty, it is cleared
177    /// first, before the context is returned.
178    ///
179    /// Taking the database rather than an already-opened store is what keeps a view
180    /// from being backed by a shared partition: the caller never chooses the mode.
181    pub async fn create_root_context<D>(
182        database: &D,
183        root_key: &[u8],
184        extra: E,
185    ) -> Result<Self, S::Error>
186    where
187        D: KeyValueDatabase<Store = S> + WithError<Error = S::Error>,
188    {
189        let store = database.open_exclusive(root_key)?;
190        store.clear_journal().await?;
191        Ok(Self::new_unchecked(store, Vec::new(), extra))
192    }
193}
194
195impl<E, S> ViewContext<E, S> {
196    /// Creates a context for the given base key, store, and an extra argument. NOTE: this
197    /// constructor doesn't check the journal of the store. In doubt, use
198    /// [`ViewContext::create_root_context`] instead.
199    pub fn new_unchecked(store: S, base_key: Vec<u8>, extra: E) -> Self {
200        Self {
201            store,
202            base_key: BaseKey { bytes: base_key },
203            extra,
204        }
205    }
206}
207
208impl<E, S> Context for ViewContext<E, S>
209where
210    E: Clone + linera_base::util::traits::AutoTraits,
211    S: ReadableKeyValueStore + WritableKeyValueStore + Clone,
212    S::Error: From<bcs::Error> + Send + Sync + std::error::Error + 'static,
213{
214    type Extra = E;
215    type Store = S;
216
217    type Error = S::Error;
218
219    fn store(&self) -> &Self::Store {
220        &self.store
221    }
222
223    fn extra(&self) -> &E {
224        &self.extra
225    }
226
227    fn base_key(&self) -> &BaseKey {
228        &self.base_key
229    }
230
231    fn base_key_mut(&mut self) -> &mut BaseKey {
232        &mut self.base_key
233    }
234}
235
236/// An implementation of [`crate::context::Context`] that stores all values in memory.
237pub type MemoryContext<E> = ViewContext<E, MemoryStore>;
238
239impl<E> MemoryContext<E> {
240    /// Creates a [`Context`] instance in memory for testing.
241    #[cfg(with_testing)]
242    pub fn new_for_testing(extra: E) -> Self {
243        Self {
244            store: MemoryStore::new_for_testing(),
245            base_key: BaseKey::default(),
246            extra,
247        }
248    }
249}
250
251impl DeletePrefixExpander for MemoryContext<()> {
252    type Error = crate::memory::MemoryStoreError;
253
254    async fn expand_delete_prefix(&self, key_prefix: &[u8]) -> Result<Vec<Vec<u8>>, Self::Error> {
255        self.store().find_keys_by_prefix(key_prefix).await
256    }
257}