common_primitives/
offchain.rs

1use crate::msa::MessageSourceId;
2use numtoa::NumToA;
3use parity_scale_codec::Decode;
4#[cfg(feature = "std")]
5use sp_externalities::ExternalitiesExt;
6use sp_runtime::offchain::storage::{StorageRetrievalError, StorageValueRef};
7use sp_runtime_interface::runtime_interface;
8extern crate alloc;
9use alloc::vec::Vec;
10use core::fmt::Debug;
11
12#[cfg(feature = "std")]
13sp_externalities::decl_extension! {
14	/// Offchain worker custom extension
15	pub struct OcwCustomExt (
16		// rpc address provided to offchain worker
17		Vec<u8>
18	);
19}
20
21/// runtime new customized
22#[runtime_interface]
23pub trait Custom: ExternalitiesExt {
24	/// another function
25	fn get_val(&mut self) -> Option<Vec<u8>> {
26		self.extension::<OcwCustomExt>().map(|ext| ext.0.clone())
27	}
28}
29/// Lock expiration timeout in milli-seconds for msa pallet per msa account
30pub const MSA_ACCOUNT_LOCK_TIMEOUT_EXPIRATION_MS: u64 = 50;
31/// Lock name prefix for msa account
32pub const MSA_ACCOUNT_LOCK_NAME_PREFIX: &[u8; 16] = b"Msa::ofw::lock::";
33/// Offchain storage prefix for msa account
34pub const MSA_ACCOUNT_STORAGE_NAME_PREFIX: &[u8; 16] = b"Msa::ofw::keys::";
35/// msa account lock name
36pub fn get_msa_account_lock_name(msa_id: MessageSourceId) -> Vec<u8> {
37	let mut buff = [0u8; 30];
38	[MSA_ACCOUNT_LOCK_NAME_PREFIX, msa_id.numtoa(10, &mut buff)].concat()
39}
40/// msa account storage key name
41pub fn get_msa_account_storage_key_name(msa_id: MessageSourceId) -> Vec<u8> {
42	let mut buff = [0u8; 30];
43	[MSA_ACCOUNT_STORAGE_NAME_PREFIX, msa_id.numtoa(10, &mut buff)].concat()
44}
45
46/// Locks the execution of the function
47#[derive(Debug)]
48pub enum LockStatus {
49	/// Lock is acquired
50	Locked,
51	/// Lock is released
52	Released,
53}
54
55/// Wrapper for offchain get operations
56pub fn get_index_value<V: Decode + Debug>(key: &[u8]) -> Result<Option<V>, StorageRetrievalError> {
57	get_impl::<V>(key)
58}
59
60/// Gets a value by the key from persistent storage
61fn get_impl<V: Decode + Debug>(key: &[u8]) -> Result<Option<V>, StorageRetrievalError> {
62	let oci_mem = StorageValueRef::persistent(key);
63	match oci_mem.get::<V>() {
64		Ok(Some(data)) => Ok(Some(data)),
65		Ok(None) => Ok(None),
66		Err(_) => Err(StorageRetrievalError::Undecodable),
67	}
68}
69
70#[cfg(test)]
71mod tests {
72	use super::*;
73	use sp_core::offchain::{testing, OffchainDbExt, OffchainWorkerExt};
74	use sp_io::TestExternalities;
75
76	#[test]
77	fn get_msa_account_lock_name_should_return_expected_value() {
78		let msa_id: MessageSourceId = 2_000_000;
79		let result = get_msa_account_lock_name(msa_id);
80		assert_eq!(result, b"Msa::ofw::lock::2000000".to_vec());
81	}
82
83	#[test]
84	fn get_msa_account_storage_name_should_return_expected_value() {
85		let msa_id: MessageSourceId = 2_000_000;
86		let result = get_msa_account_storage_key_name(msa_id);
87		assert_eq!(result, b"Msa::ofw::keys::2000000".to_vec());
88	}
89
90	#[test]
91	fn get_index_for_not_set_should_return_none() {
92		let (offchain, _state) = testing::TestOffchainExt::new();
93		let mut t = TestExternalities::default();
94		t.register_extension(OffchainDbExt::new(offchain.clone()));
95		t.register_extension(OffchainWorkerExt::new(offchain));
96
97		t.execute_with(|| {
98			let key = b"my_key";
99			let result = get_index_value::<MessageSourceId>(key);
100			assert_eq!(result, Ok(None));
101		});
102	}
103
104	#[test]
105	fn get_index_for_set_should_return_expected() {
106		// arrange
107		let (offchain, _state) = testing::TestOffchainExt::new();
108		let mut t = TestExternalities::default();
109		t.register_extension(OffchainDbExt::new(offchain.clone()));
110		t.register_extension(OffchainWorkerExt::new(offchain));
111
112		t.execute_with(|| {
113			let key = b"my_key1";
114			let msa_id: MessageSourceId = 1000000;
115			let oci_mem = StorageValueRef::persistent(key);
116			oci_mem.set(&msa_id);
117
118			// act
119			let result = get_index_value::<MessageSourceId>(key);
120
121			// assert
122			assert_eq!(result, Ok(Some(msa_id)));
123		});
124	}
125
126	#[test]
127	fn get_index_for_not_decodable_should_return_error() {
128		let (offchain, _state) = testing::TestOffchainExt::new();
129		let mut t = TestExternalities::default();
130		t.register_extension(OffchainDbExt::new(offchain.clone()));
131		t.register_extension(OffchainWorkerExt::new(offchain));
132
133		#[derive(Debug, Decode, PartialEq)]
134		struct Testing {
135			pub a: u64,
136			pub b: u32,
137			pub c: u16,
138		}
139
140		t.execute_with(|| {
141			// arrange
142			let key = b"my_key2";
143			let msa_id: MessageSourceId = 1000000;
144			let oci_mem = StorageValueRef::persistent(key);
145			oci_mem.set(&msa_id);
146
147			// act
148			let result = get_index_value::<Testing>(key);
149
150			// assert
151			assert_eq!(result, Err(StorageRetrievalError::Undecodable));
152		});
153	}
154}