Skip to content

Commit

Permalink
ETCM-9222 generic ExUnit calculation in reserve modules (#417)
Browse files Browse the repository at this point in the history
  • Loading branch information
kpinter-iohk authored Jan 23, 2025
1 parent ecec82b commit 2799222
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 313 deletions.
11 changes: 9 additions & 2 deletions toolkit/offchain/src/csl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,12 +256,19 @@ impl CostStore for Costs {
}

impl Costs {
pub fn new(
mints: HashMap<cardano_serialization_lib::ScriptHash, ExUnits>,
spends: HashMap<u32, ExUnits>,
) -> Costs {
Costs::Costs(CostLookup { mints, spends })
}

pub async fn calculate_costs<T: Transactions, F>(
make_tx: F,
client: &T,
) -> anyhow::Result<Transaction>
where
F: Fn(Costs) -> Result<Transaction, JsError>,
F: Fn(Costs) -> anyhow::Result<Transaction>,
{
let tx = make_tx(Costs::ZeroCosts)?;
// stage 1
Expand All @@ -271,7 +278,7 @@ impl Costs {
// stage 2
let costs = Self::from_ogmios(&tx, client).await?;

Ok(make_tx(costs)?)
make_tx(costs)
}

async fn from_ogmios<T: Transactions>(tx: &Transaction, client: &T) -> anyhow::Result<Costs> {
Expand Down
83 changes: 14 additions & 69 deletions toolkit/offchain/src/reserve/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,25 @@ use super::ReserveData;
use crate::{
await_tx::AwaitTx,
csl::{
get_builder_config, get_validator_budgets, zero_ex_units, MultiAssetExt, OgmiosUtxoExt,
TransactionBuilderExt, TransactionContext, TransactionOutputAmountBuilderExt,
get_builder_config, CostStore, Costs, MultiAssetExt, OgmiosUtxoExt, TransactionBuilderExt,
TransactionContext, TransactionOutputAmountBuilderExt,
},
init_governance::{get_governance_data, GovernanceData},
scripts_data::ReserveScripts,
};
use anyhow::anyhow;
use cardano_serialization_lib::{
ExUnits, JsError, MultiAsset, Transaction, TransactionBuilder, TransactionOutput,
JsError, MultiAsset, Transaction, TransactionBuilder, TransactionOutput,
TransactionOutputBuilder,
};
use ogmios_client::{
query_ledger_state::{QueryLedgerState, QueryUtxoByUtxoId},
query_network::QueryNetwork,
transactions::{OgmiosEvaluateTransactionResponse, Transactions},
transactions::Transactions,
};
use partner_chains_plutus_data::reserve::{
ReserveDatum, ReserveImmutableSettings, ReserveMutableSettings, ReserveStats,
};
use sidechain_domain::{AssetId, McTxHash, PolicyId, UtxoId};
use std::collections::HashMap;

pub async fn create_reserve_utxo<
T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
Expand All @@ -55,31 +53,12 @@ pub async fn create_reserve_utxo<
let governance = get_governance_data(genesis_utxo, client).await?;
let reserve = ReserveData::get(genesis_utxo, &ctx, client).await?;

let tx_to_evaluate = create_reserve_tx(
&parameters,
&reserve,
&governance,
zero_ex_units(),
zero_ex_units(),
&ctx,
)?;
let evaluate_response = client.evaluate_transaction(&tx_to_evaluate.to_bytes()).await?;

let (reserve_auth_ex_units, governance_ex_units) = match_costs(
&tx_to_evaluate,
&reserve.scripts.auth_policy.csl_script_hash(),
&governance.policy_script_hash(),
evaluate_response,
)?;
let tx = Costs::calculate_costs(
|costs| create_reserve_tx(&parameters, &reserve, &governance, costs, &ctx),
client,
)
.await?;

let tx = create_reserve_tx(
&parameters,
&reserve,
&governance,
governance_ex_units,
reserve_auth_ex_units,
&ctx,
)?;
let signed_tx = ctx.sign(&tx).to_bytes();
let res = client.submit_transaction(&signed_tx).await.map_err(|e| {
anyhow::anyhow!(
Expand Down Expand Up @@ -121,63 +100,29 @@ fn create_reserve_tx(
parameters: &ReserveParameters,
reserve: &ReserveData,
governance: &GovernanceData,
governance_script_cost: ExUnits,
reserve_auth_script_cost: ExUnits,
costs: Costs,
ctx: &TransactionContext,
) -> Result<Transaction, JsError> {
) -> anyhow::Result<Transaction> {
let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?);

tx_builder.add_mint_one_script_token_using_reference_script(
&reserve.scripts.auth_policy,
&reserve.auth_policy_version_utxo.to_csl_tx_input(),
&reserve_auth_script_cost,
&costs.get_mint(&reserve.scripts.auth_policy),
)?;
tx_builder.add_output(&reserve_validator_output(parameters, &reserve.scripts, ctx)?)?;

let gov_tx_input = governance.utxo_id_as_tx_input();
tx_builder.add_mint_one_script_token_using_reference_script(
&governance.policy_script,
&gov_tx_input,
&governance_script_cost,
&costs.get_mint(&governance.policy_script),
)?;
tx_builder.add_script_reference_input(
&reserve.validator_version_utxo.to_csl_tx_input(),
reserve.scripts.validator.bytes.len(),
);
tx_builder.balance_update_and_build(ctx)
}

fn match_costs(
evaluated_transaction: &Transaction,
reserve_auth_policy: &cardano_serialization_lib::ScriptHash,
governance_policy: &cardano_serialization_lib::ScriptHash,
evaluate_response: Vec<OgmiosEvaluateTransactionResponse>,
) -> Result<(ExUnits, ExUnits), anyhow::Error> {
let mint_keys = evaluated_transaction
.body()
.mint()
.expect("Every Create Reserve transaction should have two mints")
.keys();
let script_to_index: HashMap<cardano_serialization_lib::ScriptHash, usize> =
vec![(mint_keys.get(0), 0), (mint_keys.get(1), 1)].into_iter().collect();
let mint_ex_units = get_validator_budgets(evaluate_response).mint_ex_units;
if mint_ex_units.len() == 2 {
let reserve_auth_policy_idx = *script_to_index
.get(reserve_auth_policy)
.expect("Reserve Auth Policy Token is present in transaction mints");
let reserve_auth_ex_units = mint_ex_units
.get(reserve_auth_policy_idx)
.expect("mint_ex_units have two items")
.clone();
let gov_policy_idx = *script_to_index
.get(governance_policy)
.expect("Governance Policy Token is present in transaction mints");
let governance_ex_units =
mint_ex_units.get(gov_policy_idx).expect("mint_ex_units have two items").clone();
Ok((reserve_auth_ex_units, governance_ex_units))
} else {
Err(anyhow!("Could not build transaction to submit, evaluate response has wrong number of mint keys."))
}
Ok(tx_builder.balance_update_and_build(ctx)?)
}

// Creates output with reserve token and the initial deposit
Expand Down
75 changes: 17 additions & 58 deletions toolkit/offchain/src/reserve/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,20 @@ use super::{reserve_utxo_input_with_validator_script_reference, ReserveData, Tok
use crate::{
await_tx::AwaitTx,
csl::{
get_builder_config, get_validator_budgets, zero_ex_units, MultiAssetExt, OgmiosUtxoExt,
TransactionBuilderExt, TransactionContext, TransactionOutputAmountBuilderExt,
get_builder_config, CostStore, Costs, MultiAssetExt, OgmiosUtxoExt, TransactionBuilderExt,
TransactionContext, TransactionOutputAmountBuilderExt,
},
init_governance::{get_governance_data, GovernanceData},
scripts_data::ReserveScripts,
};
use anyhow::anyhow;
use cardano_serialization_lib::{
ExUnits, JsError, MultiAsset, Transaction, TransactionBuilder, TransactionOutput,
JsError, MultiAsset, Transaction, TransactionBuilder, TransactionOutput,
TransactionOutputBuilder,
};
use ogmios_client::{
query_ledger_state::{QueryLedgerState, QueryUtxoByUtxoId},
query_network::QueryNetwork,
transactions::{OgmiosEvaluateTransactionResponse, Transactions},
transactions::Transactions,
types::OgmiosUtxo,
};
use partner_chains_plutus_data::reserve::ReserveRedeemer;
Expand All @@ -58,29 +57,12 @@ pub async fn deposit_to_reserve<
let token_amount =
TokenAmount { token: parameters.token, amount: current_amount + parameters.amount };

let tx_to_evaluate = deposit_to_reserve_tx(
&token_amount,
&utxo,
&reserve,
&governance,
zero_ex_units(),
zero_ex_units(),
&ctx,
)?;
let evaluate_response = client.evaluate_transaction(&tx_to_evaluate.to_bytes()).await?;

let spend_ex_units = get_spend_cost(evaluate_response.clone())?;
let governance_ex_units = get_governance_script_cost(evaluate_response)?;
let tx = Costs::calculate_costs(
|costs| deposit_to_reserve_tx(&token_amount, &utxo, &reserve, &governance, costs, &ctx),
client,
)
.await?;

let tx = deposit_to_reserve_tx(
&token_amount,
&utxo,
&reserve,
&governance,
governance_ex_units,
spend_ex_units,
&ctx,
)?;
let signed_tx = ctx.sign(&tx).to_bytes();
let res = client.submit_transaction(&signed_tx).await.map_err(|e| {
anyhow::anyhow!(
Expand Down Expand Up @@ -112,25 +94,24 @@ fn deposit_to_reserve_tx(
current_utxo: &OgmiosUtxo,
reserve: &ReserveData,
governance: &GovernanceData,
governance_script_cost: ExUnits,
spend_reserve_auth_token_cost: ExUnits,
costs: Costs,
ctx: &TransactionContext,
) -> Result<Transaction, JsError> {
) -> anyhow::Result<Transaction> {
let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?);

tx_builder.add_output(&validator_output(parameters, current_utxo, &reserve.scripts, ctx)?)?;

tx_builder.set_inputs(&reserve_utxo_input_with_validator_script_reference(
&current_utxo,
&reserve,
ReserveRedeemer::DepositToReserve { governance_version: 1 },
&spend_reserve_auth_token_cost,
current_utxo,
reserve,
ReserveRedeemer::DepositToReserve,
&costs.get_one_spend(),
)?);

tx_builder.add_mint_one_script_token_using_reference_script(
&governance.policy_script,
&governance.utxo_id_as_tx_input(),
&governance_script_cost,
&costs.get_mint(&governance.policy_script),
)?;

tx_builder.add_script_reference_input(
Expand All @@ -141,29 +122,7 @@ fn deposit_to_reserve_tx(
&reserve.illiquid_circulation_supply_validator_version_utxo.to_csl_tx_input(),
reserve.scripts.illiquid_circulation_supply_validator.bytes.len(),
);
tx_builder.balance_update_and_build(ctx)
}

// governance token is the only minted token
fn get_governance_script_cost(
response: Vec<OgmiosEvaluateTransactionResponse>,
) -> Result<ExUnits, anyhow::Error> {
Ok(get_validator_budgets(response)
.mint_ex_units
.first()
.ok_or_else(|| anyhow!("Mint cost is missing in evaluate response"))?
.clone())
}

// Auth policy token is the only spent token is the transaction
fn get_spend_cost(
response: Vec<OgmiosEvaluateTransactionResponse>,
) -> Result<ExUnits, anyhow::Error> {
Ok(get_validator_budgets(response)
.spend_ex_units
.first()
.ok_or_else(|| anyhow!("Spend cost is missing in evaluate response"))?
.clone())
Ok(tx_builder.balance_update_and_build(ctx)?)
}

// Creates output with reserve token and updated deposit
Expand Down
6 changes: 3 additions & 3 deletions toolkit/offchain/src/reserve/handover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fn build_tx(
governance: &GovernanceData,
costs: Costs,
ctx: &TransactionContext,
) -> Result<Transaction, JsError> {
) -> anyhow::Result<Transaction> {
let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?);

let reserve_auth_policy_spend_cost = costs.get_one_spend();
Expand All @@ -109,7 +109,7 @@ fn build_tx(
tx_builder.set_inputs(&reserve_utxo_input_with_validator_script_reference(
reserve_utxo,
reserve,
ReserveRedeemer::Handover { governance_version: 1 },
ReserveRedeemer::Handover,
&reserve_auth_policy_spend_cost,
)?);

Expand All @@ -130,7 +130,7 @@ fn build_tx(
&reserve.illiquid_circulation_supply_validator_version_utxo.to_csl_tx_input(),
reserve.scripts.illiquid_circulation_supply_validator.bytes.len(),
);
tx_builder.balance_update_and_build(ctx)
Ok(tx_builder.balance_update_and_build(ctx)?)
}

// Creates output with reserve token and updated deposit
Expand Down
Loading

0 comments on commit 2799222

Please sign in to comment.