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#[cfg(feature = "std")]
133impl MainChainScriptsV1 {
134 pub fn read_from_env() -> Result<Self, envy::Error> {
140 #[derive(serde::Serialize, serde::Deserialize)]
141 pub struct MainChainScriptsEnvConfig {
142 pub governed_map_validator_address: MainchainAddress,
143 pub governed_map_policy_id: PolicyId,
144 }
145
146 let MainChainScriptsEnvConfig { governed_map_validator_address, governed_map_policy_id } =
147 envy::from_env::<MainChainScriptsEnvConfig>()?;
148
149 Ok(Self {
150 validator_address: governed_map_validator_address,
151 asset_policy_id: governed_map_policy_id,
152 })
153 }
154}
155
156#[derive(Decode, Encode, Debug, PartialEq)]
158#[cfg_attr(feature = "std", derive(thiserror::Error))]
159pub enum InherentError {
160 #[cfg_attr(feature = "std", error("Inherent missing for Governed Map pallet"))]
162 InherentMissing,
163 #[cfg_attr(feature = "std", error("Unexpected inherent for Governed Map pallet"))]
165 InherentNotExpected,
166 #[cfg_attr(
167 feature = "std",
168 error("Data in Governed Map pallet inherent differs from inherent data")
169 )]
170 IncorrectInherent,
172 #[cfg_attr(feature = "std", error("Governed Map key {0} exceeds size bounds"))]
173 KeyExceedsBounds(String),
175 #[cfg_attr(feature = "std", error("Governed Map value {1:?} for key {0} exceeds size bounds"))]
176 ValueExceedsBounds(String, ByteString),
178 #[cfg_attr(feature = "std", error("Number of changes to the Governed Map exceeds the limit"))]
184 TooManyChanges,
185}
186
187impl IsFatalError for InherentError {
188 fn is_fatal_error(&self) -> bool {
189 true
190 }
191}
192
193pub trait OnGovernedMappingChange<MaxKeyLength, MaxValueLength>
195where
196 MaxKeyLength: Get<u32>,
197 MaxValueLength: Get<u32>,
198{
199 fn on_governed_mapping_change(
201 key: BoundedString<MaxKeyLength>,
202 new_value: Option<BoundedVec<u8, MaxValueLength>>,
203 old_value: Option<BoundedVec<u8, MaxValueLength>>,
204 );
205}
206
207impl<MaxKeyLength, MaxValueLength> OnGovernedMappingChange<MaxKeyLength, MaxValueLength> for ()
208where
209 MaxKeyLength: Get<u32>,
210 MaxValueLength: Get<u32>,
211{
212 fn on_governed_mapping_change(
213 _key: BoundedString<MaxKeyLength>,
214 _new_value: Option<BoundedVec<u8, MaxValueLength>>,
215 _old_value: Option<BoundedVec<u8, MaxValueLength>>,
216 ) {
217 }
218}
219
220macro_rules! impl_tuple_on_governed_mapping_change {
221 ($first_type:ident, $($type:ident),+) => {
222 impl<MaxKeyLength, MaxValueLength, $first_type, $($type),+>
223 OnGovernedMappingChange<MaxKeyLength, MaxValueLength>
224 for ($first_type, $($type),+) where
225 MaxKeyLength: Get<u32>,
226 MaxValueLength: Get<u32>,
227 $first_type: OnGovernedMappingChange<MaxKeyLength, MaxValueLength>,
228 $($type: OnGovernedMappingChange<MaxKeyLength, MaxValueLength>),+
229 {
230 fn on_governed_mapping_change(
231 key: BoundedString<MaxKeyLength>,
232 new_value: Option<BoundedVec<u8, MaxValueLength>>,
233 old_value: Option<BoundedVec<u8, MaxValueLength>>,
234 ) {
235 <$first_type as OnGovernedMappingChange<MaxKeyLength, MaxValueLength>>::on_governed_mapping_change(key.clone(), new_value.clone(), old_value.clone());
236 $(<$type as OnGovernedMappingChange<MaxKeyLength, MaxValueLength>>::on_governed_mapping_change(key.clone(), new_value.clone(), old_value.clone());)+
237 }
238 }
239 };
240}
241
242impl_tuple_on_governed_mapping_change!(A, B);
243impl_tuple_on_governed_mapping_change!(A, B, C);
244impl_tuple_on_governed_mapping_change!(A, B, C, D);
245impl_tuple_on_governed_mapping_change!(A, B, C, D, E);
246
247pub type GovernedMapInherentDataV1 = BTreeMap<String, Option<ByteString>>;
254
255#[cfg(feature = "std")]
257#[derive(Debug, PartialEq)]
258pub enum GovernedMapInherentDataProvider {
259 Inert,
261 ActiveV1 {
263 data: GovernedMapInherentDataV1,
265 },
266}
267
268#[cfg(feature = "std")]
269#[async_trait::async_trait]
270impl sp_inherents::InherentDataProvider for GovernedMapInherentDataProvider {
271 async fn provide_inherent_data(
272 &self,
273 inherent_data: &mut sp_inherents::InherentData,
274 ) -> Result<(), sp_inherents::Error> {
275 match self {
276 Self::ActiveV1 { data } => {
277 inherent_data.put_data(INHERENT_IDENTIFIER, &data)?;
278 },
279 _ => {},
280 }
281 Ok(())
282 }
283
284 async fn try_handle_error(
285 &self,
286 identifier: &InherentIdentifier,
287 mut error: &[u8],
288 ) -> Option<Result<(), sp_inherents::Error>> {
289 if identifier == &INHERENT_IDENTIFIER {
290 let error = InherentError::decode(&mut error).ok()?;
291 Some(Err(sp_inherents::Error::Application(Box::from(error))))
292 } else {
293 None
294 }
295 }
296}
297
298#[cfg(feature = "std")]
300#[async_trait::async_trait]
301pub trait GovernedMapDataSource {
302 async fn get_state_at_block(
304 &self,
305 mc_block: McBlockHash,
306 main_chain_scripts: MainChainScriptsV1,
307 ) -> Result<BTreeMap<String, ByteString>, Box<dyn std::error::Error + Send + Sync>>;
308
309 async fn get_mapping_changes(
320 &self,
321 since_mc_block: Option<McBlockHash>,
322 up_to_mc_block: McBlockHash,
323 main_chain_scripts: MainChainScriptsV1,
324 ) -> Result<Vec<(String, Option<ByteString>)>, Box<dyn std::error::Error + Send + Sync>>;
325}
326
327#[cfg(feature = "std")]
329#[derive(Debug, thiserror::Error)]
330pub enum InherentProviderCreationError {
331 #[error("Runtime API call failed: {0}")]
333 ApiError(#[from] sp_api::ApiError),
334 #[error("Data source call failed: {0}")]
336 DataSourceError(Box<dyn std::error::Error + Send + Sync>),
337 #[error("Unsupported pallet version {0} (highest supported version: {1})")]
339 UnsupportedPalletVersion(u32, u32),
340}
341
342#[cfg(feature = "std")]
343impl GovernedMapInherentDataProvider {
344 pub async fn new<T, Block>(
358 client: &T,
359 parent_hash: Block::Hash,
360 mc_hash: McBlockHash,
361 parent_mc_hash: Option<McBlockHash>,
362 data_source: &(dyn GovernedMapDataSource + Send + Sync),
363 ) -> Result<Self, InherentProviderCreationError>
364 where
365 Block: sp_runtime::traits::Block,
366 T: ProvideRuntimeApi<Block> + Send + Sync,
367 T::Api: GovernedMapIDPApi<Block>,
368 {
369 let api = client.runtime_api();
370 if !api.has_api::<dyn GovernedMapIDPApi<Block>>(parent_hash)? {
371 log::info!("💤 Skipping Governed Map observation. Pallet not detected in the runtime.");
372 return Ok(Self::Inert);
373 }
374
375 match api.get_pallet_version(parent_hash)? {
376 1 => Self::new_v1(client, parent_hash, mc_hash, parent_mc_hash, data_source).await,
377 unsupported_version => {
378 Err(InherentProviderCreationError::UnsupportedPalletVersion(unsupported_version, 1))
379 },
380 }
381 }
382
383 async fn new_v1<T, Block>(
384 client: &T,
385 parent_hash: Block::Hash,
386 mc_hash: McBlockHash,
387 parent_mc_hash: Option<McBlockHash>,
388 data_source: &(dyn GovernedMapDataSource + Send + Sync),
389 ) -> Result<Self, InherentProviderCreationError>
390 where
391 Block: sp_runtime::traits::Block,
392 T: ProvideRuntimeApi<Block> + Send + Sync,
393 T::Api: GovernedMapIDPApi<Block>,
394 {
395 let api = client.runtime_api();
396
397 let Some(main_chain_script) = api.get_main_chain_scripts(parent_hash)? else {
398 log::info!("💤 Skipping Governed Map observation. Main chain scripts not set yet.");
399 return Ok(Self::Inert);
400 };
401
402 if api.is_initialized(parent_hash)? {
403 let raw_changes = data_source
404 .get_mapping_changes(parent_mc_hash, mc_hash, main_chain_script)
405 .await
406 .map_err(InherentProviderCreationError::DataSourceError)?;
407
408 let mut changes = GovernedMapInherentDataV1::new();
409
410 for (key, value) in raw_changes.into_iter() {
411 changes.insert(key, value);
412 }
413
414 Ok(Self::ActiveV1 { data: changes })
415 } else {
416 let new_state = data_source
417 .get_state_at_block(mc_hash, main_chain_script)
418 .await
419 .map_err(InherentProviderCreationError::DataSourceError)?;
420
421 let current_state = api.get_current_state(parent_hash)?;
422
423 let mut changes = GovernedMapInherentDataV1::new();
424
425 for key in current_state.keys() {
426 if !new_state.contains_key(key) {
427 changes.insert(key.clone(), None);
428 }
429 }
430
431 for (key, value) in new_state.into_iter() {
432 if current_state.get(&key) != Some(&value) {
433 changes.insert(key, Some(value));
434 }
435 }
436
437 Ok(Self::ActiveV1 { data: changes })
438 }
439 }
440}
441
442sp_api::decl_runtime_apis! {
443 #[api_version(1)]
445 pub trait GovernedMapIDPApi
446 {
447 fn is_initialized() -> bool;
449 fn get_current_state() -> BTreeMap<String, ByteString>;
451 fn get_main_chain_scripts() -> Option<MainChainScriptsV1>;
453 fn get_pallet_version() -> u32;
455 }
456}