Skip to content

Commit

Permalink
change: init governance transaction (#241)
Browse files Browse the repository at this point in the history
  • Loading branch information
AmbientTea authored Nov 18, 2024
1 parent a2d581a commit c2f41e5
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 2 deletions.
18 changes: 17 additions & 1 deletion toolkit/offchain/src/csl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(dead_code)]

use crate::plutus_script::PlutusScript;
use crate::{plutus_script::PlutusScript, untyped_plutus::datum_to_uplc_plutus_data};
use cardano_serialization_lib::*;
use ogmios_client::{
query_ledger_state::{PlutusCostModels, ProtocolParametersResponse},
Expand Down Expand Up @@ -146,6 +146,11 @@ pub(crate) trait OgmiosUtxoExt {
fn to_csl_tx_input(&self) -> TransactionInput;
fn to_csl_tx_output(&self) -> Result<TransactionOutput, JsError>;
fn to_csl(&self) -> Result<TransactionUnspentOutput, JsError>;

fn to_domain(&self) -> sidechain_domain::UtxoId;

/// Encodes this UTXO as a nested constructor data format which is used by PC smart contracts
fn to_uplc_plutus_data(&self) -> uplc::PlutusData;
}

impl OgmiosUtxoExt for OgmiosUtxo {
Expand All @@ -165,6 +170,17 @@ impl OgmiosUtxoExt for OgmiosUtxo {
fn to_csl(&self) -> Result<TransactionUnspentOutput, JsError> {
Ok(TransactionUnspentOutput::new(&self.to_csl_tx_input(), &self.to_csl_tx_output()?))
}

fn to_domain(&self) -> sidechain_domain::UtxoId {
sidechain_domain::UtxoId {
tx_hash: sidechain_domain::McTxHash(self.transaction.id),
index: sidechain_domain::UtxoIndex(self.index),
}
}

fn to_uplc_plutus_data(&self) -> uplc::PlutusData {
datum_to_uplc_plutus_data(&self.to_domain())
}
}

pub(crate) struct TransactionContext {
Expand Down
4 changes: 4 additions & 0 deletions toolkit/offchain/src/init_governance/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[cfg(test)]
mod tests;

pub(crate) mod transaction;
193 changes: 193 additions & 0 deletions toolkit/offchain/src/init_governance/tests.rs

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions toolkit/offchain/src/init_governance/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use crate::{csl::*, plutus_script::PlutusScript};
use cardano_serialization_lib::*;
use ogmios_client::types::OgmiosUtxo;
use partner_chains_plutus_data::version_oracle::VersionOracleDatum;
use sidechain_domain::MainchainAddressHash;

// Script ID of the governance script in the script cache.
// TODO: Use a proper value of raw_scripts::ScripId once we upgrade to a version that has it.
const SCRIPT_ID: u32 = 32;

#[allow(dead_code)]
pub(crate) fn init_governance_transaction(
multi_sig_policy: &[u8],
version_oracle_validator: &[u8],
version_oracle_policy: &[u8],
governance_authority: MainchainAddressHash,
tx_context: &TransactionContext,
genesis_utxo: OgmiosUtxo,
ex_units: ExUnits,
) -> anyhow::Result<Transaction> {
let multi_sig_policy =
PlutusScript::from_wrapped_cbor(multi_sig_policy, LanguageKind::PlutusV2)?
.apply_uplc_data(multisig_governance_policy_configuration(governance_authority))?;
let version_oracle_validator =
PlutusScript::from_wrapped_cbor(version_oracle_validator, LanguageKind::PlutusV2)?
.apply_uplc_data(genesis_utxo.to_uplc_plutus_data())?;
let version_oracle_policy =
PlutusScript::from_wrapped_cbor(version_oracle_policy, LanguageKind::PlutusV2)?
.apply_uplc_data(genesis_utxo.to_uplc_plutus_data())?
.apply_uplc_data(version_oracle_validator.address_data(tx_context.network)?)?;
let config = crate::csl::get_builder_config(&tx_context)?;
let mut tx_builder = TransactionBuilder::new(&config);

tx_builder.set_mint_builder(&{
let mut mint_builder = MintBuilder::new();

mint_builder.add_asset(
&mint_witness(&version_oracle_policy, &multi_sig_policy, &ex_units)?,
&version_oracle_asset_name(),
&Int::new_i32(1),
)?;
mint_builder
});

tx_builder.add_output(&version_oracle_datum_output(
version_oracle_validator.clone(),
version_oracle_policy.clone(),
multi_sig_policy,
tx_context.network,
&tx_context,
)?)?;

tx_builder.set_inputs(&{
let mut tx_input_builder = TxInputsBuilder::new();
tx_input_builder.add_key_input(
&tx_context.payment_key_hash(),
&genesis_utxo.to_csl_tx_input(),
&Value::new(&Coin::from(genesis_utxo.value.lovelace)),
);

tx_input_builder
});

Ok(tx_builder.balance_update_and_build(&tx_context)?)
}

fn version_oracle_asset_name() -> AssetName {
AssetName::new(b"Version oracle".to_vec()).expect("Constant asset name should work")
}

fn mint_witness(
version_oracle_policy: &PlutusScript,
multi_sig_policy: &PlutusScript,
ex_units: &ExUnits,
) -> anyhow::Result<MintWitness> {
Ok(MintWitness::new_plutus_script(
&PlutusScriptSource::new(&version_oracle_policy.to_csl()),
&Redeemer::new(
&RedeemerTag::new_mint(),
&0u32.into(),
&PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(&0u64.into(), &{
let mut list = PlutusList::new();
list.add(&PlutusData::new_integer(&SCRIPT_ID.into()));
list.add(&PlutusData::new_bytes(multi_sig_policy.script_hash().to_vec()));
list
})),
&ex_units,
),
))
}

fn version_oracle_datum_output(
version_oracle_validator: PlutusScript,
version_oracle_policy: PlutusScript,
multi_sig_policy: PlutusScript,
network: NetworkIdKind,
tx_context: &TransactionContext,
) -> anyhow::Result<cardano_serialization_lib::TransactionOutput> {
let datum: PlutusData = VersionOracleDatum {
version_oracle: SCRIPT_ID,
currency_symbol: version_oracle_policy.policy_id().0.into(),
}
.into();

let amount_builder = TransactionOutputBuilder::new()
.with_address(&version_oracle_validator.address(network))
.with_plutus_data(&datum)
.with_script_ref(&ScriptRef::new_plutus_script(&multi_sig_policy.to_csl()))
.next()?;
let mut ma = MultiAsset::new();
let mut assets = Assets::new();
assets.insert(&version_oracle_asset_name(), &1u64.into());
ma.insert(&version_oracle_policy.policy_id().0.into(), &assets);
let output = amount_builder.with_coin_and_asset(&0u64.into(), &ma).build()?;
let min_ada = MinOutputAdaCalculator::new(
&output,
&DataCost::new_coins_per_byte(
&tx_context.protocol_parameters.min_utxo_deposit_coefficient.into(),
),
)
.calculate_ada()?;
let output = amount_builder.with_coin_and_asset(&min_ada, &ma).build()?;
Ok(output)
}

// Returns the simplest MultiSig policy configuration plutus data:
// there is one required authority and it is the governance authority from sidechain params.
pub fn multisig_governance_policy_configuration(
governance_authority: MainchainAddressHash,
) -> uplc::PlutusData {
uplc::PlutusData::Array(vec![
uplc::PlutusData::Array(vec![uplc::PlutusData::BoundedBytes(
governance_authority.0.to_vec().into(),
)]),
uplc::PlutusData::BigInt(uplc::BigInt::Int(1.into())),
])
}
2 changes: 2 additions & 0 deletions toolkit/offchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub mod collateral_selection;
pub mod csl;
/// Supports D-Parameter upsert
pub mod d_param;
/// Supports governance initialization
pub mod init_governance;
/// Supports Permissioned Candidates upsert
pub mod permissioned_candidates;
/// Utilities for handling Plutus script data
Expand Down
1 change: 1 addition & 0 deletions toolkit/offchain/src/plutus_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use uplc::ast::{DeBruijn, Program};
use crate::{csl::*, untyped_plutus::*};

/// Wraps a Plutus script cbor
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PlutusScript {
pub bytes: Vec<u8>,
pub language: LanguageKind,
Expand Down
9 changes: 8 additions & 1 deletion toolkit/utils/ogmios-client/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
use std::fmt::Debug;
use std::str::FromStr;

#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
Expand Down Expand Up @@ -163,12 +164,18 @@ impl TryFrom<serde_json::Value> for OgmiosValue {
}
}

#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
#[derive(Clone, Default, Deserialize, Eq, PartialEq)]
pub struct OgmiosTx {
#[serde(deserialize_with = "parse_bytes_array")]
pub id: [u8; 32],
}

impl Debug for OgmiosTx {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OgmiosTx").field("id", &hex::encode(&self.id)).finish()
}
}

pub(crate) fn parse_bytes<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
Expand Down

0 comments on commit c2f41e5

Please sign in to comment.