1#![cfg_attr(not(feature = "std"), no_std)]
88#![deny(missing_docs)]
89
90extern crate alloc;
91
92use alloc::fmt::Debug;
93use alloc::string::String;
94use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
95use scale_info::TypeInfo;
96use sidechain_domain::{byte_string::*, *};
97#[cfg(feature = "std")]
98use sp_api::*;
99use sp_inherents::*;
100use sp_runtime::BoundedVec;
101use sp_runtime::traits::Get;
102
103#[cfg(any(test, feature = "mock"))]
104mod mock;
105#[cfg(test)]
106mod tests;
107
108pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"govrnmap";
110
111#[derive(
113 Debug,
114 Clone,
115 PartialEq,
116 Eq,
117 TypeInfo,
118 Encode,
119 Decode,
120 DecodeWithMemTracking,
121 MaxEncodedLen,
122 Default,
123)]
124#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
125pub struct MainChainScriptsV1 {
126 pub validator_address: MainchainAddress,
128 pub asset_policy_id: PolicyId,
130}
131
132#[derive(Decode, Encode, Debug, PartialEq)]
134#[cfg_attr(feature = "std", derive(thiserror::Error))]
135pub enum InherentError {
136 #[cfg_attr(feature = "std", error("Inherent missing for Governed Map pallet"))]
138 InherentMissing,
139 #[cfg_attr(feature = "std", error("Unexpected inherent for Governed Map pallet"))]
141 InherentNotExpected,
142 #[cfg_attr(
143 feature = "std",
144 error("Data in Governed Map pallet inherent differs from inherent data")
145 )]
146 IncorrectInherent,
148 #[cfg_attr(feature = "std", error("Governed Map key {0} exceeds size bounds"))]
149 KeyExceedsBounds(String),
151 #[cfg_attr(feature = "std", error("Governed Map value {1:?} for key {0} exceeds size bounds"))]
152 ValueExceedsBounds(String, ByteString),
154 #[cfg_attr(feature = "std", error("Number of changes to the Governed Map exceeds the limit"))]
160 TooManyChanges,
161}
162
163impl IsFatalError for InherentError {
164 fn is_fatal_error(&self) -> bool {
165 true
166 }
167}
168
169pub trait OnGovernedMappingChange<MaxKeyLength, MaxValueLength>
171where
172 MaxKeyLength: Get<u32>,
173 MaxValueLength: Get<u32>,
174{
175 fn on_governed_mapping_change(
177 key: BoundedString<MaxKeyLength>,
178 new_value: Option<BoundedVec<u8, MaxValueLength>>,
179 old_value: Option<BoundedVec<u8, MaxValueLength>>,
180 );
181}
182
183impl<MaxKeyLength, MaxValueLength> OnGovernedMappingChange<MaxKeyLength, MaxValueLength> for ()
184where
185 MaxKeyLength: Get<u32>,
186 MaxValueLength: Get<u32>,
187{
188 fn on_governed_mapping_change(
189 _key: BoundedString<MaxKeyLength>,
190 _new_value: Option<BoundedVec<u8, MaxValueLength>>,
191 _old_value: Option<BoundedVec<u8, MaxValueLength>>,
192 ) {
193 }
194}
195
196macro_rules! impl_tuple_on_governed_mapping_change {
197 ($first_type:ident, $($type:ident),+) => {
198 impl<MaxKeyLength, MaxValueLength, $first_type, $($type),+>
199 OnGovernedMappingChange<MaxKeyLength, MaxValueLength>
200 for ($first_type, $($type),+) where
201 MaxKeyLength: Get<u32>,
202 MaxValueLength: Get<u32>,
203 $first_type: OnGovernedMappingChange<MaxKeyLength, MaxValueLength>,
204 $($type: OnGovernedMappingChange<MaxKeyLength, MaxValueLength>),+
205 {
206 fn on_governed_mapping_change(
207 key: BoundedString<MaxKeyLength>,
208 new_value: Option<BoundedVec<u8, MaxValueLength>>,
209 old_value: Option<BoundedVec<u8, MaxValueLength>>,
210 ) {
211 <$first_type as OnGovernedMappingChange<MaxKeyLength, MaxValueLength>>::on_governed_mapping_change(key.clone(), new_value.clone(), old_value.clone());
212 $(<$type as OnGovernedMappingChange<MaxKeyLength, MaxValueLength>>::on_governed_mapping_change(key.clone(), new_value.clone(), old_value.clone());)+
213 }
214 }
215 };
216}
217
218impl_tuple_on_governed_mapping_change!(A, B);
219impl_tuple_on_governed_mapping_change!(A, B, C);
220impl_tuple_on_governed_mapping_change!(A, B, C, D);
221impl_tuple_on_governed_mapping_change!(A, B, C, D, E);
222
223pub type GovernedMapInherentDataV1 = BTreeMap<String, Option<ByteString>>;
230
231#[cfg(feature = "std")]
233#[derive(Debug, PartialEq)]
234pub enum GovernedMapInherentDataProvider {
235 Inert,
237 ActiveV1 {
239 data: GovernedMapInherentDataV1,
241 },
242}
243
244#[cfg(feature = "std")]
245#[async_trait::async_trait]
246impl sp_inherents::InherentDataProvider for GovernedMapInherentDataProvider {
247 async fn provide_inherent_data(
248 &self,
249 inherent_data: &mut sp_inherents::InherentData,
250 ) -> Result<(), sp_inherents::Error> {
251 match self {
252 Self::ActiveV1 { data } => {
253 inherent_data.put_data(INHERENT_IDENTIFIER, &data)?;
254 },
255 _ => {},
256 }
257 Ok(())
258 }
259
260 async fn try_handle_error(
261 &self,
262 identifier: &InherentIdentifier,
263 mut error: &[u8],
264 ) -> Option<Result<(), sp_inherents::Error>> {
265 if identifier == &INHERENT_IDENTIFIER {
266 let error = InherentError::decode(&mut error).ok()?;
267 Some(Err(sp_inherents::Error::Application(Box::from(error))))
268 } else {
269 None
270 }
271 }
272}
273
274#[cfg(feature = "std")]
276#[async_trait::async_trait]
277pub trait GovernedMapDataSource {
278 async fn get_state_at_block(
280 &self,
281 mc_block: McBlockHash,
282 main_chain_scripts: MainChainScriptsV1,
283 ) -> Result<BTreeMap<String, ByteString>, Box<dyn std::error::Error + Send + Sync>>;
284
285 async fn get_mapping_changes(
296 &self,
297 since_mc_block: Option<McBlockHash>,
298 up_to_mc_block: McBlockHash,
299 main_chain_scripts: MainChainScriptsV1,
300 ) -> Result<Vec<(String, Option<ByteString>)>, Box<dyn std::error::Error + Send + Sync>>;
301}
302
303#[cfg(feature = "std")]
305#[derive(Debug, thiserror::Error)]
306pub enum InherentProviderCreationError {
307 #[error("Runtime API call failed: {0}")]
309 ApiError(#[from] sp_api::ApiError),
310 #[error("Data source call failed: {0}")]
312 DataSourceError(Box<dyn std::error::Error + Send + Sync>),
313 #[error("Unsupported pallet version {0} (highest supported version: {1})")]
315 UnsupportedPalletVersion(u32, u32),
316}
317
318#[cfg(feature = "std")]
319impl GovernedMapInherentDataProvider {
320 pub async fn new<T, Block>(
334 client: &T,
335 parent_hash: Block::Hash,
336 mc_hash: McBlockHash,
337 parent_mc_hash: Option<McBlockHash>,
338 data_source: &(dyn GovernedMapDataSource + Send + Sync),
339 ) -> Result<Self, InherentProviderCreationError>
340 where
341 Block: sp_runtime::traits::Block,
342 T: ProvideRuntimeApi<Block> + Send + Sync,
343 T::Api: GovernedMapIDPApi<Block>,
344 {
345 let api = client.runtime_api();
346 if !api.has_api::<dyn GovernedMapIDPApi<Block>>(parent_hash)? {
347 log::info!("💤 Skipping Governed Map observation. Pallet not detected in the runtime.");
348 return Ok(Self::Inert);
349 }
350
351 match api.get_pallet_version(parent_hash)? {
352 1 => Self::new_v1(client, parent_hash, mc_hash, parent_mc_hash, data_source).await,
353 unsupported_version => {
354 Err(InherentProviderCreationError::UnsupportedPalletVersion(unsupported_version, 1))
355 },
356 }
357 }
358
359 async fn new_v1<T, Block>(
360 client: &T,
361 parent_hash: Block::Hash,
362 mc_hash: McBlockHash,
363 parent_mc_hash: Option<McBlockHash>,
364 data_source: &(dyn GovernedMapDataSource + Send + Sync),
365 ) -> Result<Self, InherentProviderCreationError>
366 where
367 Block: sp_runtime::traits::Block,
368 T: ProvideRuntimeApi<Block> + Send + Sync,
369 T::Api: GovernedMapIDPApi<Block>,
370 {
371 let api = client.runtime_api();
372
373 let Some(main_chain_script) = api.get_main_chain_scripts(parent_hash)? else {
374 log::info!("💤 Skipping Governed Map observation. Main chain scripts not set yet.");
375 return Ok(Self::Inert);
376 };
377
378 if api.is_initialized(parent_hash)? {
379 let raw_changes = data_source
380 .get_mapping_changes(parent_mc_hash, mc_hash, main_chain_script)
381 .await
382 .map_err(InherentProviderCreationError::DataSourceError)?;
383
384 let mut changes = GovernedMapInherentDataV1::new();
385
386 for (key, value) in raw_changes.into_iter() {
387 changes.insert(key, value);
388 }
389
390 Ok(Self::ActiveV1 { data: changes })
391 } else {
392 let new_state = data_source
393 .get_state_at_block(mc_hash, main_chain_script)
394 .await
395 .map_err(InherentProviderCreationError::DataSourceError)?;
396
397 let current_state = api.get_current_state(parent_hash)?;
398
399 let mut changes = GovernedMapInherentDataV1::new();
400
401 for key in current_state.keys() {
402 if !new_state.contains_key(key) {
403 changes.insert(key.clone(), None);
404 }
405 }
406
407 for (key, value) in new_state.into_iter() {
408 if current_state.get(&key) != Some(&value) {
409 changes.insert(key, Some(value));
410 }
411 }
412
413 Ok(Self::ActiveV1 { data: changes })
414 }
415 }
416}
417
418sp_api::decl_runtime_apis! {
419 #[api_version(1)]
421 pub trait GovernedMapIDPApi
422 {
423 fn is_initialized() -> bool;
425 fn get_current_state() -> BTreeMap<String, ByteString>;
427 fn get_main_chain_scripts() -> Option<MainChainScriptsV1>;
429 fn get_pallet_version() -> u32;
431 }
432}