1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use crate::msa::MessageSourceId;
use numtoa::NumToA;
use parity_scale_codec::Decode;
#[cfg(feature = "std")]
use sp_externalities::ExternalitiesExt;
use sp_runtime::offchain::storage::{StorageRetrievalError, StorageValueRef};
use sp_runtime_interface::runtime_interface;
use sp_std::{fmt::Debug, vec, vec::Vec};

#[cfg(feature = "std")]
sp_externalities::decl_extension! {
	/// Offchain worker custom extension
	pub struct OcwCustomExt (
		// rpc address provided to offchain worker
		Vec<u8>
	);
}

/// runtime new customized
#[runtime_interface]
pub trait Custom: ExternalitiesExt {
	/// another function
	fn get_val(&mut self) -> Option<Vec<u8>> {
		self.extension::<OcwCustomExt>().map(|ext| ext.0.clone())
	}
}
/// Lock expiration timeout in milli-seconds for msa pallet per msa account
pub const MSA_ACCOUNT_LOCK_TIMEOUT_EXPIRATION_MS: u64 = 50;
/// Lock name prefix for msa account
pub const MSA_ACCOUNT_LOCK_NAME_PREFIX: &[u8; 16] = b"Msa::ofw::lock::";
/// Offchain storage prefix for msa account
pub const MSA_ACCOUNT_STORAGE_NAME_PREFIX: &[u8; 16] = b"Msa::ofw::keys::";
/// msa account lock name
pub fn get_msa_account_lock_name(msa_id: MessageSourceId) -> Vec<u8> {
	let mut buff = [0u8; 30];
	vec![MSA_ACCOUNT_LOCK_NAME_PREFIX, msa_id.numtoa(10, &mut buff)].concat()
}
/// msa account storage key name
pub fn get_msa_account_storage_key_name(msa_id: MessageSourceId) -> Vec<u8> {
	let mut buff = [0u8; 30];
	vec![MSA_ACCOUNT_STORAGE_NAME_PREFIX, msa_id.numtoa(10, &mut buff)].concat()
}

/// Locks the execution of the function
#[derive(Debug)]
pub enum LockStatus {
	/// Lock is acquired
	Locked,
	/// Lock is released
	Released,
}

/// Wrapper for offchain get operations
pub fn get_index_value<V: Decode + Debug>(key: &[u8]) -> Result<Option<V>, StorageRetrievalError> {
	get_impl::<V>(key)
}

/// Gets a value by the key from persistent storage
fn get_impl<V: Decode + Debug>(key: &[u8]) -> Result<Option<V>, StorageRetrievalError> {
	let oci_mem = StorageValueRef::persistent(key);
	match oci_mem.get::<V>() {
		Ok(Some(data)) => Ok(Some(data)),
		Ok(None) => Ok(None),
		Err(_) => Err(StorageRetrievalError::Undecodable),
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use sp_core::offchain::{testing, OffchainDbExt, OffchainWorkerExt};
	use sp_io::TestExternalities;

	#[test]
	fn get_msa_account_lock_name_should_return_expected_value() {
		let msa_id: MessageSourceId = 2_000_000;
		let result = get_msa_account_lock_name(msa_id);
		assert_eq!(result, b"Msa::ofw::lock::2000000".to_vec());
	}

	#[test]
	fn get_msa_account_storage_name_should_return_expected_value() {
		let msa_id: MessageSourceId = 2_000_000;
		let result = get_msa_account_storage_key_name(msa_id);
		assert_eq!(result, b"Msa::ofw::keys::2000000".to_vec());
	}

	#[test]
	fn get_index_for_not_set_should_return_none() {
		let (offchain, _state) = testing::TestOffchainExt::new();
		let mut t = TestExternalities::default();
		t.register_extension(OffchainDbExt::new(offchain.clone()));
		t.register_extension(OffchainWorkerExt::new(offchain));

		t.execute_with(|| {
			let key = b"my_key";
			let result = get_index_value::<MessageSourceId>(key);
			assert_eq!(result, Ok(None));
		});
	}

	#[test]
	fn get_index_for_set_should_return_expected() {
		// arrange
		let (offchain, _state) = testing::TestOffchainExt::new();
		let mut t = TestExternalities::default();
		t.register_extension(OffchainDbExt::new(offchain.clone()));
		t.register_extension(OffchainWorkerExt::new(offchain));

		t.execute_with(|| {
			let key = b"my_key1";
			let msa_id: MessageSourceId = 1000000;
			let oci_mem = StorageValueRef::persistent(key);
			oci_mem.set(&msa_id);

			// act
			let result = get_index_value::<MessageSourceId>(key);

			// assert
			assert_eq!(result, Ok(Some(msa_id)));
		});
	}

	#[test]
	fn get_index_for_not_decodable_should_return_error() {
		let (offchain, _state) = testing::TestOffchainExt::new();
		let mut t = TestExternalities::default();
		t.register_extension(OffchainDbExt::new(offchain.clone()));
		t.register_extension(OffchainWorkerExt::new(offchain));

		#[derive(Debug, Decode, PartialEq)]
		struct Testing {
			pub a: u64,
			pub b: u32,
			pub c: u16,
		}

		t.execute_with(|| {
			// arrange
			let key = b"my_key2";
			let msa_id: MessageSourceId = 1000000;
			let oci_mem = StorageValueRef::persistent(key);
			oci_mem.set(&msa_id);

			// act
			let result = get_index_value::<Testing>(key);

			// assert
			assert_eq!(result, Err(StorageRetrievalError::Undecodable));
		});
	}
}