Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kpinter-iohk committed Jan 16, 2025
1 parent a6b6b92 commit 1cf5705
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 21 deletions.
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
34 changes: 28 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,34 @@ 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<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())
}))
}
}

pub async fn get_reserve_data<
T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
>(
genesis_utxo: UtxoId,
Expand Down
134 changes: 127 additions & 7 deletions toolkit/offchain/src/reserve/update_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,127 @@
//! * 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::{
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 mut reserve_settings = reserve
.get_reserve_settings(&ctx, client)
.await?
.expect("reserve settings 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(),
&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"),
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_script_cost: &ExUnits,
ctx: &TransactionContext,
) -> Result<Transaction, JsError> {
let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?);
Expand All @@ -50,7 +160,7 @@ 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);
Expand Down Expand Up @@ -82,12 +192,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 +264,8 @@ mod tests {
&(&parameters).into(),
&reserve,
&test_governance_data(),
governance_script_cost(),
redeemer_script_cost(),
&governance_script_cost(),
&redeemer_script_cost(),
&test_transaction_context(),
)
.unwrap();
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
56 changes: 54 additions & 2 deletions toolkit/offchain/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ use ogmios_client::{
};
use partner_chains_cardano_offchain::{
await_tx::{AwaitTx, FixedDelayRetries},
csl::TransactionContext,
d_param, init_governance, permissioned_candidates,
register::Register,
reserve, scripts_data, update_governance,
};
use partner_chains_plutus_data::reserve::ReserveMutableSettings;
use sidechain_domain::{
AdaBasedStaking, AssetId, AssetName, AuraPublicKey, CandidateRegistration, DParameter,
GrandpaPublicKey, MainchainAddressHash, MainchainPrivateKey, MainchainPublicKey,
Expand Down Expand Up @@ -63,6 +65,13 @@ const REWARDS_TOKEN_ASSET_NAME_STR: &str = "52657761726420746f6b656e";
const INITIAL_DEPOSIT_AMOUNT: u64 = 500000;
const DEPOSIT_AMOUNT: u64 = 100000;

const UPDATED_TOTAL_ACCRUED_FUNCTION_SCRIPT_HASH: PolicyId = PolicyId([234u8; 28]);
const UPDATED_INITIAL_INCENTIVE: u64 = 101;
const UPDATED_MUTABLE_SETTINGS: ReserveMutableSettings = ReserveMutableSettings {
total_accrued_function_script_hash: UPDATED_TOTAL_ACCRUED_FUNCTION_SCRIPT_HASH,
initial_incentive: UPDATED_INITIAL_INCENTIVE,
};

#[tokio::test]
async fn governance_flow() {
let image = GenericImage::new(TEST_IMAGE, TEST_IMAGE_TAG);
Expand Down Expand Up @@ -119,6 +128,8 @@ async fn reserve_management_scenario() {
assert_reserve_deposited(genesis_utxo, INITIAL_DEPOSIT_AMOUNT, &client).await;
run_deposit_to_reserve(genesis_utxo, &client).await;
assert_reserve_deposited(genesis_utxo, INITIAL_DEPOSIT_AMOUNT + DEPOSIT_AMOUNT, &client).await;
let _ = run_update_reserve_settings_management(genesis_utxo, &client).await;
assert_mutable_settings_eq(genesis_utxo, UPDATED_MUTABLE_SETTINGS, &client).await;
}

#[tokio::test]
Expand Down Expand Up @@ -162,6 +173,7 @@ async fn await_ogmios(ogmios_port: u16) -> Result<OgmiosClients, String> {
/// * "dave" address: addr_test1vphpcf32drhhznv6rqmrmgpuwq06kug0lkg22ux777rtlqst2er0r
/// * "eve" address: addr_test1vzzt5pwz3pum9xdgxalxyy52m3aqur0n43pcl727l37ggscl8h7v8
/// * addr_test1vzuasm5nqzh7n909f7wang7apjprpg29l2f9sk6shlt84rqep6nyc - has attached V-function script
///
/// Its hash is 0xf8fbe7316561e57de9ecd1c86ee8f8b512a314ba86499ba9a584bfa8fe2edc8d
async fn initial_transaction<T: Transactions + QueryUtxoByUtxoId>(
client: &T,
Expand Down Expand Up @@ -314,12 +326,30 @@ async fn run_create_reserve_management<
.unwrap()
}

async fn run_update_reserve_settings_management<
T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
>(
genesis_utxo: UtxoId,
client: &T,
) -> McTxHash {
reserve::update_settings::update_reserve_settings(
genesis_utxo,
GOVERNANCE_AUTHORITY_PAYMENT_KEY.0,
UPDATED_TOTAL_ACCRUED_FUNCTION_SCRIPT_HASH,
UPDATED_INITIAL_INCENTIVE,
client,
&FixedDelayRetries::new(Duration::from_millis(500), 100),
)
.await
.unwrap()
}

async fn run_deposit_to_reserve<
T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
>(
genesis_utxo: UtxoId,
client: &T,
) -> () {
) {
reserve::deposit::deposit_to_reserve(
reserve::deposit::TokenAmount {
token: AssetId {
Expand Down Expand Up @@ -383,7 +413,7 @@ async fn assert_token_amount_eq<T: QueryLedgerState>(
}),
"Expected to find UTXO with {} of {}.{} at {}",
expected_amount,
hex::encode(&token_policy_id.0),
hex::encode(token_policy_id.0),
hex::encode(&token_asset_name.0),
address,
);
Expand All @@ -404,3 +434,25 @@ async fn assert_reserve_deposited<T: QueryLedgerState>(
)
.await;
}

async fn assert_mutable_settings_eq<T: QueryLedgerState + ogmios_client::OgmiosClient>(
genesis_utxo: UtxoId,
updated_mutable_settings: ReserveMutableSettings,
client: &T,
) {
let ctx = TransactionContext::for_payment_key(GOVERNANCE_AUTHORITY_PAYMENT_KEY.0, client)
.await
.unwrap();
let reserve_data = crate::reserve::get_reserve_data(genesis_utxo, &ctx, client).await.unwrap();
let mutable_settings = reserve_data
.get_reserve_settings(&ctx, client)
.await
.unwrap()
.unwrap()
.mutable_settings;
assert_eq!(
mutable_settings.total_accrued_function_script_hash,
updated_mutable_settings.total_accrued_function_script_hash
);
assert_eq!(mutable_settings.initial_incentive, updated_mutable_settings.initial_incentive);
}

0 comments on commit 1cf5705

Please sign in to comment.