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