partner_chains_dolos_data_sources/
governed_map.rs1use crate::{
2 Result,
3 client::{MiniBFClient, api::MiniBFApi},
4};
5use async_trait::async_trait;
6use cardano_serialization_lib::PlutusData;
7use partner_chains_plutus_data::governed_map::GovernedMapDatum;
8use sidechain_domain::byte_string::ByteString;
9use sidechain_domain::*;
10use sp_governed_map::{GovernedMapDataSource, MainChainScriptsV1};
11use std::collections::BTreeMap;
12
13pub struct GovernedMapDataSourceImpl {
14 client: MiniBFClient,
15}
16
17impl GovernedMapDataSourceImpl {
18 pub fn new(client: MiniBFClient) -> Self {
19 Self { client }
20 }
21}
22
23#[async_trait]
24impl GovernedMapDataSource for GovernedMapDataSourceImpl {
25 async fn get_state_at_block(
26 &self,
27 mc_block: McBlockHash,
28 main_chain_scripts: MainChainScriptsV1,
29 ) -> Result<BTreeMap<String, ByteString>> {
30 let block = self.client.blocks_by_id(mc_block.clone()).await?;
32 let block_number =
33 McBlockNumber(block.height.unwrap_or_default().try_into().unwrap_or(0u32));
34
35 let utxos = self
37 .client
38 .addresses_utxos(main_chain_scripts.validator_address.clone())
39 .await
40 .unwrap_or_default();
41
42 let asset_unit = format_asset_unit(&main_chain_scripts.asset_policy_id);
46 let mut mappings = BTreeMap::new();
47
48 for utxo in utxos {
49 let tx_hash = match McTxHash::decode_hex(&utxo.tx_hash) {
51 Ok(hash) => hash,
52 Err(e) => {
53 log::warn!("Failed to decode tx_hash '{}': {}", utxo.tx_hash, e);
54 continue;
55 },
56 };
57 let tx = self.client.transaction_by_hash(tx_hash).await?;
58 let utxo_block_height = tx.block_height as u32;
59
60 if utxo_block_height > block_number.0 {
61 continue;
62 }
63
64 let has_asset = utxo.amount.iter().any(|a| a.unit == asset_unit);
66 if !has_asset {
67 continue;
68 }
69
70 if let Some(datum_hex) = &utxo.inline_datum {
72 if let Some((key, value)) = parse_governed_map_datum(datum_hex) {
73 mappings.insert(key, value);
74 }
75 }
76 }
77
78 Ok(mappings)
79 }
80
81 async fn get_mapping_changes(
82 &self,
83 since_mc_block: Option<McBlockHash>,
84 up_to_mc_block: McBlockHash,
85 scripts: MainChainScriptsV1,
86 ) -> Result<Vec<(String, Option<ByteString>)>> {
87 let current_mappings = self.get_state_at_block(up_to_mc_block, scripts.clone()).await?;
89
90 let Some(since_mc_block) = since_mc_block else {
92 let changes =
93 current_mappings.into_iter().map(|(key, value)| (key, Some(value))).collect();
94 return Ok(changes);
95 };
96
97 let previous_mappings = self.get_state_at_block(since_mc_block, scripts).await?;
99
100 let mut changes = Vec::new();
102
103 for (key, value) in current_mappings.iter() {
105 if previous_mappings.get(key) != Some(value) {
106 changes.push((key.clone(), Some(value.clone())));
107 }
108 }
109
110 for key in previous_mappings.keys() {
112 if !current_mappings.contains_key(key) {
113 changes.push((key.clone(), None));
114 }
115 }
116
117 Ok(changes)
118 }
119}
120
121fn format_asset_unit(policy_id: &PolicyId) -> String {
122 policy_id.to_hex_string()[2..].to_string()
125}
126
127fn parse_governed_map_datum(datum_hex: &str) -> Option<(String, ByteString)> {
129 match PlutusData::from_hex(datum_hex) {
130 Ok(plutus_data) => match GovernedMapDatum::try_from(plutus_data) {
131 Ok(GovernedMapDatum { key, value }) => Some((key, value)),
132 Err(err) => {
133 log::warn!("Failed to parse GovernedMapDatum: {}", err);
134 None
135 },
136 },
137 Err(err) => {
138 log::warn!("Failed to parse PlutusData from hex: {}", err);
139 None
140 },
141 }
142}