common_primitives/
offchain.rs1use crate::msa::MessageSourceId;
2use numtoa::NumToA;
3use parity_scale_codec::{Decode, Encode};
4#[cfg(feature = "std")]
5use sp_externalities::ExternalitiesExt;
6use sp_runtime::offchain::storage::{StorageRetrievalError, StorageValueRef};
7extern crate alloc;
8use alloc::vec::Vec;
9use core::fmt::Debug;
10use sp_runtime_interface::{
11	pass_by::{AllocateAndReturnByCodec, PassFatPointerAndReadWrite},
12	runtime_interface,
13};
14
15#[cfg(feature = "std")]
16sp_externalities::decl_extension! {
17	pub struct OcwCustomExt (
19		Vec<u8>
21	);
22}
23
24#[runtime_interface]
26pub trait Custom: ExternalitiesExt {
27	fn get_val(&mut self) -> AllocateAndReturnByCodec<Option<Vec<u8>>> {
29		self.extension::<OcwCustomExt>().map(|ext| ext.0.clone())
30	}
31
32	fn get_val_buffered(&mut self, output: PassFatPointerAndReadWrite<&mut [u8]>) -> u32 {
36		match self.extension::<OcwCustomExt>() {
37			Some(ext) => {
38				let encoded = ext.0.clone().encode();
39				let written = core::cmp::min(encoded.len(), output.len());
40				output[..written].copy_from_slice(&encoded[..written]);
41				written as u32
42			},
43			None => 0,
44		}
45	}
46}
47pub const MSA_ACCOUNT_LOCK_TIMEOUT_EXPIRATION_MS: u64 = 50;
49pub const MSA_ACCOUNT_LOCK_NAME_PREFIX: &[u8; 16] = b"Msa::ofw::lock::";
51pub const MSA_ACCOUNT_STORAGE_NAME_PREFIX: &[u8; 16] = b"Msa::ofw::keys::";
53pub fn get_msa_account_lock_name(msa_id: MessageSourceId) -> Vec<u8> {
55	let mut buff = [0u8; 30];
56	[MSA_ACCOUNT_LOCK_NAME_PREFIX, msa_id.numtoa(10, &mut buff)].concat()
57}
58pub fn get_msa_account_storage_key_name(msa_id: MessageSourceId) -> Vec<u8> {
60	let mut buff = [0u8; 30];
61	[MSA_ACCOUNT_STORAGE_NAME_PREFIX, msa_id.numtoa(10, &mut buff)].concat()
62}
63
64#[derive(Debug)]
66pub enum LockStatus {
67	Locked,
69	Released,
71}
72
73pub fn get_index_value<V: Decode + Debug>(key: &[u8]) -> Result<Option<V>, StorageRetrievalError> {
75	get_impl::<V>(key)
76}
77
78fn get_impl<V: Decode + Debug>(key: &[u8]) -> Result<Option<V>, StorageRetrievalError> {
80	let oci_mem = StorageValueRef::persistent(key);
81	match oci_mem.get::<V>() {
82		Ok(Some(data)) => Ok(Some(data)),
83		Ok(None) => Ok(None),
84		Err(_) => Err(StorageRetrievalError::Undecodable),
85	}
86}
87
88#[cfg(test)]
89mod tests {
90	use super::*;
91	use sp_core::offchain::{testing, OffchainDbExt, OffchainWorkerExt};
92	use sp_io::TestExternalities;
93
94	#[test]
95	fn get_msa_account_lock_name_should_return_expected_value() {
96		let msa_id: MessageSourceId = 2_000_000;
97		let result = get_msa_account_lock_name(msa_id);
98		assert_eq!(result, b"Msa::ofw::lock::2000000".to_vec());
99	}
100
101	#[test]
102	fn get_msa_account_storage_name_should_return_expected_value() {
103		let msa_id: MessageSourceId = 2_000_000;
104		let result = get_msa_account_storage_key_name(msa_id);
105		assert_eq!(result, b"Msa::ofw::keys::2000000".to_vec());
106	}
107
108	#[test]
109	fn get_index_for_not_set_should_return_none() {
110		let (offchain, _state) = testing::TestOffchainExt::new();
111		let mut t = TestExternalities::default();
112		t.register_extension(OffchainDbExt::new(offchain.clone()));
113		t.register_extension(OffchainWorkerExt::new(offchain));
114
115		t.execute_with(|| {
116			let key = b"my_key";
117			let result = get_index_value::<MessageSourceId>(key);
118			assert_eq!(result, Ok(None));
119		});
120	}
121
122	#[test]
123	fn get_index_for_set_should_return_expected() {
124		let (offchain, _state) = testing::TestOffchainExt::new();
126		let mut t = TestExternalities::default();
127		t.register_extension(OffchainDbExt::new(offchain.clone()));
128		t.register_extension(OffchainWorkerExt::new(offchain));
129
130		t.execute_with(|| {
131			let key = b"my_key1";
132			let msa_id: MessageSourceId = 1000000;
133			let oci_mem = StorageValueRef::persistent(key);
134			oci_mem.set(&msa_id);
135
136			let result = get_index_value::<MessageSourceId>(key);
138
139			assert_eq!(result, Ok(Some(msa_id)));
141		});
142	}
143
144	#[test]
145	fn get_index_for_not_decodable_should_return_error() {
146		let (offchain, _state) = testing::TestOffchainExt::new();
147		let mut t = TestExternalities::default();
148		t.register_extension(OffchainDbExt::new(offchain.clone()));
149		t.register_extension(OffchainWorkerExt::new(offchain));
150
151		#[derive(Debug, Decode, PartialEq)]
152		struct Testing {
153			pub a: u64,
154			pub b: u32,
155			pub c: u16,
156		}
157
158		t.execute_with(|| {
159			let key = b"my_key2";
161			let msa_id: MessageSourceId = 1000000;
162			let oci_mem = StorageValueRef::persistent(key);
163			oci_mem.set(&msa_id);
164
165			let result = get_index_value::<Testing>(key);
167
168			assert_eq!(result, Err(StorageRetrievalError::Undecodable));
170		});
171	}
172}