referrerpolicy=no-referrer-when-downgrade

frame_support/storage/generator/
mod.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Generators are a set of trait on which storage traits are implemented.
19//!
20//! (i.e. implementing the generator for StorageValue on a type will automatically derive the
21//! implementation of StorageValue for this type).
22//!
23//! They are used by `decl_storage`.
24//!
25//! This is internal api and is subject to change.
26
27pub(crate) mod double_map;
28pub(crate) mod map;
29pub(crate) mod nmap;
30pub(crate) mod value;
31
32pub use double_map::StorageDoubleMap;
33pub use map::StorageMap;
34pub use nmap::StorageNMap;
35pub use value::StorageValue;
36
37#[cfg(test)]
38mod tests {
39	use alloc::vec::Vec;
40	use codec::Encode;
41	use sp_io::TestExternalities;
42	use sp_runtime::{generic, traits::BlakeTwo256, BuildStorage};
43
44	use crate::{
45		assert_noop, assert_ok,
46		storage::{generator::StorageValue, unhashed},
47	};
48
49	#[crate::pallet]
50	pub mod frame_system {
51		#[allow(unused)]
52		use super::{frame_system, frame_system::pallet_prelude::*};
53		pub use crate::dispatch::RawOrigin;
54		use crate::pallet_prelude::*;
55
56		#[pallet::pallet]
57		pub struct Pallet<T>(_);
58
59		#[pallet::config]
60		#[pallet::disable_frame_system_supertrait_check]
61		pub trait Config: 'static {
62			type Block: sp_runtime::traits::Block;
63			type AccountId;
64			type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
65			type RuntimeOrigin;
66			type RuntimeCall;
67			type RuntimeTask;
68			type PalletInfo: crate::traits::PalletInfo;
69			type DbWeight: Get<crate::weights::RuntimeDbWeight>;
70		}
71
72		#[pallet::origin]
73		pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
74
75		#[pallet::error]
76		pub enum Error<T> {
77			/// Required by construct_runtime
78			CallFiltered,
79		}
80
81		#[pallet::call]
82		impl<T: Config> Pallet<T> {}
83
84		#[pallet::storage]
85		pub type Value<T> = StorageValue<_, (u64, u64), ValueQuery>;
86
87		#[pallet::storage]
88		pub type Map<T> = StorageMap<_, Blake2_128Concat, u16, u64, ValueQuery>;
89
90		#[pallet::storage]
91		pub type NumberMap<T> = StorageMap<_, Identity, u32, u64, ValueQuery>;
92
93		#[pallet::storage]
94		pub type DoubleMap<T> =
95			StorageDoubleMap<_, Blake2_128Concat, u16, Twox64Concat, u32, u64, ValueQuery>;
96
97		#[pallet::storage]
98		pub type NMap<T> = StorageNMap<
99			_,
100			(storage::Key<Blake2_128Concat, u16>, storage::Key<Twox64Concat, u32>),
101			u64,
102			ValueQuery,
103		>;
104
105		pub mod pallet_prelude {
106			pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
107
108			pub type HeaderFor<T> =
109				<<T as super::Config>::Block as sp_runtime::traits::HeaderProvider>::HeaderT;
110
111			pub type BlockNumberFor<T> = <HeaderFor<T> as sp_runtime::traits::Header>::Number;
112		}
113	}
114
115	type BlockNumber = u32;
116	type AccountId = u32;
117	type Header = generic::Header<BlockNumber, BlakeTwo256>;
118	type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;
119	type Block = generic::Block<Header, UncheckedExtrinsic>;
120
121	crate::construct_runtime!(
122		pub enum Runtime
123		{
124			System: self::frame_system,
125		}
126	);
127
128	impl self::frame_system::Config for Runtime {
129		type AccountId = AccountId;
130		type Block = Block;
131		type BaseCallFilter = crate::traits::Everything;
132		type RuntimeOrigin = RuntimeOrigin;
133		type RuntimeCall = RuntimeCall;
134		type RuntimeTask = RuntimeTask;
135		type PalletInfo = PalletInfo;
136		type DbWeight = ();
137	}
138
139	pub fn key_before_prefix(mut prefix: Vec<u8>) -> Vec<u8> {
140		let last = prefix.iter_mut().last().unwrap();
141		assert_ne!(*last, 0, "mock function not implemented for this prefix");
142		*last -= 1;
143		prefix
144	}
145
146	pub fn key_after_prefix(mut prefix: Vec<u8>) -> Vec<u8> {
147		let last = prefix.iter_mut().last().unwrap();
148		assert_ne!(*last, 255, "mock function not implemented for this prefix");
149		*last += 1;
150		prefix
151	}
152
153	#[test]
154	fn value_translate_works() {
155		let t = RuntimeGenesisConfig::default().build_storage().unwrap();
156		TestExternalities::new(t).execute_with(|| {
157			type Value = self::frame_system::Value<Runtime>;
158
159			// put the old value `1111u32` in the storage.
160			let key = Value::storage_value_final_key();
161			unhashed::put_raw(&key, &1111u32.encode());
162
163			// translate
164			let translate_fn = |old: Option<u32>| -> Option<(u64, u64)> {
165				old.map(|o| (o.into(), (o * 2).into()))
166			};
167			let res = Value::translate(translate_fn);
168			debug_assert!(res.is_ok());
169
170			// new storage should be `(1111, 1111 * 2)`
171			assert_eq!(Value::get(), (1111, 2222));
172		})
173	}
174
175	#[test]
176	fn map_translate_works() {
177		let t = RuntimeGenesisConfig::default().build_storage().unwrap();
178		TestExternalities::new(t).execute_with(|| {
179			type NumberMap = self::frame_system::NumberMap<Runtime>;
180
181			// start with a map of u32 -> u64.
182			for i in 0u32..100u32 {
183				unhashed::put(&NumberMap::hashed_key_for(&i), &(i as u64));
184			}
185
186			assert_eq!(
187				NumberMap::iter().collect::<Vec<_>>(),
188				(0..100).map(|x| (x as u32, x as u64)).collect::<Vec<_>>(),
189			);
190
191			// do translation.
192			NumberMap::translate(
193				|k: u32, v: u64| if k % 2 == 0 { Some(((k as u64) << 32) | v) } else { None },
194			);
195
196			assert_eq!(
197				NumberMap::iter().collect::<Vec<_>>(),
198				(0..50u32)
199					.map(|x| x * 2)
200					.map(|x| (x, ((x as u64) << 32) | x as u64))
201					.collect::<Vec<_>>(),
202			);
203		})
204	}
205
206	#[test]
207	fn try_mutate_works() {
208		let t = RuntimeGenesisConfig::default().build_storage().unwrap();
209		TestExternalities::new(t).execute_with(|| {
210			type Value = self::frame_system::Value<Runtime>;
211			type NumberMap = self::frame_system::NumberMap<Runtime>;
212			type DoubleMap = self::frame_system::DoubleMap<Runtime>;
213
214			assert_eq!(Value::get(), (0, 0));
215			assert_eq!(NumberMap::get(0), 0);
216			assert_eq!(DoubleMap::get(0, 0), 0);
217
218			// `assert_noop` ensures that the state does not change
219			assert_noop!(
220				Value::try_mutate(|value| -> Result<(), &'static str> {
221					*value = (2, 2);
222					Err("don't change value")
223				}),
224				"don't change value"
225			);
226
227			assert_noop!(
228				NumberMap::try_mutate(0, |value| -> Result<(), &'static str> {
229					*value = 4;
230					Err("don't change value")
231				}),
232				"don't change value"
233			);
234
235			assert_noop!(
236				DoubleMap::try_mutate(0, 0, |value| -> Result<(), &'static str> {
237					*value = 6;
238					Err("don't change value")
239				}),
240				"don't change value"
241			);
242
243			// Showing this explicitly for clarity
244			assert_eq!(Value::get(), (0, 0));
245			assert_eq!(NumberMap::get(0), 0);
246			assert_eq!(DoubleMap::get(0, 0), 0);
247
248			assert_ok!(Value::try_mutate(|value| -> Result<(), &'static str> {
249				*value = (2, 2);
250				Ok(())
251			}));
252
253			assert_ok!(NumberMap::try_mutate(0, |value| -> Result<(), &'static str> {
254				*value = 4;
255				Ok(())
256			}));
257
258			assert_ok!(DoubleMap::try_mutate(0, 0, |value| -> Result<(), &'static str> {
259				*value = 6;
260				Ok(())
261			}));
262
263			assert_eq!(Value::get(), (2, 2));
264			assert_eq!(NumberMap::get(0), 4);
265			assert_eq!(DoubleMap::get(0, 0), 6);
266		});
267	}
268}