ink_storage/lazy/
mod.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Low-level collections and data structures to manage storage entities in the
16//! persisted contract storage.
17//!
18//! These low-level collections are not aware of the elements they manage thus
19//! extra care has to be taken when operating directly on them.
20
21mod mapping;
22mod vec;
23
24#[doc(inline)]
25pub use self::mapping::Mapping;
26pub use self::vec::StorageVec;
27
28use crate::traits::{
29    AutoKey,
30    StorableHint,
31    StorageKey,
32};
33use core::marker::PhantomData;
34use ink_primitives::Key;
35use ink_storage_traits::Storable;
36use scale::{
37    Error,
38    Input,
39    Output,
40};
41
42/// A simple wrapper around a type to store it in a separate storage cell under its own
43/// storage key. If you want to update the value, first you need to
44/// [`get`](crate::Lazy::get) it, update the value, and then call
45/// [`set`](crate::Lazy::set) with the new value.
46///
47/// # Important
48///
49/// The wrapper requires its own pre-defined storage key in order to determine where it
50/// stores value. By default, the is automatically calculated using
51/// [`AutoKey`](crate::traits::AutoKey) during compilation. However, anyone can specify a
52/// storage key using [`ManualKey`](crate::traits::ManualKey). Specifying the storage key
53/// can be helpful for upgradeable contracts or you want to be resistant to future changes
54/// of storage key calculation strategy.
55///
56/// # Note
57///
58/// If the contract has two or more `Lazy` with the same storage key, modifying the value
59/// of one of them will modify others.
60///
61/// This is an example of how you can do this:
62/// ```rust
63/// # use ink::env::{
64/// #     Environment,
65/// #     DefaultEnvironment,
66/// # };
67///
68/// # #[ink::contract]
69/// # mod my_module {
70/// use ink::{
71///     storage::{
72///         traits::ManualKey,
73///         Lazy,
74///     },
75///     H160,
76/// };
77///
78/// #[ink(storage)]
79/// #[derive(Default)]
80/// pub struct MyContract {
81///     owner: Lazy<H160>,
82///     // todo maybe use something else than `Balance`?
83///     balance: Lazy<Balance, ManualKey<123>>,
84/// }
85///
86/// impl MyContract {
87///     #[ink(constructor)]
88///     pub fn new() -> Self {
89///         let mut instance = Self::default();
90///         let caller = Self::env().caller();
91///         instance.owner.set(&caller);
92///         instance.balance.set(&123456);
93///         instance
94///     }
95///
96/// #   #[ink(message)]
97/// #   pub fn my_message(&self) { }
98/// }
99/// # }
100/// ```
101#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
102pub struct Lazy<V, KeyType: StorageKey = AutoKey> {
103    _marker: PhantomData<fn() -> (V, KeyType)>,
104}
105
106/// We implement this manually because the derived implementation adds trait bounds.
107impl<V, KeyType> Default for Lazy<V, KeyType>
108where
109    KeyType: StorageKey,
110{
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116impl<V, KeyType> Lazy<V, KeyType>
117where
118    KeyType: StorageKey,
119{
120    /// Creates a new empty `Lazy`.
121    pub const fn new() -> Self {
122        Self {
123            _marker: PhantomData,
124        }
125    }
126}
127
128impl<V, KeyType> core::fmt::Debug for Lazy<V, KeyType>
129where
130    KeyType: StorageKey,
131{
132    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
133        f.debug_struct("Lazy").field("key", &KeyType::KEY).finish()
134    }
135}
136
137impl<V, KeyType> Lazy<V, KeyType>
138where
139    V: Storable,
140    KeyType: StorageKey,
141{
142    /// Reads the `value` from the contract storage, if it exists.
143    ///
144    /// # Panics
145    ///
146    /// Traps if the encoded `value` doesn't fit into the static buffer.
147    pub fn get(&self) -> Option<V> {
148        match ink_env::get_contract_storage::<Key, V>(&KeyType::KEY) {
149            Ok(Some(value)) => Some(value),
150            _ => None,
151        }
152    }
153
154    /// Try to read the `value` from the contract storage.
155    ///
156    /// To successfully retrieve the `value`, the encoded `key` and `value`
157    /// must both fit into the static buffer together.
158    ///
159    /// Returns:
160    /// - `Some(Ok(_))` if `value` was received from storage and could be decoded.
161    /// - `Some(Err(_))` if retrieving the `value` would exceed the static buffer size.
162    /// - `None` if there was no value under this storage key.
163    #[cfg(feature = "unstable-hostfn")]
164    pub fn try_get(&self) -> Option<ink_env::Result<V>> {
165        let key_size = <Key as Storable>::encoded_size(&KeyType::KEY);
166
167        if key_size >= ink_env::BUFFER_SIZE {
168            return Some(Err(ink_env::Error::BufferTooSmall));
169        }
170
171        let value_size: usize = ink_env::contains_contract_storage(&KeyType::KEY)?
172            .try_into()
173            .expect("targets of less than 32bit pointer size are not supported; qed");
174
175        if key_size.saturating_add(value_size) > ink_env::BUFFER_SIZE {
176            return Some(Err(ink_env::Error::BufferTooSmall));
177        }
178
179        self.get().map(Ok)
180    }
181
182    /// Writes the given `value` to the contract storage.
183    ///
184    /// # Panics
185    ///
186    /// Traps if the encoded `value` doesn't fit into the static buffer.
187    pub fn set(&mut self, value: &V) {
188        ink_env::set_contract_storage::<Key, V>(&KeyType::KEY, value);
189    }
190
191    /// Try to set the given `value` to the contract storage.
192    ///
193    /// To successfully store the `value`, the encoded `key` and `value`
194    /// must fit into the static buffer together.
195    pub fn try_set(&mut self, value: &V) -> ink_env::Result<()> {
196        let key_size = <Key as Storable>::encoded_size(&KeyType::KEY);
197        let value_size = <V as Storable>::encoded_size(value);
198
199        if key_size.saturating_add(value_size) > ink_env::BUFFER_SIZE {
200            return Err(ink_env::Error::BufferTooSmall);
201        };
202
203        self.set(value);
204
205        Ok(())
206    }
207}
208
209impl<V, KeyType> Lazy<V, KeyType>
210where
211    V: Storable + Default,
212    KeyType: StorageKey,
213{
214    /// Reads the `value` from the contract storage.
215    ///
216    /// Returns the default value for the storage type if no `value` exists.
217    pub fn get_or_default(&self) -> V {
218        match ink_env::get_contract_storage::<Key, V>(&KeyType::KEY) {
219            Ok(Some(value)) => value,
220            _ => Default::default(),
221        }
222    }
223}
224
225impl<V, KeyType> Storable for Lazy<V, KeyType>
226where
227    KeyType: StorageKey,
228{
229    #[inline(always)]
230    fn encode<T: Output + ?Sized>(&self, _dest: &mut T) {}
231
232    #[inline(always)]
233    fn decode<I: Input>(_input: &mut I) -> Result<Self, Error> {
234        Ok(Default::default())
235    }
236
237    #[inline(always)]
238    fn encoded_size(&self) -> usize {
239        0
240    }
241}
242
243impl<V, Key, InnerKey> StorableHint<Key> for Lazy<V, InnerKey>
244where
245    Key: StorageKey,
246    InnerKey: StorageKey,
247    V: StorableHint<Key>,
248{
249    type Type = Lazy<V::Type, Key>;
250    type PreferredKey = InnerKey;
251}
252
253impl<V, KeyType> StorageKey for Lazy<V, KeyType>
254where
255    KeyType: StorageKey,
256{
257    const KEY: Key = KeyType::KEY;
258}
259
260#[cfg(feature = "std")]
261const _: () = {
262    use crate::traits::StorageLayout;
263    use ink_metadata::layout::{
264        Layout,
265        LayoutKey,
266        RootLayout,
267    };
268
269    impl<V, KeyType> StorageLayout for Lazy<V, KeyType>
270    where
271        V: StorageLayout + scale_info::TypeInfo + 'static,
272        KeyType: StorageKey + scale_info::TypeInfo + 'static,
273    {
274        fn layout(_: &Key) -> Layout {
275            Layout::Root(RootLayout::new(
276                LayoutKey::from(&KeyType::KEY),
277                <V as StorageLayout>::layout(&KeyType::KEY),
278                scale_info::meta_type::<Self>(),
279            ))
280        }
281    }
282};
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287    use crate::traits::ManualKey;
288
289    #[test]
290    fn set_and_get_work() {
291        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
292            let mut storage: Lazy<u8> = Lazy::new();
293            storage.set(&2);
294            assert_eq!(storage.get(), Some(2));
295
296            Ok(())
297        })
298        .unwrap()
299    }
300
301    #[test]
302    fn set_and_get_work_for_two_lazy_with_same_manual_key() {
303        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
304            let mut storage: Lazy<u8, ManualKey<123>> = Lazy::new();
305            storage.set(&2);
306
307            let storage2: Lazy<u8, ManualKey<123>> = Lazy::new();
308            assert_eq!(storage2.get(), Some(2));
309
310            Ok(())
311        })
312        .unwrap()
313    }
314
315    #[test]
316    fn gets_or_default_if_no_key_set() {
317        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
318            let storage: Lazy<u8> = Lazy::new();
319            assert_eq!(storage.get_or_default(), 0);
320
321            Ok(())
322        })
323        .unwrap()
324    }
325
326    #[test]
327    fn gets_returns_none_if_no_value_was_set() {
328        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
329            let storage: Lazy<u8> = Lazy::new();
330            assert_eq!(storage.get(), None);
331
332            Ok(())
333        })
334        .unwrap()
335    }
336
337    #[test]
338    fn fallible_storage_works_for_fitting_data() {
339        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
340            // The default `Key` is an 4 byte int
341            const KEY_SIZE: usize = 4;
342            const VALUE_SIZE: usize = ink_env::BUFFER_SIZE - KEY_SIZE;
343
344            let mut storage: Lazy<[u8; VALUE_SIZE]> = Lazy::new();
345
346            let value = [0u8; VALUE_SIZE];
347            assert_eq!(storage.try_set(&value), Ok(()));
348            assert_eq!(storage.try_get(), Some(Ok(value)));
349
350            Ok(())
351        })
352        .unwrap()
353    }
354
355    #[test]
356    fn fallible_storage_fails_gracefully_for_overgrown_data() {
357        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
358            // The default `Key` is an 4 byte int
359            const KEY_SIZE: usize = 4;
360            const VALUE_SIZE: usize = ink_env::BUFFER_SIZE - KEY_SIZE + 1;
361
362            let mut storage: Lazy<[u8; VALUE_SIZE]> = Lazy::new();
363
364            let value = [0u8; VALUE_SIZE];
365            assert_eq!(storage.try_get(), None);
366            assert_eq!(storage.try_set(&value), Err(ink_env::Error::BufferTooSmall));
367
368            // The off-chain impl conveniently uses a Vec for encoding,
369            // allowing writing values exceeding the static buffer size.
370            ink_env::set_contract_storage(&storage.key(), &value);
371            assert_eq!(storage.try_get(), Some(Err(ink_env::Error::BufferTooSmall)));
372
373            Ok(())
374        })
375        .unwrap()
376    }
377}