Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ETCM-9064 reserve update command #391

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions toolkit/offchain/src/csl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub(crate) fn get_builder_config(
.ref_script_coins_per_byte(&convert_reference_script_costs(
&protocol_parameters.min_fee_reference_scripts.clone(),
)?)
.deduplicate_explicit_ref_inputs_with_regular_inputs(true)
.build()
}

Expand Down Expand Up @@ -276,7 +277,7 @@ impl MainchainPrivateKeyExt for MainchainPrivateKey {
}
}

pub(crate) struct TransactionContext {
pub struct TransactionContext {
/// This key is added as required signer and used to sign the transaction.
pub(crate) payment_key: PrivateKey,
/// Used to pay for the transaction fees and uncovered transaction inputs
Expand All @@ -289,7 +290,7 @@ pub(crate) struct TransactionContext {
impl TransactionContext {
/// Gets `TransactionContext`, having UTXOs for the given payment key and the network configuration,
/// required to perform most of the partner-chains smart contract operations.
pub(crate) async fn for_payment_key<C: QueryLedgerState + QueryNetwork>(
pub async fn for_payment_key<C: QueryLedgerState + QueryNetwork>(
payment_signing_key: [u8; 32],
client: &C,
) -> Result<TransactionContext, anyhow::Error> {
Expand Down
35 changes: 29 additions & 6 deletions toolkit/offchain/src/reserve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

use crate::{csl::TransactionContext, scripts_data};
use anyhow::anyhow;
use cardano_serialization_lib::PlutusData;
use init::find_script_utxo;
use ogmios_client::{
query_ledger_state::{QueryLedgerState, QueryUtxoByUtxoId},
query_network::QueryNetwork,
transactions::Transactions,
types::OgmiosUtxo,
};
use partner_chains_plutus_data::reserve::ReserveDatum;
use sidechain_domain::UtxoId;

pub mod create;
Expand All @@ -17,14 +19,35 @@ pub mod init;
pub mod update_settings;

#[derive(Clone, Debug)]
pub(crate) struct ReserveData {
pub(crate) scripts: scripts_data::ReserveScripts,
pub(crate) auth_policy_version_utxo: OgmiosUtxo,
pub(crate) validator_version_utxo: OgmiosUtxo,
pub(crate) illiquid_circulation_supply_validator_version_utxo: OgmiosUtxo,
pub struct ReserveData {
pub scripts: scripts_data::ReserveScripts,
pub auth_policy_version_utxo: OgmiosUtxo,
pub validator_version_utxo: OgmiosUtxo,
pub illiquid_circulation_supply_validator_version_utxo: OgmiosUtxo,
}

pub(crate) async fn get_reserve_data<
impl ReserveData {
pub async fn get_reserve_settings<
T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
>(
&self,
ctx: &TransactionContext,
client: &T,
) -> Result<Option<(OgmiosUtxo, ReserveDatum)>, anyhow::Error> {
let validator_address = self.scripts.validator.address(ctx.network).to_bech32(None)?;
let validator_utxos = client.query_utxos(&[validator_address]).await?;

Ok(validator_utxos.into_iter().find_map(|utxo| {
utxo.clone()
.datum
.and_then(|d| PlutusData::from_bytes(d.bytes).ok())
.and_then(|d| ReserveDatum::try_from(d).ok())
.map(|d| (utxo, d))
}))
}
}

pub async fn get_reserve_data<
T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
>(
genesis_utxo: UtxoId,
Expand Down
149 changes: 138 additions & 11 deletions toolkit/offchain/src/reserve/update_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,131 @@
//! * Reserve Validator Version Utxo
//! * Governance Policy Script

use super::get_reserve_data;
use super::ReserveData;
use crate::{csl::*, init_governance::GovernanceData};
use crate::{
await_tx::AwaitTx, csl::*, init_governance::get_governance_data,
init_governance::GovernanceData,
};
use anyhow::anyhow;
use cardano_serialization_lib::*;
use ogmios_client::transactions::OgmiosEvaluateTransactionResponse;
use ogmios_client::types::OgmiosUtxo;
use ogmios_client::{
query_ledger_state::{QueryLedgerState, QueryUtxoByUtxoId},
query_network::QueryNetwork,
transactions::Transactions,
};
use partner_chains_plutus_data::reserve::{ReserveDatum, ReserveRedeemer};
use sidechain_domain::{McTxHash, ScriptHash, UtxoId};
use std::collections::HashMap;

pub async fn update_reserve_settings<
T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
A: AwaitTx,
>(
genesis_utxo: UtxoId,
payment_key: [u8; 32],
total_accrued_function_script_hash: ScriptHash,
reserve_initial_incentive_amount: u64,
client: &T,
await_tx: &A,
) -> anyhow::Result<McTxHash> {
let ctx = TransactionContext::for_payment_key(payment_key, client).await?;
let governance = get_governance_data(genesis_utxo, client).await?;
let reserve = get_reserve_data(genesis_utxo, &ctx, client).await?;
let (reserve_utxo, mut reserve_settings) = reserve
.get_reserve_settings(&ctx, client)
.await?
.expect("reserve utxo must exist");

reserve_settings.mutable_settings.total_accrued_function_script_hash =
total_accrued_function_script_hash;
reserve_settings.mutable_settings.initial_incentive = reserve_initial_incentive_amount;

let tx_to_evaluate = update_reserve_settings_tx(
&reserve_settings,
&reserve,
&governance,
&zero_ex_units(),
&reserve_utxo,
&zero_ex_units(),
&ctx,
)?;

let evaluate_response = client.evaluate_transaction(&tx_to_evaluate.to_bytes()).await?;

let costs = match_costs(
&tx_to_evaluate,
&[reserve.scripts.validator.csl_script_hash(), governance.policy_script_hash()],
evaluate_response,
)?;

let tx = update_reserve_settings_tx(
&reserve_settings,
&reserve,
&governance,
costs
.get(&governance.policy_script_hash())
.expect("governance policy has cost calculated"),
&reserve_utxo,
costs
.get(&reserve.scripts.validator.csl_script_hash())
.expect("reserve validator has cost calculated"),
&ctx,
)?;
let signed_tx = ctx.sign(&tx).to_bytes();
let res = client.submit_transaction(&signed_tx).await.map_err(|e| {
anyhow::anyhow!(
"Update Reserve Settings transaction request failed: {}, tx bytes: {}",
e,
hex::encode(signed_tx)
)
})?;
let tx_id = res.transaction.id;
log::info!("Update Reserve Settings transaction submitted: {}", hex::encode(tx_id));
await_tx.await_tx_output(client, UtxoId::new(tx_id, 0)).await?;
Ok(McTxHash(tx_id))
}

fn match_costs(
evaluated_transaction: &Transaction,
script_hashes: &[cardano_serialization_lib::ScriptHash],
evaluate_response: Vec<OgmiosEvaluateTransactionResponse>,
) -> Result<HashMap<cardano_serialization_lib::ScriptHash, ExUnits>, anyhow::Error> {
let mint_keys = evaluated_transaction
.body()
.mint()
.expect("Transaction should have mints")
.keys();
let mut script_to_index: HashMap<cardano_serialization_lib::ScriptHash, usize> = HashMap::new();
for i in 0..mint_keys.len() {
script_to_index.insert(mint_keys.get(i), i);
}
let mint_ex_units = get_validator_budgets(evaluate_response).mint_ex_units;
if mint_ex_units.len() == script_hashes.len() {
let mut all_ex_units = HashMap::new();
for sh in script_hashes {
let script_idx = *script_to_index.get(sh).unwrap_or_else(|| {
panic!("Token {} should be present in transaction mints", sh.to_hex())
});
let ex_units =
mint_ex_units.get(script_idx).expect("mint_ex_units has script hash").clone();
all_ex_units.insert(sh.clone(), ex_units);
}
Ok(all_ex_units)
} else {
Err(anyhow!("Could not build transaction to submit, evaluate response has wrong number of mint keys."))
}
}

fn update_reserve_settings_tx(
datum: &ReserveDatum,
reserve: &ReserveData,
governance: &GovernanceData,
governance_script_cost: ExUnits,
redeemer_script_cost: ExUnits,
governance_script_cost: &ExUnits,
reserve_utxo: &OgmiosUtxo,
reserve_script_cost: &ExUnits,
ctx: &TransactionContext,
) -> Result<Transaction, JsError> {
let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?);
Expand All @@ -35,9 +149,6 @@ fn update_reserve_settings_tx(
{
let mut inputs = TxInputsBuilder::new();

let utxo = &reserve.validator_version_utxo;
let input = utxo.to_csl_tx_input();
let amount = &utxo.value.to_csl()?;
let witness = PlutusWitness::new_with_ref_without_datum(
&PlutusScriptSource::new_ref_input(
&reserve.scripts.validator.csl_script_hash(),
Expand All @@ -50,10 +161,11 @@ fn update_reserve_settings_tx(
// CSL will set redeemer index for the index of script input after sorting transaction inputs
&0u32.into(),
&ReserveRedeemer::UpdateReserve { governance_version: 1u64 }.into(),
&redeemer_script_cost,
reserve_script_cost,
),
);
inputs.add_plutus_script_input(&witness, &input, amount);
let amount = reserve_utxo.value.to_csl()?;
inputs.add_plutus_script_input(&witness, &reserve_utxo.to_csl_tx_input(), &amount);

tx_builder.set_inputs(&inputs);
}
Expand Down Expand Up @@ -82,12 +194,22 @@ fn update_reserve_settings_tx(
tx_builder.add_mint_one_script_token_using_reference_script(
&governance.policy_script,
&gov_tx_input,
&governance_script_cost,
governance_script_cost,
)?;
tx_builder.add_script_reference_input(
&reserve.illiquid_circulation_supply_validator_version_utxo.to_csl_tx_input(),
reserve.scripts.illiquid_circulation_supply_validator.bytes.len(),
);

tx_builder.add_script_reference_input(
&reserve.auth_policy_version_utxo.to_csl_tx_input(),
reserve.scripts.auth_policy.bytes.len(),
);
tx_builder.add_script_reference_input(
&reserve.validator_version_utxo.to_csl_tx_input(),
reserve.scripts.validator.bytes.len(),
);

tx_builder.add_required_signer(&ctx.payment_key_hash());

tx_builder.balance_update_and_build(ctx)
Expand Down Expand Up @@ -144,8 +266,9 @@ mod tests {
&(&parameters).into(),
&reserve,
&test_governance_data(),
governance_script_cost(),
redeemer_script_cost(),
&governance_script_cost(),
&reserve_tx_input(),
&redeemer_script_cost(),
&test_transaction_context(),
)
.unwrap();
Expand Down Expand Up @@ -219,6 +342,10 @@ mod tests {
GovernanceData { policy_script: test_governance_script(), utxo: test_governance_input() }
}

fn reserve_tx_input() -> OgmiosUtxo {
OgmiosUtxo { transaction: OgmiosTx { id: [20; 32] }, index: 0, ..Default::default() }
}

fn governance_script_cost() -> ExUnits {
ExUnits::new(&100u64.into(), &200u64.into())
}
Expand Down
8 changes: 4 additions & 4 deletions toolkit/offchain/src/scripts_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,10 @@ pub(crate) fn registered_candidates_scripts(
}

#[derive(Clone, Debug)]
pub(crate) struct ReserveScripts {
pub(crate) validator: PlutusScript,
pub(crate) auth_policy: PlutusScript,
pub(crate) illiquid_circulation_supply_validator: PlutusScript,
pub struct ReserveScripts {
pub validator: PlutusScript,
pub auth_policy: PlutusScript,
pub illiquid_circulation_supply_validator: PlutusScript,
}

pub(crate) fn reserve_scripts(
Expand Down
Loading
Loading