partner_chains_cardano_offchain/d_param/
mod.rs1use crate::await_tx::AwaitTx;
8use crate::cardano_keys::CardanoPaymentSigningKey;
9use crate::csl::{
10 CostStore, Costs, InputsBuilderExt, NetworkTypeExt, TransactionBuilderExt, TransactionContext,
11 TransactionExt, empty_asset_name, get_builder_config, unit_plutus_data,
12};
13use crate::governance::GovernanceData;
14use crate::multisig::submit_or_create_tx_to_sign;
15use crate::multisig::{
16 MultiSigSmartContractResult, MultiSigSmartContractResult::TransactionSubmitted,
17};
18use crate::plutus_script::PlutusScript;
19use crate::scripts_data::PlutusScriptData;
20use anyhow::anyhow;
21use cardano_serialization_lib::{PlutusData, Transaction, TransactionBuilder, TxInputsBuilder};
22use ogmios_client::query_ledger_state::QueryUtxoByUtxoId;
23use ogmios_client::{
24 query_ledger_state::QueryLedgerState, query_network::QueryNetwork, transactions::Transactions,
25 types::OgmiosUtxo,
26};
27use partner_chains_plutus_data::d_param::{DParamDatum, d_parameter_to_plutus_data};
28use sidechain_domain::{DParameter, UtxoId};
29
30#[cfg(test)]
31mod tests;
32
33pub async fn upsert_d_param<
40 C: QueryLedgerState + QueryNetwork + Transactions + QueryUtxoByUtxoId,
41 A: AwaitTx,
42>(
43 genesis_utxo: UtxoId,
44 d_parameter: &DParameter,
45 payment_signing_key: &CardanoPaymentSigningKey,
46 client: &C,
47 await_tx: &A,
48) -> anyhow::Result<Option<MultiSigSmartContractResult>> {
49 let ctx = TransactionContext::for_payment_key(payment_signing_key, client).await?;
50 let scripts = crate::scripts_data::d_parameter_scripts(genesis_utxo, ctx.network)?;
51 let validator_utxos = client.query_utxos(&[scripts.validator_address.clone()]).await?;
52
53 let tx_hash_opt = match get_current_d_parameter(validator_utxos, &scripts)? {
54 Some((_, current_d_param)) if current_d_param == *d_parameter => {
55 log::info!("Current D-parameter value is equal to the one to be set.");
56 None
57 },
58 Some((current_utxo, _)) => {
59 log::info!("Current D-parameter is different to the one to be set. Updating.");
60 Some(
61 update_d_param(
62 &scripts.validator,
63 &scripts.policy,
64 d_parameter,
65 ¤t_utxo,
66 ctx,
67 genesis_utxo,
68 client,
69 await_tx,
70 )
71 .await?,
72 )
73 },
74 None => {
75 log::info!("There is no D-parameter set. Inserting new one.");
76 Some(
77 insert_d_param(
78 &scripts.validator,
79 &scripts.policy,
80 d_parameter,
81 ctx,
82 genesis_utxo,
83 client,
84 await_tx,
85 )
86 .await?,
87 )
88 },
89 };
90 if let Some(TransactionSubmitted(tx_hash)) = tx_hash_opt {
91 await_tx.await_tx_output(client, tx_hash).await?;
92 }
93 Ok(tx_hash_opt)
94}
95
96fn get_current_d_parameter(
97 validator_utxos: Vec<OgmiosUtxo>,
98 scripts: &PlutusScriptData,
99) -> Result<Option<(OgmiosUtxo, DParameter)>, anyhow::Error> {
100 let utxos_with_d_param_token: Vec<OgmiosUtxo> = validator_utxos
101 .into_iter()
102 .filter(|utxo| utxo.value.native_tokens.get(&scripts.policy_id().0).is_some())
103 .collect();
104
105 if utxos_with_d_param_token.len() > 1 {
106 return Err(anyhow!("Multiple UTXOs with D-parameter token found"));
107 }
108
109 if let Some(utxo) = utxos_with_d_param_token.first() {
110 let datum = utxo.datum.clone().ok_or_else(|| {
111 anyhow!("Invalid state: an UTXO at the validator script address does not have a datum")
112 })?;
113 let datum_plutus_data = PlutusData::from_bytes(datum.bytes).map_err(|e| {
114 anyhow!("Internal error: could not decode datum of D-parameter validator script: {}", e)
115 })?;
116 let current_d_param: DParameter = DParamDatum::try_from(datum_plutus_data)
117 .map_err(|e| {
118 anyhow!(
119 "Internal error: could not decode datum of D-parameter validator script: {}",
120 e
121 )
122 })?
123 .into();
124 Ok(Some((utxo.clone(), current_d_param)))
125 } else {
126 Ok(None)
127 }
128}
129
130async fn insert_d_param<
131 C: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
132 A: AwaitTx,
133>(
134 validator: &PlutusScript,
135 policy: &PlutusScript,
136 d_parameter: &DParameter,
137 ctx: TransactionContext,
138 genesis_utxo: UtxoId,
139 client: &C,
140 await_tx: &A,
141) -> anyhow::Result<MultiSigSmartContractResult> {
142 let gov_data = GovernanceData::get(genesis_utxo, client).await?;
143
144 submit_or_create_tx_to_sign(
145 &gov_data,
146 ctx,
147 |costs, ctx| mint_d_param_token_tx(validator, policy, d_parameter, &gov_data, costs, &ctx),
148 "Insert D-parameter",
149 client,
150 await_tx,
151 )
152 .await
153}
154
155async fn update_d_param<
156 C: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
157 A: AwaitTx,
158>(
159 validator: &PlutusScript,
160 policy: &PlutusScript,
161 d_parameter: &DParameter,
162 current_utxo: &OgmiosUtxo,
163 ctx: TransactionContext,
164 genesis_utxo: UtxoId,
165 client: &C,
166 await_tx: &A,
167) -> anyhow::Result<MultiSigSmartContractResult> {
168 let governance_data = GovernanceData::get(genesis_utxo, client).await?;
169
170 submit_or_create_tx_to_sign(
171 &governance_data,
172 ctx,
173 |costs, ctx| {
174 update_d_param_tx(
175 validator,
176 policy,
177 d_parameter,
178 current_utxo,
179 &governance_data,
180 costs,
181 &ctx,
182 )
183 },
184 "Update D-parameter",
185 client,
186 await_tx,
187 )
188 .await
189}
190
191fn mint_d_param_token_tx(
192 validator: &PlutusScript,
193 policy: &PlutusScript,
194 d_parameter: &DParameter,
195 governance_data: &GovernanceData,
196 costs: Costs,
197 ctx: &TransactionContext,
198) -> anyhow::Result<Transaction> {
199 let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?);
200 tx_builder.add_mint_one_script_token(
202 policy,
203 &empty_asset_name(),
204 &unit_plutus_data(),
205 &costs.get_mint(&policy.clone()),
206 )?;
207 tx_builder.add_output_with_one_script_token(
208 validator,
209 policy,
210 &d_parameter_to_plutus_data(d_parameter),
211 ctx,
212 )?;
213
214 let gov_tx_input = governance_data.utxo_id_as_tx_input();
215 tx_builder.add_mint_one_script_token_using_reference_script(
216 &governance_data.policy.script(),
217 &gov_tx_input,
218 &costs,
219 )?;
220
221 Ok(tx_builder.balance_update_and_build(ctx)?.remove_native_script_witnesses())
222}
223
224fn update_d_param_tx(
225 validator: &PlutusScript,
226 policy: &PlutusScript,
227 d_parameter: &DParameter,
228 script_utxo: &OgmiosUtxo,
229 governance_data: &GovernanceData,
230 costs: Costs,
231 ctx: &TransactionContext,
232) -> anyhow::Result<Transaction> {
233 let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?);
234
235 let mut inputs = TxInputsBuilder::new();
236 inputs.add_script_utxo_input(
237 script_utxo,
238 validator,
239 &unit_plutus_data(),
240 &costs.get_one_spend(),
241 )?;
242 tx_builder.set_inputs(&inputs);
243
244 tx_builder.add_output_with_one_script_token(
245 validator,
246 policy,
247 &d_parameter_to_plutus_data(d_parameter),
248 ctx,
249 )?;
250
251 let gov_tx_input = governance_data.utxo_id_as_tx_input();
252 tx_builder.add_mint_one_script_token_using_reference_script(
253 &governance_data.policy.script(),
254 &gov_tx_input,
255 &costs,
256 )?;
257
258 Ok(tx_builder.balance_update_and_build(ctx)?.remove_native_script_witnesses())
259}
260
261pub async fn get_d_param<C: QueryLedgerState + QueryNetwork>(
263 genesis_utxo: UtxoId,
264 client: &C,
265) -> anyhow::Result<Option<DParameter>> {
266 let network = client.shelley_genesis_configuration().await?.network.to_csl();
267 let scripts = crate::scripts_data::d_parameter_scripts(genesis_utxo, network)?;
268 let validator_utxos = client.query_utxos(&[scripts.validator_address.clone()]).await?;
269 Ok(get_current_d_parameter(validator_utxos, &scripts)?.map(|(_, d_parameter)| d_parameter))
270}