From 139b55e846d406e98b34e8974c38d615f62d5b6b Mon Sep 17 00:00:00 2001 From: sourabhxyz Date: Thu, 1 Aug 2024 11:49:31 +0530 Subject: [PATCH] feat: (wip) add support of Maestro to compute for execution units Related to #109. --- .../src/tx_builder.rs | 14 +++ .../src/tx_builder_utils.rs | 114 ++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/src/core/libs/cardano_multiplatform_lib/src/tx_builder.rs b/src/core/libs/cardano_multiplatform_lib/src/tx_builder.rs index f077ac7d..b8679fc4 100644 --- a/src/core/libs/cardano_multiplatform_lib/src/tx_builder.rs +++ b/src/core/libs/cardano_multiplatform_lib/src/tx_builder.rs @@ -311,6 +311,7 @@ pub struct TransactionBuilderConfig { max_collateral_inputs: u32, // protocol parameter slot_config: (BigNum, BigNum, u32), // (zero_time, zero_slot, slot_length) blockfrost: Blockfrost, + maestro: Maestro, } #[wasm_bindgen] @@ -329,6 +330,7 @@ pub struct TransactionBuilderConfigBuilder { max_collateral_inputs: Option, // protocol parameter slot_config: Option<(BigNum, BigNum, u32)>, // (zero_time, zero_slot, slot_length) blockfrost: Option, + maestro: Option, } #[wasm_bindgen] @@ -348,6 +350,7 @@ impl TransactionBuilderConfigBuilder { max_collateral_inputs: None, slot_config: None, blockfrost: None, + maestro: None, } } @@ -429,6 +432,12 @@ impl TransactionBuilderConfigBuilder { cfg } + pub fn maestro(&self, maestro: &Maestro) -> Self { + let mut cfg = self.clone(); + cfg.maestro = Some(maestro.clone()); + cfg + } + pub fn build(&self) -> Result { let cfg = self.clone(); Ok(TransactionBuilderConfig { @@ -479,6 +488,11 @@ impl TransactionBuilderConfigBuilder { } else { Blockfrost::new("".to_string(), "".to_string()) }, + maestro: if cfg.maestro.is_some() { + cfg.maestro.unwrap() + } else { + Maestro::new("".to_string(), "".to_string()) + }, }) } } diff --git a/src/core/libs/cardano_multiplatform_lib/src/tx_builder_utils.rs b/src/core/libs/cardano_multiplatform_lib/src/tx_builder_utils.rs index b6b3713c..e31187b8 100644 --- a/src/core/libs/cardano_multiplatform_lib/src/tx_builder_utils.rs +++ b/src/core/libs/cardano_multiplatform_lib/src/tx_builder_utils.rs @@ -59,6 +59,15 @@ pub struct Blockfrost { project_id: String, } +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct Maestro { + url: String, + api_key: String, +} + #[wasm_bindgen] impl Blockfrost { pub fn new(url: String, project_id: String) -> Self { @@ -75,6 +84,22 @@ impl Blockfrost { } } +#[wasm_bindgen] +impl Maestro { + pub fn new(url: String, api_key: String) -> Self { + Self { + url: url.clone(), + api_key: api_key.clone(), + } + } + pub fn url(&self) -> String { + self.url.clone() + } + pub fn api_key(&self) -> String { + self.api_key.clone() + } +} + #[wasm_bindgen] pub fn apply_params_to_plutus_script( params: &PlutusList, @@ -143,6 +168,14 @@ pub async fn get_ex_units_blockfrost( Ok(Redeemers::new()) } +#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] +pub async fn get_ex_units_maestro( + tx: Transaction, + ms: &Maestro, +) -> Result { + Ok(Redeemers::new()) +} + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] pub async fn get_ex_units_blockfrost( tx: Transaction, @@ -224,6 +257,87 @@ pub async fn get_ex_units_blockfrost( } } +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] +pub async fn get_ex_units_maestro( + tx: Transaction, + ms: &Maestro, +) -> Result { + if ms.url.is_empty() || ms.api_key.is_empty() { + return Err(JsError::from_str( + "Maestro not set. Can't calculate ex units", + )); + } + + let mut opts = RequestInit::new(); + opts.method("POST"); + let tx_hex = hex::encode(tx.to_bytes()); + opts.body(Some(&JsValue::from(tx_hex))); + + let url = &ms.url; + + let request = Request::new_with_str_and_init(&url, &opts)?; + request.headers().set("Content-Type", "application/cbor")?; + request.headers().set("api_key", &ms.api_key)?; + + let window = js_sys::global().unchecked_into::(); + let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; + + // `resp_value` is a `Response` object. + assert!(resp_value.is_instance_of::()); + let resp: Response = resp_value.dyn_into().unwrap(); + + // Convert this other `Promise` into a rust `Future`. + let json = JsFuture::from(resp.json()?).await?; + + // Use serde to parse the JSON into a struct. + let redeemer_result: RedeemerResult = json.into_serde().unwrap(); + + match redeemer_result.result { + Some(res) => { + if let Some(e) = &res.EvaluationFailure { + return Err(JsError::from_str( + &serde_json::to_string_pretty(&e).unwrap(), + )); + } + let mut redeemers: BTreeMap = BTreeMap::new(); + for (pointer, eu) in &res.EvaluationResult.unwrap() { + let r: Vec<&str> = pointer.split(":").collect(); + let tag = match r[0] { + "spend" => RedeemerTag::new_spend(), + "mint" => RedeemerTag::new_mint(), + "certificate" => RedeemerTag::new_cert(), + "withdrawal" => RedeemerTag::new_reward(), + _ => return Err(JsValue::NULL), + }; + let index = &to_bignum(r[1].parse::().unwrap()); + let ex_units = ExUnits::new(&to_bignum(eu.memory), &to_bignum(eu.steps)); + + for tx_redeemer in &tx.witness_set.redeemers.clone().unwrap().0 { + if tx_redeemer.tag() == tag && tx_redeemer.index() == *index { + let updated_redeemer = Redeemer::new( + &tx_redeemer.tag(), + &tx_redeemer.index(), + &tx_redeemer.data(), + &ex_units, + ); + redeemers.insert( + RedeemerWitnessKey::new( + &updated_redeemer.tag(), + &updated_redeemer.index(), + ), + updated_redeemer.clone(), + ); + } + } + } + + Ok(Redeemers(redeemers.values().cloned().collect())) + } + + None => Err(JsValue::NULL), + } +} + #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum ScriptWitnessKind {