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::storage::{
71///     traits::ManualKey,
72///     Lazy,
73/// };
74///
75/// #[ink(storage)]
76/// #[derive(Default)]
77/// pub struct MyContract {
78///     owner: Lazy<Address>,
79///     // todo maybe use something else than `Balance`?
80///     balance: Lazy<Balance, ManualKey<123>>,
81/// }
82///
83/// impl MyContract {
84///     #[ink(constructor)]
85///     pub fn new() -> Self {
86///         let mut instance = Self::default();
87///         let caller = Self::env().caller();
88///         instance.owner.set(&caller);
89///         instance.balance.set(&123456);
90///         instance
91///     }
92///
93/// #   #[ink(message)]
94/// #   pub fn my_message(&self) { }
95/// }
96/// # }
97/// ```
98#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
99pub struct Lazy<V, KeyType: StorageKey = AutoKey> {
100    _marker: PhantomData<fn() -> (V, KeyType)>,
101}
102
103/// We implement this manually because the derived implementation adds trait bounds.
104impl<V, KeyType> Default for Lazy<V, KeyType>
105where
106    KeyType: StorageKey,
107{
108    fn default() -> Self {
109        Self::new()
110    }
111}
112
113impl<V, KeyType> Lazy<V, KeyType>
114where
115    KeyType: StorageKey,
116{
117    /// Creates a new empty `Lazy`.
118    pub const fn new() -> Self {
119        Self {
120            _marker: PhantomData,
121        }
122    }
123}
124
125impl<V, KeyType> core::fmt::Debug for Lazy<V, KeyType>
126where
127    KeyType: StorageKey,
128{
129    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
130        f.debug_struct("Lazy").field("key", &KeyType::KEY).finish()
131    }
132}
133
134impl<V, KeyType> Lazy<V, KeyType>
135where
136    V: Storable,
137    KeyType: StorageKey,
138{
139    /// Reads the `value` from the contract storage, if it exists.
140    ///
141    /// # Panics
142    ///
143    /// Traps if the encoded `value` doesn't fit into the static buffer.
144    pub fn get(&self) -> Option<V> {
145        match ink_env::get_contract_storage::<Key, V>(&KeyType::KEY) {
146            Ok(Some(value)) => Some(value),
147            _ => None,
148        }
149    }
150
151    /// Try to read the `value` from the contract storage.
152    ///
153    /// To successfully retrieve the `value`, the encoded `key` and `value`
154    /// must both fit into the static buffer together.
155    ///
156    /// Returns:
157    /// - `Some(Ok(_))` if `value` was received from storage and could be decoded.
158    /// - `Some(Err(_))` if retrieving the `value` would exceed the static buffer size.
159    /// - `None` if there was no value under this storage key.
160    #[cfg(feature = "unstable-hostfn")]
161    pub fn try_get(&self) -> Option<ink_env::Result<V>> {
162        let key_size = <Key as Storable>::encoded_size(&KeyType::KEY);
163
164        if key_size >= ink_env::BUFFER_SIZE {
165            return Some(Err(ink_env::Error::BufferTooSmall));
166        }
167
168        let value_size: usize = ink_env::contains_contract_storage(&KeyType::KEY)?
169            .try_into()
170            .expect("targets of less than 32bit pointer size are not supported; qed");
171
172        if key_size.saturating_add(value_size) > ink_env::BUFFER_SIZE {
173            return Some(Err(ink_env::Error::BufferTooSmall));
174        }
175
176        self.get().map(Ok)
177    }
178
179    /// Writes the given `value` to the contract storage.
180    ///
181    /// # Panics
182    ///
183    /// Traps if the encoded `value` doesn't fit into the static buffer.
184    pub fn set(&mut self, value: &V) {
185        ink_env::set_contract_storage::<Key, V>(&KeyType::KEY, value);
186    }
187
188    /// Try to set the given `value` to the contract storage.
189    ///
190    /// To successfully store the `value`, the encoded `key` and `value`
191    /// must fit into the static buffer together.
192    pub fn try_set(&mut self, value: &V) -> ink_env::Result<()> {
193        let key_size = <Key as Storable>::encoded_size(&KeyType::KEY);
194        let value_size = <V as Storable>::encoded_size(value);
195
196        if key_size.saturating_add(value_size) > ink_env::BUFFER_SIZE {
197            return Err(ink_env::Error::BufferTooSmall);
198        };
199
200        self.set(value);
201
202        Ok(())
203    }
204}
205
206impl<V, KeyType> Lazy<V, KeyType>
207where
208    V: Storable + Default,
209    KeyType: StorageKey,
210{
211    /// Reads the `value` from the contract storage.
212    ///
213    /// Returns the default value for the storage type if no `value` exists.
214    pub fn get_or_default(&self) -> V {
215        match ink_env::get_contract_storage::<Key, V>(&KeyType::KEY) {
216            Ok(Some(value)) => value,
217            _ => Default::default(),
218        }
219    }
220}
221
222impl<V, KeyType> Storable for Lazy<V, KeyType>
223where
224    KeyType: StorageKey,
225{
226    #[inline(always)]
227    fn encode<T: Output + ?Sized>(&self, _dest: &mut T) {}
228
229    #[inline(always)]
230    fn decode<I: Input>(_input: &mut I) -> Result<Self, Error> {
231        Ok(Default::default())
232    }
233
234    #[inline(always)]
235    fn encoded_size(&self) -> usize {
236        0
237    }
238}
239
240impl<V, Key, InnerKey> StorableHint<Key> for Lazy<V, InnerKey>
241where
242    Key: StorageKey,
243    InnerKey: StorageKey,
244    V: StorableHint<Key>,
245{
246    type Type = Lazy<V::Type, Key>;
247    type PreferredKey = InnerKey;
248}
249
250impl<V, KeyType> StorageKey for Lazy<V, KeyType>
251where
252    KeyType: StorageKey,
253{
254    const KEY: Key = KeyType::KEY;
255}
256
257#[cfg(feature = "std")]
258const _: () = {
259    use crate::traits::StorageLayout;
260    use ink_metadata::layout::{
261        Layout,
262        LayoutKey,
263        RootLayout,
264    };
265
266    impl<V, KeyType> StorageLayout for Lazy<V, KeyType>
267    where
268        V: StorageLayout + scale_info::TypeInfo + 'static,
269        KeyType: StorageKey + scale_info::TypeInfo + 'static,
270    {
271        fn layout(_: &Key) -> Layout {
272            Layout::Root(RootLayout::new(
273                LayoutKey::from(&KeyType::KEY),
274                <V as StorageLayout>::layout(&KeyType::KEY),
275                scale_info::meta_type::<Self>(),
276            ))
277        }
278    }
279};
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284    use crate::traits::ManualKey;
285
286    #[test]
287    fn set_and_get_work() {
288        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
289            let mut storage: Lazy<u8> = Lazy::new();
290            storage.set(&2);
291            assert_eq!(storage.get(), Some(2));
292
293            Ok(())
294        })
295        .unwrap()
296    }
297
298    #[test]
299    fn set_and_get_work_for_two_lazy_with_same_manual_key() {
300        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
301            let mut storage: Lazy<u8, ManualKey<123>> = Lazy::new();
302            storage.set(&2);
303
304            let storage2: Lazy<u8, ManualKey<123>> = Lazy::new();
305            assert_eq!(storage2.get(), Some(2));
306
307            Ok(())
308        })
309        .unwrap()
310    }
311
312    #[test]
313    fn gets_or_default_if_no_key_set() {
314        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
315            let storage: Lazy<u8> = Lazy::new();
316            assert_eq!(storage.get_or_default(), 0);
317
318            Ok(())
319        })
320        .unwrap()
321    }
322
323    #[test]
324    fn gets_returns_none_if_no_value_was_set() {
325        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
326            let storage: Lazy<u8> = Lazy::new();
327            assert_eq!(storage.get(), None);
328
329            Ok(())
330        })
331        .unwrap()
332    }
333
334    #[test]
335    fn fallible_storage_works_for_fitting_data() {
336        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
337            // The default `Key` is an 4 byte int
338            const KEY_SIZE: usize = 4;
339            const VALUE_SIZE: usize = ink_env::BUFFER_SIZE - KEY_SIZE;
340
341            let mut storage: Lazy<[u8; VALUE_SIZE]> = Lazy::new();
342
343            let value = [0u8; VALUE_SIZE];
344            assert_eq!(storage.try_set(&value), Ok(()));
345            assert_eq!(storage.try_get(), Some(Ok(value)));
346
347            Ok(())
348        })
349        .unwrap()
350    }
351
352    #[test]
353    fn fallible_storage_fails_gracefully_for_overgrown_data() {
354        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
355            // The default `Key` is an 4 byte int
356            const KEY_SIZE: usize = 4;
357            const VALUE_SIZE: usize = ink_env::BUFFER_SIZE - KEY_SIZE + 1;
358
359            let mut storage: Lazy<[u8; VALUE_SIZE]> = Lazy::new();
360
361            let value = [0u8; VALUE_SIZE];
362            assert_eq!(storage.try_get(), None);
363            assert_eq!(storage.try_set(&value), Err(ink_env::Error::BufferTooSmall));
364
365            // The off-chain impl conveniently uses a Vec for encoding,
366            // allowing writing values exceeding the static buffer size.
367            ink_env::set_contract_storage(&storage.key(), &value);
368            assert_eq!(storage.try_get(), Some(Err(ink_env::Error::BufferTooSmall)));
369
370            Ok(())
371        })
372        .unwrap()
373    }
374}