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///     Lazy,
72///     traits::ManualKey,
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    pub fn try_get(&self) -> Option<ink_env::Result<V>> {
161        let key_size = <Key as Storable>::encoded_size(&KeyType::KEY);
162
163        if key_size >= ink_env::BUFFER_SIZE {
164            return Some(Err(ink_env::Error::BufferTooSmall));
165        }
166
167        let value_size: usize = ink_env::contains_contract_storage(&KeyType::KEY)?
168            .try_into()
169            .expect("targets of less than 32bit pointer size are not supported; qed");
170
171        if key_size.saturating_add(value_size) > ink_env::BUFFER_SIZE {
172            return Some(Err(ink_env::Error::BufferTooSmall));
173        }
174
175        self.get().map(Ok)
176    }
177
178    /// Writes the given `value` to the contract storage.
179    ///
180    /// # Panics
181    ///
182    /// Traps if the encoded `value` doesn't fit into the static buffer.
183    pub fn set(&mut self, value: &V) {
184        ink_env::set_contract_storage::<Key, V>(&KeyType::KEY, value);
185    }
186
187    /// Try to set the given `value` to the contract storage.
188    ///
189    /// To successfully store the `value`, the encoded `key` and `value`
190    /// must fit into the static buffer together.
191    pub fn try_set(&mut self, value: &V) -> ink_env::Result<()> {
192        let key_size = <Key as Storable>::encoded_size(&KeyType::KEY);
193        let value_size = <V as Storable>::encoded_size(value);
194
195        if key_size.saturating_add(value_size) > ink_env::BUFFER_SIZE {
196            return Err(ink_env::Error::BufferTooSmall);
197        };
198
199        self.set(value);
200
201        Ok(())
202    }
203}
204
205impl<V, KeyType> Lazy<V, KeyType>
206where
207    V: Storable + Default,
208    KeyType: StorageKey,
209{
210    /// Reads the `value` from the contract storage.
211    ///
212    /// Returns the default value for the storage type if no `value` exists.
213    pub fn get_or_default(&self) -> V {
214        match ink_env::get_contract_storage::<Key, V>(&KeyType::KEY) {
215            Ok(Some(value)) => value,
216            _ => Default::default(),
217        }
218    }
219}
220
221impl<V, KeyType> Storable for Lazy<V, KeyType>
222where
223    KeyType: StorageKey,
224{
225    #[inline(always)]
226    fn encode<T: Output + ?Sized>(&self, _dest: &mut T) {}
227
228    #[inline(always)]
229    fn decode<I: Input>(_input: &mut I) -> Result<Self, Error> {
230        Ok(Default::default())
231    }
232
233    #[inline(always)]
234    fn encoded_size(&self) -> usize {
235        0
236    }
237}
238
239impl<V, Key, InnerKey> StorableHint<Key> for Lazy<V, InnerKey>
240where
241    Key: StorageKey,
242    InnerKey: StorageKey,
243    V: StorableHint<Key>,
244{
245    type Type = Lazy<V::Type, Key>;
246    type PreferredKey = InnerKey;
247}
248
249impl<V, KeyType> StorageKey for Lazy<V, KeyType>
250where
251    KeyType: StorageKey,
252{
253    const KEY: Key = KeyType::KEY;
254}
255
256#[cfg(feature = "std")]
257const _: () = {
258    use crate::traits::StorageLayout;
259    use ink_metadata::layout::{
260        Layout,
261        LayoutKey,
262        RootLayout,
263    };
264
265    impl<V, KeyType> StorageLayout for Lazy<V, KeyType>
266    where
267        V: StorageLayout + scale_info::TypeInfo + 'static,
268        KeyType: StorageKey + scale_info::TypeInfo + 'static,
269    {
270        fn layout(_: &Key) -> Layout {
271            Layout::Root(RootLayout::new(
272                LayoutKey::from(&KeyType::KEY),
273                <V as StorageLayout>::layout(&KeyType::KEY),
274                scale_info::meta_type::<Self>(),
275            ))
276        }
277    }
278};
279
280#[cfg(test)]
281mod tests {
282    use super::*;
283    use crate::traits::ManualKey;
284
285    #[test]
286    fn set_and_get_work() {
287        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
288            let mut storage: Lazy<u8> = Lazy::new();
289            storage.set(&2);
290            assert_eq!(storage.get(), Some(2));
291
292            Ok(())
293        })
294        .unwrap()
295    }
296
297    #[test]
298    fn set_and_get_work_for_two_lazy_with_same_manual_key() {
299        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
300            let mut storage: Lazy<u8, ManualKey<123>> = Lazy::new();
301            storage.set(&2);
302
303            let storage2: Lazy<u8, ManualKey<123>> = Lazy::new();
304            assert_eq!(storage2.get(), Some(2));
305
306            Ok(())
307        })
308        .unwrap()
309    }
310
311    #[test]
312    fn gets_or_default_if_no_key_set() {
313        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
314            let storage: Lazy<u8> = Lazy::new();
315            assert_eq!(storage.get_or_default(), 0);
316
317            Ok(())
318        })
319        .unwrap()
320    }
321
322    #[test]
323    fn gets_returns_none_if_no_value_was_set() {
324        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
325            let storage: Lazy<u8> = Lazy::new();
326            assert_eq!(storage.get(), None);
327
328            Ok(())
329        })
330        .unwrap()
331    }
332
333    #[test]
334    fn fallible_storage_works_for_fitting_data() {
335        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
336            // The default `Key` is an 4 byte int
337            const KEY_SIZE: usize = 4;
338            const VALUE_SIZE: usize = ink_env::BUFFER_SIZE - KEY_SIZE;
339
340            let mut storage: Lazy<[u8; VALUE_SIZE]> = Lazy::new();
341
342            let value = [0u8; VALUE_SIZE];
343            assert_eq!(storage.try_set(&value), Ok(()));
344            assert_eq!(storage.try_get(), Some(Ok(value)));
345
346            Ok(())
347        })
348        .unwrap()
349    }
350
351    #[test]
352    fn fallible_storage_fails_gracefully_for_overgrown_data() {
353        ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
354            // The default `Key` is an 4 byte int
355            const KEY_SIZE: usize = 4;
356            const VALUE_SIZE: usize = ink_env::BUFFER_SIZE - KEY_SIZE + 1;
357
358            let mut storage: Lazy<[u8; VALUE_SIZE]> = Lazy::new();
359
360            let value = [0u8; VALUE_SIZE];
361            assert_eq!(storage.try_get(), None);
362            assert_eq!(storage.try_set(&value), Err(ink_env::Error::BufferTooSmall));
363
364            // The off-chain impl conveniently uses a Vec for encoding,
365            // allowing writing values exceeding the static buffer size.
366            ink_env::set_contract_storage(&storage.key(), &value);
367            assert_eq!(storage.try_get(), Some(Err(ink_env::Error::BufferTooSmall)));
368
369            Ok(())
370        })
371        .unwrap()
372    }
373}