orml_utilities/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3use frame_support::storage::{with_transaction, TransactionOutcome};
4use sp_runtime::DispatchError;
5use sp_std::result::Result;
6
7pub mod offchain_worker;
8pub mod ordered_set;
9
10pub use offchain_worker::OffchainErr;
11pub use ordered_set::OrderedSet;
12
13/// Execute the supplied function in a new storage transaction.
14///
15/// All changes to storage performed by the supplied function are discarded if
16/// the returned outcome is `Result::Err`.
17///
18/// Transactions can be nested to any depth. Commits happen to the parent
19/// transaction.
20pub fn with_transaction_result<R>(f: impl FnOnce() -> Result<R, DispatchError>) -> Result<R, DispatchError> {
21	with_transaction(|| {
22		let res = f();
23		if res.is_ok() {
24			TransactionOutcome::Commit(res)
25		} else {
26			TransactionOutcome::Rollback(res)
27		}
28	})
29}
30
31/// Simulate execution of the supplied function in a new storage transaction.
32/// Changes to storage performed by the supplied function are always discarded.
33pub fn simulate_execution<R>(f: impl FnOnce() -> Result<R, DispatchError>) -> Result<R, DispatchError> {
34	with_transaction(|| {
35		let res = f();
36		TransactionOutcome::Rollback(res)
37	})
38}
39
40#[cfg(test)]
41mod tests {
42	use super::*;
43	use frame_support::{assert_noop, assert_ok, construct_runtime, derive_impl, pallet_prelude::*};
44	use sp_io::TestExternalities;
45	use sp_runtime::traits::IdentityLookup;
46	use sp_runtime::{DispatchError, DispatchResult};
47	use sp_std::result::Result;
48
49	#[allow(dead_code)]
50	#[frame_support::pallet]
51	pub mod module {
52		use super::*;
53
54		#[pallet::config]
55		pub trait Config: frame_system::Config {}
56
57		#[pallet::pallet]
58		pub struct Pallet<T>(_);
59
60		#[pallet::storage]
61		pub type Value<T: Config> = StorageValue<_, u32, ValueQuery>;
62
63		#[pallet::storage]
64		pub type Map<T: Config> = StorageMap<_, Twox64Concat, [u8; 4], u32, ValueQuery>;
65	}
66
67	use module::*;
68
69	#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
70	impl frame_system::Config for Runtime {
71		type AccountId = u128;
72		type Lookup = IdentityLookup<Self::AccountId>;
73		type Block = Block;
74	}
75
76	impl module::Config for Runtime {}
77
78	type Block = frame_system::mocking::MockBlock<Runtime>;
79
80	construct_runtime!(
81		pub enum Runtime {
82			System: frame_system,
83			TestModule: module,
84		}
85	);
86
87	#[test]
88	fn storage_transaction_basic_commit() {
89		TestExternalities::default().execute_with(|| {
90			assert_eq!(Value::<Runtime>::get(), 0);
91			assert!(!Map::<Runtime>::contains_key(b"val0"));
92
93			assert_ok!(with_transaction_result(|| -> DispatchResult {
94				Value::<Runtime>::set(99);
95				Map::<Runtime>::insert(b"val0", 99);
96				assert_eq!(Value::<Runtime>::get(), 99);
97				assert_eq!(Map::<Runtime>::get(b"val0"), 99);
98				Ok(())
99			}));
100
101			assert_eq!(Value::<Runtime>::get(), 99);
102			assert_eq!(Map::<Runtime>::get(b"val0"), 99);
103		});
104	}
105
106	#[test]
107	fn storage_transaction_basic_rollback() {
108		TestExternalities::default().execute_with(|| {
109			assert_eq!(Value::<Runtime>::get(), 0);
110			assert_eq!(Map::<Runtime>::get(b"val0"), 0);
111
112			assert_noop!(
113				with_transaction_result(|| -> DispatchResult {
114					Value::<Runtime>::set(99);
115					Map::<Runtime>::insert(b"val0", 99);
116					assert_eq!(Value::<Runtime>::get(), 99);
117					assert_eq!(Map::<Runtime>::get(b"val0"), 99);
118					Err("test".into())
119				}),
120				DispatchError::Other("test")
121			);
122
123			assert_eq!(Value::<Runtime>::get(), 0);
124			assert_eq!(Map::<Runtime>::get(b"val0"), 0);
125		});
126	}
127
128	#[test]
129	fn simulate_execution_works() {
130		TestExternalities::default().execute_with(|| {
131			assert_eq!(Value::<Runtime>::get(), 0);
132			assert_eq!(Map::<Runtime>::get(b"val0"), 0);
133
134			// Roll back on `Err`.
135			assert_noop!(
136				simulate_execution(|| -> DispatchResult {
137					Value::<Runtime>::set(99);
138					Map::<Runtime>::insert(b"val0", 99);
139					Err(DispatchError::Other("test"))
140				}),
141				DispatchError::Other("test")
142			);
143			assert_eq!(Value::<Runtime>::get(), 0);
144			assert_eq!(Map::<Runtime>::get(b"val0"), 0);
145
146			// Roll back on `Ok`, but returns `Ok` result.
147			assert_ok!(
148				simulate_execution(|| -> Result<u32, DispatchError> {
149					Value::<Runtime>::set(99);
150					Map::<Runtime>::insert(b"val0", 99);
151					Ok(99)
152				}),
153				99
154			);
155			assert_eq!(Value::<Runtime>::get(), 0);
156			assert_eq!(Map::<Runtime>::get(b"val0"), 0);
157		});
158	}
159}