From 1224706e8f9b72abb5c52a89d138cc0770f40118 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Fri, 1 Mar 2024 07:15:03 -0800 Subject: [PATCH 1/5] Blockfrost crates Rust and WASM crates for creating tx builders from blockfrost. The rust crate can be directly used with the `blockfrost` crate when the feature `direct_api` is enabled. If it is not then various parsing ability is still exposed for cost models. The WASM crate due to technical restrictions just exposes an endpoint that takes in the response JSON and creates the config. the cml/wasm crate exports this new `cml-blockfrost-wasm` functionality which only increased compiled size from 896kb -> 911kb so should be fine to include by default. TODO: some documentation but this would conflict with #307's documentation update so can be done at a later date. --- Cargo.toml | 2 + blockfrost/rust/Cargo.toml | 20 + blockfrost/rust/src/direct_api.rs | 93 ++++ blockfrost/rust/src/lib.rs | 101 ++++ blockfrost/wasm/Cargo.toml | 15 + blockfrost/wasm/src/lib.rs | 756 ++++++++++++++++++++++++++ chain/rust/src/builders/tx_builder.rs | 1 + chain/rust/src/plutus/utils.rs | 13 + chain/rust/src/utils.rs | 16 +- chain/wasm/src/utils.rs | 13 +- cml/wasm/Cargo.toml | 1 + cml/wasm/src/lib.rs | 1 + 12 files changed, 1030 insertions(+), 2 deletions(-) create mode 100644 blockfrost/rust/Cargo.toml create mode 100644 blockfrost/rust/src/direct_api.rs create mode 100644 blockfrost/rust/src/lib.rs create mode 100644 blockfrost/wasm/Cargo.toml create mode 100644 blockfrost/wasm/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 023a16b3..4317b659 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,8 @@ # this is for the new crate structure. The legacy code (current CML) still resides in the `rust` directory. members = [ + "blockfrost/rust", + "blockfrost/wasm", "chain/rust", "chain/wasm", "chain/wasm/json-gen", diff --git a/blockfrost/rust/Cargo.toml b/blockfrost/rust/Cargo.toml new file mode 100644 index 00000000..8a9f998d --- /dev/null +++ b/blockfrost/rust/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "cml-blockfrost" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cml-chain = { path = "../../chain/rust", version = "5.1.0" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.57" +thiserror = "1.0.37" + +# only used for direct_api +blockfrost = { version = "1.0.1", optional = true } + +[features] +# This allows directly hooking into the blockfrost-rust crate +direct_api = ["blockfrost"] diff --git a/blockfrost/rust/src/direct_api.rs b/blockfrost/rust/src/direct_api.rs new file mode 100644 index 00000000..294c223b --- /dev/null +++ b/blockfrost/rust/src/direct_api.rs @@ -0,0 +1,93 @@ +use blockfrost::{BlockfrostAPI, BlockfrostError}; +use cml_chain::{ + builders::tx_builder::{TransactionBuilderConfigBuilder, TxBuilderConfigField}, + fees::LinearFee, + plutus::ExUnitPrices, + Coin, SubCoin, +}; +use std::str::FromStr; + +use crate::{parse_cost_models, BlockfrostParamsParseError}; + +#[derive(Debug, thiserror::Error)] +pub enum BlockfrostTxBuilderConfigError { + #[error("Parsing: {0}")] + Parsing(#[from] BlockfrostParamsParseError), + #[error("Blockfrost: {0}")] + Blockfrost(#[from] BlockfrostError), +} + +/** + * Completely automated config creation via supplied blockfrost API. + * Both calls blockfrost via the passed API to get enough information + * and also parses the information to make a TransactionBuilderConfigBuilder + * with all necessary protocol parameter information set + */ +pub async fn make_tx_builder_cfg( + api: &BlockfrostAPI, +) -> Result { + let params = api.epochs_latest_parameters().await?; + let coins_per_utxo_byte = params + .coins_per_utxo_word + .ok_or(BlockfrostParamsParseError::MissingField( + TxBuilderConfigField::CoinsPerUtxoBytes, + )) + .and_then(|c| { + Coin::from_str(&c).map_err(|_| { + BlockfrostParamsParseError::IncorrectFormat(TxBuilderConfigField::CoinsPerUtxoBytes) + }) + })?; + let pool_deposit = Coin::from_str(¶ms.pool_deposit).map_err(|_| { + BlockfrostParamsParseError::IncorrectFormat(TxBuilderConfigField::PoolDeposit) + })?; + let key_deposit = Coin::from_str(¶ms.key_deposit).map_err(|_| { + BlockfrostParamsParseError::IncorrectFormat(TxBuilderConfigField::KeyDeposit) + })?; + let max_value_size = params + .max_val_size + .ok_or(BlockfrostParamsParseError::MissingField( + TxBuilderConfigField::MaxValueSize, + )) + .and_then(|c| { + u32::from_str(&c).map_err(|_| { + BlockfrostParamsParseError::IncorrectFormat(TxBuilderConfigField::MaxValueSize) + }) + })?; + let ex_unit_prices = match (params.price_mem, params.price_step) { + (Some(price_mem), Some(price_step)) => Ok(ExUnitPrices::new( + SubCoin::from_base10_f32(price_mem), + SubCoin::from_base10_f32(price_step), + )), + _ => Err(BlockfrostParamsParseError::MissingField( + TxBuilderConfigField::ExUnitPrices, + )), + }?; + let fee_algo = LinearFee::new(params.min_fee_a as u64, params.min_fee_b as u64); + let max_tx_size = params.max_tx_size as u32; + let collateral_percentage = + params + .collateral_percent + .ok_or(BlockfrostParamsParseError::MissingField( + TxBuilderConfigField::CollateralPercentage, + ))? as u32; + let max_collateral_inputs = + params + .max_collateral_inputs + .ok_or(BlockfrostParamsParseError::MissingField( + TxBuilderConfigField::MaxCollateralInputs, + ))? as u32; + let mut config_builder = TransactionBuilderConfigBuilder::new() + .fee_algo(fee_algo) + .coins_per_utxo_byte(coins_per_utxo_byte) + .pool_deposit(pool_deposit) + .key_deposit(key_deposit) + .max_value_size(max_value_size) + .max_tx_size(max_tx_size) + .ex_unit_prices(ex_unit_prices) + .collateral_percentage(collateral_percentage) + .max_collateral_inputs(max_collateral_inputs); + if let Some(cost_models) = params.cost_models { + config_builder = config_builder.cost_models(parse_cost_models(&cost_models)?); + } + Ok(config_builder) +} diff --git a/blockfrost/rust/src/lib.rs b/blockfrost/rust/src/lib.rs new file mode 100644 index 00000000..fac079f9 --- /dev/null +++ b/blockfrost/rust/src/lib.rs @@ -0,0 +1,101 @@ +use cml_chain::{ + builders::tx_builder::TxBuilderConfigField, + plutus::{CostModels, Language}, + Int, +}; +use std::collections::HashMap; + +#[cfg(feature = "direct_api")] +pub mod direct_api; + +#[derive(Debug, thiserror::Error)] +pub enum CostModelsError { + #[error("Invalid object format: {0:?}")] + InvalidFormat(serde_json::Value), + #[error("Invalid cost format: {0:?}")] + InvalidCost(Option), + #[error("Invalid op count: {0}")] + OpCount(usize), +} + +#[derive(Debug, thiserror::Error)] +pub enum BlockfrostParamsParseError { + #[error("Missing field: {0:?}")] + MissingField(TxBuilderConfigField), + #[error("Incorrect format in field: {0:?}")] + IncorrectFormat(TxBuilderConfigField), + #[error("Invalid cost models {0:?} for language {1:?}")] + CostModels(CostModelsError, Language), +} + +pub fn parse_cost_model( + serde_value: &serde_json::Value, + language: Language, +) -> Result, CostModelsError> { + if let serde_json::Value::Object(cost_obj) = serde_value { + let mut costs = vec![]; + // bad idea to assume it's ordered - depends on feature enabled + // and could possibly change + let mut keys: Vec = cost_obj.keys().cloned().collect(); + if keys.len() != CostModels::op_count(language) { + return Err(CostModelsError::OpCount(keys.len())); + } + keys.sort(); + for key in keys { + let cost = cost_obj + .get(&key) + .and_then(|c| c.as_i64()) + .ok_or_else(|| CostModelsError::InvalidCost(cost_obj.get(&key).cloned()))?; + costs.push(Int::from(cost)); + } + Ok(costs) + } else { + Err(CostModelsError::InvalidFormat(serde_value.clone())) + } +} + +pub fn parse_sancho_cost_model( + serde_value: &serde_json::Value, +) -> Result, CostModelsError> { + if let serde_json::Value::Object(cost_obj) = serde_value { + let mut costs = vec![]; + for i in 0..CostModels::PLUTUS_V3_COUNT { + let cost = cost_obj + .get(&i.to_string()) + .and_then(|val| val.as_i64()) + .ok_or_else(|| { + CostModelsError::InvalidCost(cost_obj.get(&i.to_string()).cloned()) + })?; + costs.push(cml_chain::Int::from(cost)); + } + Ok(costs) + } else { + Err(CostModelsError::InvalidFormat(serde_value.clone())) + } +} + +pub fn parse_cost_models( + costs: &HashMap, +) -> Result { + let mut cost_models = CostModels::new(); + if let Some(plutus_v1) = costs.get("PlutusV1") { + cost_models.plutus_v1 = Some( + parse_cost_model(plutus_v1, Language::PlutusV1) + .map_err(|e| BlockfrostParamsParseError::CostModels(e, Language::PlutusV1))?, + ); + } + if let Some(plutus_v2) = costs.get("PlutusV2") { + cost_models.plutus_v2 = Some( + parse_cost_model(plutus_v2, Language::PlutusV2) + .map_err(|e| BlockfrostParamsParseError::CostModels(e, Language::PlutusV2))?, + ); + } + // Sancho testnet has a very different format for some reason + if let Some(plutus_v3) = costs.get("PlutusV3") { + cost_models.plutus_v3 = Some( + parse_sancho_cost_model(plutus_v3) + .map_err(|e| BlockfrostParamsParseError::CostModels(e, Language::PlutusV3))?, + ); + } + Ok(cost_models) +} diff --git a/blockfrost/wasm/Cargo.toml b/blockfrost/wasm/Cargo.toml new file mode 100644 index 00000000..52c0d083 --- /dev/null +++ b/blockfrost/wasm/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "cml-blockfrost-wasm" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cml-blockfrost = { path = "../rust", version = "0.1.0", direct_api = false } +cml-chain = { path = "../../chain/rust", version = "5.1.0" } +cml-chain-wasm = { path = "../../chain/wasm", version = "5.1.0" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.57" +wasm-bindgen = { version = "0.2", features=["serde-serialize"] } \ No newline at end of file diff --git a/blockfrost/wasm/src/lib.rs b/blockfrost/wasm/src/lib.rs new file mode 100644 index 00000000..0ea66dee --- /dev/null +++ b/blockfrost/wasm/src/lib.rs @@ -0,0 +1,756 @@ +use cml_blockfrost::{parse_cost_models, BlockfrostParamsParseError}; +use cml_chain::{ + builders::tx_builder::{TransactionBuilderConfigBuilder, TxBuilderConfigField}, + fees::LinearFee, + plutus::ExUnitPrices, + Coin, SubCoin, +}; +use std::str::FromStr; +use wasm_bindgen::prelude::{wasm_bindgen, JsError}; + +fn parse_string_coin_param( + params: &serde_json::Value, + key: &str, + field: TxBuilderConfigField, +) -> Result { + params + .get(key) + .ok_or(BlockfrostParamsParseError::MissingField(field)) + .and_then(|v| { + match v { + serde_json::Value::String(c) => Coin::from_str(&c).ok(), + _ => None, + } + .ok_or(BlockfrostParamsParseError::IncorrectFormat(field)) + }) +} + +fn parse_u64_param( + params: &serde_json::Value, + key: &str, + field: TxBuilderConfigField, +) -> Result { + params + .get(key) + .ok_or(BlockfrostParamsParseError::MissingField(field)) + .and_then(|v| { + v.as_u64() + .ok_or(BlockfrostParamsParseError::IncorrectFormat(field)) + }) +} + +fn make_tx_builder_cfg_impl( + params: serde_json::Value, +) -> Result { + let coins_per_utxo_byte = parse_string_coin_param( + ¶ms, + "coins_per_utxo_word", + TxBuilderConfigField::CoinsPerUtxoBytes, + )?; + let pool_deposit = + parse_string_coin_param(¶ms, "pool_deposit", TxBuilderConfigField::PoolDeposit)?; + let key_deposit = + parse_string_coin_param(¶ms, "key_deposit", TxBuilderConfigField::KeyDeposit)?; + let max_value_size = + parse_string_coin_param(¶ms, "max_val_size", TxBuilderConfigField::MaxValueSize)? + as u32; + let ex_unit_prices = match (params.get("price_mem"), params.get("price_step")) { + (Some(price_mem), Some(price_step)) => match (price_mem.as_f64(), price_step.as_f64()) { + (Some(price_mem), Some(price_step)) => Ok(ExUnitPrices::new( + SubCoin::from_base10_f32(price_mem as f32), + SubCoin::from_base10_f32(price_step as f32), + )), + _ => Err(BlockfrostParamsParseError::IncorrectFormat( + TxBuilderConfigField::ExUnitPrices, + )), + }, + _ => Err(BlockfrostParamsParseError::MissingField( + TxBuilderConfigField::ExUnitPrices, + )), + }?; + let fee_algo = match (params.get("min_fee_a"), params.get("min_fee_b")) { + (Some(min_fee_a), Some(min_fee_b)) => match (min_fee_a.as_u64(), min_fee_b.as_u64()) { + (Some(min_fee_a), Some(min_fee_b)) => Ok(LinearFee::new(min_fee_a, min_fee_b)), + _ => Err(BlockfrostParamsParseError::IncorrectFormat( + TxBuilderConfigField::ExUnitPrices, + )), + }, + _ => Err(BlockfrostParamsParseError::MissingField( + TxBuilderConfigField::ExUnitPrices, + )), + }?; + let max_tx_size = + parse_u64_param(¶ms, "max_tx_size", TxBuilderConfigField::MaxTxSize)? as u32; + let collateral_percentage = parse_u64_param( + ¶ms, + "collateral_percent", + TxBuilderConfigField::CollateralPercentage, + )? as u32; + let max_collateral_inputs = parse_u64_param( + ¶ms, + "max_collateral_inputs", + TxBuilderConfigField::MaxCollateralInputs, + )? as u32; + let mut config_builder = TransactionBuilderConfigBuilder::new() + .fee_algo(fee_algo) + .coins_per_utxo_byte(coins_per_utxo_byte) + .pool_deposit(pool_deposit) + .key_deposit(key_deposit) + .max_value_size(max_value_size) + .max_tx_size(max_tx_size) + .ex_unit_prices(ex_unit_prices) + .collateral_percentage(collateral_percentage) + .max_collateral_inputs(max_collateral_inputs); + if let Some(cost_models_value) = params.get("cost_models") { + if let serde_json::Value::Object(cost_models) = cost_models_value { + let cost_models_hashmap = cost_models + .into_iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + config_builder = config_builder.cost_models(parse_cost_models(&cost_models_hashmap)?); + } else { + return Err(BlockfrostParamsParseError::IncorrectFormat( + TxBuilderConfigField::CostModels, + )); + } + } + Ok(config_builder.into()) +} + +/** + * Parses Blockfrost's `epochs/latest/parameters` response JSON to make a TransactionBuilderConfigBuilder + * + * This simplifies `TransactionBuilder` creation if using blockfrost for protocol parameters + */ +#[wasm_bindgen] +pub fn blockfrost_make_tx_builder_cfg( + response_json: &str, +) -> Result { + let params: serde_json::Value = + serde_json::from_str(response_json).map_err(|e| JsError::new(&e.to_string()))?; + make_tx_builder_cfg_impl(params) + .map(Into::into) + .map_err(Into::into) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tx_builder_config_from_json() { + let response_json = "{ + \"epoch\": 225, + \"min_fee_a\": 44, + \"min_fee_b\": 155381, + \"max_block_size\": 65536, + \"max_tx_size\": 16384, + \"max_block_header_size\": 1100, + \"key_deposit\": \"2000000\", + \"pool_deposit\": \"500000000\", + \"e_max\": 18, + \"n_opt\": 150, + \"a0\": 0.3, + \"rho\": 0.003, + \"tau\": 0.2, + \"decentralisation_param\": 0.5, + \"extra_entropy\": null, + \"protocol_major_ver\": 2, + \"protocol_minor_ver\": 0, + \"min_utxo\": \"1000000\", + \"min_pool_cost\": \"340000000\", + \"nonce\": \"1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81\", + \"cost_models\": { + \"PlutusV2\": { + \"addInteger-cpu-arguments-intercept\": 205665, + \"addInteger-cpu-arguments-slope\": 812, + \"addInteger-memory-arguments-intercept\": 1, + \"addInteger-memory-arguments-slope\": 1, + \"appendByteString-cpu-arguments-intercept\": 1000, + \"appendByteString-cpu-arguments-slope\": 571, + \"appendByteString-memory-arguments-intercept\": 0, + \"appendByteString-memory-arguments-slope\": 1, + \"appendString-cpu-arguments-intercept\": 1000, + \"appendString-cpu-arguments-slope\": 24177, + \"appendString-memory-arguments-intercept\": 4, + \"appendString-memory-arguments-slope\": 1, + \"bData-cpu-arguments\": 1000, + \"bData-memory-arguments\": 32, + \"blake2b_256-cpu-arguments-intercept\": 117366, + \"blake2b_256-cpu-arguments-slope\": 10475, + \"blake2b_256-memory-arguments\": 4, + \"cekApplyCost-exBudgetCPU\": 23000, + \"cekApplyCost-exBudgetMemory\": 100, + \"cekBuiltinCost-exBudgetCPU\": 23000, + \"cekBuiltinCost-exBudgetMemory\": 100, + \"cekConstCost-exBudgetCPU\": 23000, + \"cekConstCost-exBudgetMemory\": 100, + \"cekDelayCost-exBudgetCPU\": 23000, + \"cekDelayCost-exBudgetMemory\": 100, + \"cekForceCost-exBudgetCPU\": 23000, + \"cekForceCost-exBudgetMemory\": 100, + \"cekLamCost-exBudgetCPU\": 23000, + \"cekLamCost-exBudgetMemory\": 100, + \"cekStartupCost-exBudgetCPU\": 100, + \"cekStartupCost-exBudgetMemory\": 100, + \"cekVarCost-exBudgetCPU\": 23000, + \"cekVarCost-exBudgetMemory\": 100, + \"chooseData-cpu-arguments\": 19537, + \"chooseData-memory-arguments\": 32, + \"chooseList-cpu-arguments\": 175354, + \"chooseList-memory-arguments\": 32, + \"chooseUnit-cpu-arguments\": 46417, + \"chooseUnit-memory-arguments\": 4, + \"consByteString-cpu-arguments-intercept\": 221973, + \"consByteString-cpu-arguments-slope\": 511, + \"consByteString-memory-arguments-intercept\": 0, + \"consByteString-memory-arguments-slope\": 1, + \"constrData-cpu-arguments\": 89141, + \"constrData-memory-arguments\": 32, + \"decodeUtf8-cpu-arguments-intercept\": 497525, + \"decodeUtf8-cpu-arguments-slope\": 14068, + \"decodeUtf8-memory-arguments-intercept\": 4, + \"decodeUtf8-memory-arguments-slope\": 2, + \"divideInteger-cpu-arguments-constant\": 196500, + \"divideInteger-cpu-arguments-model-arguments-intercept\": 453240, + \"divideInteger-cpu-arguments-model-arguments-slope\": 220, + \"divideInteger-memory-arguments-intercept\": 0, + \"divideInteger-memory-arguments-minimum\": 1, + \"divideInteger-memory-arguments-slope\": 1, + \"encodeUtf8-cpu-arguments-intercept\": 1000, + \"encodeUtf8-cpu-arguments-slope\": 28662, + \"encodeUtf8-memory-arguments-intercept\": 4, + \"encodeUtf8-memory-arguments-slope\": 2, + \"equalsByteString-cpu-arguments-constant\": 245000, + \"equalsByteString-cpu-arguments-intercept\": 216773, + \"equalsByteString-cpu-arguments-slope\": 62, + \"equalsByteString-memory-arguments\": 1, + \"equalsData-cpu-arguments-intercept\": 1060367, + \"equalsData-cpu-arguments-slope\": 12586, + \"equalsData-memory-arguments\": 1, + \"equalsInteger-cpu-arguments-intercept\": 208512, + \"equalsInteger-cpu-arguments-slope\": 421, + \"equalsInteger-memory-arguments\": 1, + \"equalsString-cpu-arguments-constant\": 187000, + \"equalsString-cpu-arguments-intercept\": 1000, + \"equalsString-cpu-arguments-slope\": 52998, + \"equalsString-memory-arguments\": 1, + \"fstPair-cpu-arguments\": 80436, + \"fstPair-memory-arguments\": 32, + \"headList-cpu-arguments\": 43249, + \"headList-memory-arguments\": 32, + \"iData-cpu-arguments\": 1000, + \"iData-memory-arguments\": 32, + \"ifThenElse-cpu-arguments\": 80556, + \"ifThenElse-memory-arguments\": 1, + \"indexByteString-cpu-arguments\": 57667, + \"indexByteString-memory-arguments\": 4, + \"lengthOfByteString-cpu-arguments\": 1000, + \"lengthOfByteString-memory-arguments\": 10, + \"lessThanByteString-cpu-arguments-intercept\": 197145, + \"lessThanByteString-cpu-arguments-slope\": 156, + \"lessThanByteString-memory-arguments\": 1, + \"lessThanEqualsByteString-cpu-arguments-intercept\": 197145, + \"lessThanEqualsByteString-cpu-arguments-slope\": 156, + \"lessThanEqualsByteString-memory-arguments\": 1, + \"lessThanEqualsInteger-cpu-arguments-intercept\": 204924, + \"lessThanEqualsInteger-cpu-arguments-slope\": 473, + \"lessThanEqualsInteger-memory-arguments\": 1, + \"lessThanInteger-cpu-arguments-intercept\": 208896, + \"lessThanInteger-cpu-arguments-slope\": 511, + \"lessThanInteger-memory-arguments\": 1, + \"listData-cpu-arguments\": 52467, + \"listData-memory-arguments\": 32, + \"mapData-cpu-arguments\": 64832, + \"mapData-memory-arguments\": 32, + \"mkCons-cpu-arguments\": 65493, + \"mkCons-memory-arguments\": 32, + \"mkNilData-cpu-arguments\": 22558, + \"mkNilData-memory-arguments\": 32, + \"mkNilPairData-cpu-arguments\": 16563, + \"mkNilPairData-memory-arguments\": 32, + \"mkPairData-cpu-arguments\": 76511, + \"mkPairData-memory-arguments\": 32, + \"modInteger-cpu-arguments-constant\": 196500, + \"modInteger-cpu-arguments-model-arguments-intercept\": 453240, + \"modInteger-cpu-arguments-model-arguments-slope\": 220, + \"modInteger-memory-arguments-intercept\": 0, + \"modInteger-memory-arguments-minimum\": 1, + \"modInteger-memory-arguments-slope\": 1, + \"multiplyInteger-cpu-arguments-intercept\": 69522, + \"multiplyInteger-cpu-arguments-slope\": 11687, + \"multiplyInteger-memory-arguments-intercept\": 0, + \"multiplyInteger-memory-arguments-slope\": 1, + \"nullList-cpu-arguments\": 60091, + \"nullList-memory-arguments\": 32, + \"quotientInteger-cpu-arguments-constant\": 196500, + \"quotientInteger-cpu-arguments-model-arguments-intercept\": 453240, + \"quotientInteger-cpu-arguments-model-arguments-slope\": 220, + \"quotientInteger-memory-arguments-intercept\": 0, + \"quotientInteger-memory-arguments-minimum\": 1, + \"quotientInteger-memory-arguments-slope\": 1, + \"remainderInteger-cpu-arguments-constant\": 196500, + \"remainderInteger-cpu-arguments-model-arguments-intercept\": 453240, + \"remainderInteger-cpu-arguments-model-arguments-slope\": 220, + \"remainderInteger-memory-arguments-intercept\": 0, + \"remainderInteger-memory-arguments-minimum\": 1, + \"remainderInteger-memory-arguments-slope\": 1, + \"serialiseData-cpu-arguments-intercept\": 1159724, + \"serialiseData-cpu-arguments-slope\": 392670, + \"serialiseData-memory-arguments-intercept\": 0, + \"serialiseData-memory-arguments-slope\": 2, + \"sha2_256-cpu-arguments-intercept\": 806990, + \"sha2_256-cpu-arguments-slope\": 30482, + \"sha2_256-memory-arguments\": 4, + \"sha3_256-cpu-arguments-intercept\": 1927926, + \"sha3_256-cpu-arguments-slope\": 82523, + \"sha3_256-memory-arguments\": 4, + \"sliceByteString-cpu-arguments-intercept\": 265318, + \"sliceByteString-cpu-arguments-slope\": 0, + \"sliceByteString-memory-arguments-intercept\": 4, + \"sliceByteString-memory-arguments-slope\": 0, + \"sndPair-cpu-arguments\": 85931, + \"sndPair-memory-arguments\": 32, + \"subtractInteger-cpu-arguments-intercept\": 205665, + \"subtractInteger-cpu-arguments-slope\": 812, + \"subtractInteger-memory-arguments-intercept\": 1, + \"subtractInteger-memory-arguments-slope\": 1, + \"tailList-cpu-arguments\": 41182, + \"tailList-memory-arguments\": 32, + \"trace-cpu-arguments\": 212342, + \"trace-memory-arguments\": 32, + \"unBData-cpu-arguments\": 31220, + \"unBData-memory-arguments\": 32, + \"unConstrData-cpu-arguments\": 32696, + \"unConstrData-memory-arguments\": 32, + \"unIData-cpu-arguments\": 43357, + \"unIData-memory-arguments\": 32, + \"unListData-cpu-arguments\": 32247, + \"unListData-memory-arguments\": 32, + \"unMapData-cpu-arguments\": 38314, + \"unMapData-memory-arguments\": 32, + \"verifyEcdsaSecp256k1Signature-cpu-arguments\": 35892428, + \"verifyEcdsaSecp256k1Signature-memory-arguments\": 10, + \"verifyEd25519Signature-cpu-arguments-intercept\": 57996947, + \"verifyEd25519Signature-cpu-arguments-slope\": 18975, + \"verifyEd25519Signature-memory-arguments\": 10, + \"verifySchnorrSecp256k1Signature-cpu-arguments-intercept\": 38887044, + \"verifySchnorrSecp256k1Signature-cpu-arguments-slope\": 32947, + \"verifySchnorrSecp256k1Signature-memory-arguments\": 10 + }, + \"PlutusV1\": { + \"addInteger-cpu-arguments-intercept\": 205665, + \"addInteger-cpu-arguments-slope\": 812, + \"addInteger-memory-arguments-intercept\": 1, + \"addInteger-memory-arguments-slope\": 1, + \"appendByteString-cpu-arguments-intercept\": 1000, + \"appendByteString-cpu-arguments-slope\": 571, + \"appendByteString-memory-arguments-intercept\": 0, + \"appendByteString-memory-arguments-slope\": 1, + \"appendString-cpu-arguments-intercept\": 1000, + \"appendString-cpu-arguments-slope\": 24177, + \"appendString-memory-arguments-intercept\": 4, + \"appendString-memory-arguments-slope\": 1, + \"bData-cpu-arguments\": 1000, + \"bData-memory-arguments\": 32, + \"blake2b_256-cpu-arguments-intercept\": 117366, + \"blake2b_256-cpu-arguments-slope\": 10475, + \"blake2b_256-memory-arguments\": 4, + \"cekApplyCost-exBudgetCPU\": 23000, + \"cekApplyCost-exBudgetMemory\": 100, + \"cekBuiltinCost-exBudgetCPU\": 23000, + \"cekBuiltinCost-exBudgetMemory\": 100, + \"cekConstCost-exBudgetCPU\": 23000, + \"cekConstCost-exBudgetMemory\": 100, + \"cekDelayCost-exBudgetCPU\": 23000, + \"cekDelayCost-exBudgetMemory\": 100, + \"cekForceCost-exBudgetCPU\": 23000, + \"cekForceCost-exBudgetMemory\": 100, + \"cekLamCost-exBudgetCPU\": 23000, + \"cekLamCost-exBudgetMemory\": 100, + \"cekStartupCost-exBudgetCPU\": 100, + \"cekStartupCost-exBudgetMemory\": 100, + \"cekVarCost-exBudgetCPU\": 23000, + \"cekVarCost-exBudgetMemory\": 100, + \"chooseData-cpu-arguments\": 19537, + \"chooseData-memory-arguments\": 32, + \"chooseList-cpu-arguments\": 175354, + \"chooseList-memory-arguments\": 32, + \"chooseUnit-cpu-arguments\": 46417, + \"chooseUnit-memory-arguments\": 4, + \"consByteString-cpu-arguments-intercept\": 221973, + \"consByteString-cpu-arguments-slope\": 511, + \"consByteString-memory-arguments-intercept\": 0, + \"consByteString-memory-arguments-slope\": 1, + \"constrData-cpu-arguments\": 89141, + \"constrData-memory-arguments\": 32, + \"decodeUtf8-cpu-arguments-intercept\": 497525, + \"decodeUtf8-cpu-arguments-slope\": 14068, + \"decodeUtf8-memory-arguments-intercept\": 4, + \"decodeUtf8-memory-arguments-slope\": 2, + \"divideInteger-cpu-arguments-constant\": 196500, + \"divideInteger-cpu-arguments-model-arguments-intercept\": 453240, + \"divideInteger-cpu-arguments-model-arguments-slope\": 220, + \"divideInteger-memory-arguments-intercept\": 0, + \"divideInteger-memory-arguments-minimum\": 1, + \"divideInteger-memory-arguments-slope\": 1, + \"encodeUtf8-cpu-arguments-intercept\": 1000, + \"encodeUtf8-cpu-arguments-slope\": 28662, + \"encodeUtf8-memory-arguments-intercept\": 4, + \"encodeUtf8-memory-arguments-slope\": 2, + \"equalsByteString-cpu-arguments-constant\": 245000, + \"equalsByteString-cpu-arguments-intercept\": 216773, + \"equalsByteString-cpu-arguments-slope\": 62, + \"equalsByteString-memory-arguments\": 1, + \"equalsData-cpu-arguments-intercept\": 1060367, + \"equalsData-cpu-arguments-slope\": 12586, + \"equalsData-memory-arguments\": 1, + \"equalsInteger-cpu-arguments-intercept\": 208512, + \"equalsInteger-cpu-arguments-slope\": 421, + \"equalsInteger-memory-arguments\": 1, + \"equalsString-cpu-arguments-constant\": 187000, + \"equalsString-cpu-arguments-intercept\": 1000, + \"equalsString-cpu-arguments-slope\": 52998, + \"equalsString-memory-arguments\": 1, + \"fstPair-cpu-arguments\": 80436, + \"fstPair-memory-arguments\": 32, + \"headList-cpu-arguments\": 43249, + \"headList-memory-arguments\": 32, + \"iData-cpu-arguments\": 1000, + \"iData-memory-arguments\": 32, + \"ifThenElse-cpu-arguments\": 80556, + \"ifThenElse-memory-arguments\": 1, + \"indexByteString-cpu-arguments\": 57667, + \"indexByteString-memory-arguments\": 4, + \"lengthOfByteString-cpu-arguments\": 1000, + \"lengthOfByteString-memory-arguments\": 10, + \"lessThanByteString-cpu-arguments-intercept\": 197145, + \"lessThanByteString-cpu-arguments-slope\": 156, + \"lessThanByteString-memory-arguments\": 1, + \"lessThanEqualsByteString-cpu-arguments-intercept\": 197145, + \"lessThanEqualsByteString-cpu-arguments-slope\": 156, + \"lessThanEqualsByteString-memory-arguments\": 1, + \"lessThanEqualsInteger-cpu-arguments-intercept\": 204924, + \"lessThanEqualsInteger-cpu-arguments-slope\": 473, + \"lessThanEqualsInteger-memory-arguments\": 1, + \"lessThanInteger-cpu-arguments-intercept\": 208896, + \"lessThanInteger-cpu-arguments-slope\": 511, + \"lessThanInteger-memory-arguments\": 1, + \"listData-cpu-arguments\": 52467, + \"listData-memory-arguments\": 32, + \"mapData-cpu-arguments\": 64832, + \"mapData-memory-arguments\": 32, + \"mkCons-cpu-arguments\": 65493, + \"mkCons-memory-arguments\": 32, + \"mkNilData-cpu-arguments\": 22558, + \"mkNilData-memory-arguments\": 32, + \"mkNilPairData-cpu-arguments\": 16563, + \"mkNilPairData-memory-arguments\": 32, + \"mkPairData-cpu-arguments\": 76511, + \"mkPairData-memory-arguments\": 32, + \"modInteger-cpu-arguments-constant\": 196500, + \"modInteger-cpu-arguments-model-arguments-intercept\": 453240, + \"modInteger-cpu-arguments-model-arguments-slope\": 220, + \"modInteger-memory-arguments-intercept\": 0, + \"modInteger-memory-arguments-minimum\": 1, + \"modInteger-memory-arguments-slope\": 1, + \"multiplyInteger-cpu-arguments-intercept\": 69522, + \"multiplyInteger-cpu-arguments-slope\": 11687, + \"multiplyInteger-memory-arguments-intercept\": 0, + \"multiplyInteger-memory-arguments-slope\": 1, + \"nullList-cpu-arguments\": 60091, + \"nullList-memory-arguments\": 32, + \"quotientInteger-cpu-arguments-constant\": 196500, + \"quotientInteger-cpu-arguments-model-arguments-intercept\": 453240, + \"quotientInteger-cpu-arguments-model-arguments-slope\": 220, + \"quotientInteger-memory-arguments-intercept\": 0, + \"quotientInteger-memory-arguments-minimum\": 1, + \"quotientInteger-memory-arguments-slope\": 1, + \"remainderInteger-cpu-arguments-constant\": 196500, + \"remainderInteger-cpu-arguments-model-arguments-intercept\": 453240, + \"remainderInteger-cpu-arguments-model-arguments-slope\": 220, + \"remainderInteger-memory-arguments-intercept\": 0, + \"remainderInteger-memory-arguments-minimum\": 1, + \"remainderInteger-memory-arguments-slope\": 1, + \"sha2_256-cpu-arguments-intercept\": 806990, + \"sha2_256-cpu-arguments-slope\": 30482, + \"sha2_256-memory-arguments\": 4, + \"sha3_256-cpu-arguments-intercept\": 1927926, + \"sha3_256-cpu-arguments-slope\": 82523, + \"sha3_256-memory-arguments\": 4, + \"sliceByteString-cpu-arguments-intercept\": 265318, + \"sliceByteString-cpu-arguments-slope\": 0, + \"sliceByteString-memory-arguments-intercept\": 4, + \"sliceByteString-memory-arguments-slope\": 0, + \"sndPair-cpu-arguments\": 85931, + \"sndPair-memory-arguments\": 32, + \"subtractInteger-cpu-arguments-intercept\": 205665, + \"subtractInteger-cpu-arguments-slope\": 812, + \"subtractInteger-memory-arguments-intercept\": 1, + \"subtractInteger-memory-arguments-slope\": 1, + \"tailList-cpu-arguments\": 41182, + \"tailList-memory-arguments\": 32, + \"trace-cpu-arguments\": 212342, + \"trace-memory-arguments\": 32, + \"unBData-cpu-arguments\": 31220, + \"unBData-memory-arguments\": 32, + \"unConstrData-cpu-arguments\": 32696, + \"unConstrData-memory-arguments\": 32, + \"unIData-cpu-arguments\": 43357, + \"unIData-memory-arguments\": 32, + \"unListData-cpu-arguments\": 32247, + \"unListData-memory-arguments\": 32, + \"unMapData-cpu-arguments\": 38314, + \"unMapData-memory-arguments\": 32, + \"verifyEd25519Signature-cpu-arguments-intercept\": 57996947, + \"verifyEd25519Signature-cpu-arguments-slope\": 18975, + \"verifyEd25519Signature-memory-arguments\": 10 + }, + \"PlutusV3\": { + \"0\": 205665, + \"1\": 812, + \"10\": 4, + \"100\": 32, + \"101\": 65493, + \"102\": 32, + \"103\": 22558, + \"104\": 32, + \"105\": 16563, + \"106\": 32, + \"107\": 76511, + \"108\": 32, + \"109\": 196500, + \"11\": 1, + \"110\": 453240, + \"111\": 220, + \"112\": 0, + \"113\": 1, + \"114\": 1, + \"115\": 69522, + \"116\": 11687, + \"117\": 0, + \"118\": 1, + \"119\": 60091, + \"12\": 1000, + \"120\": 32, + \"121\": 196500, + \"122\": 453240, + \"123\": 220, + \"124\": 0, + \"125\": 1, + \"126\": 1, + \"127\": 196500, + \"128\": 453240, + \"129\": 220, + \"13\": 32, + \"130\": 0, + \"131\": 1, + \"132\": 1, + \"133\": 1159724, + \"134\": 392670, + \"135\": 0, + \"136\": 2, + \"137\": 806990, + \"138\": 30482, + \"139\": 4, + \"14\": 117366, + \"140\": 1927926, + \"141\": 82523, + \"142\": 4, + \"143\": 265318, + \"144\": 0, + \"145\": 4, + \"146\": 0, + \"147\": 85931, + \"148\": 32, + \"149\": 205665, + \"15\": 10475, + \"150\": 812, + \"151\": 1, + \"152\": 1, + \"153\": 41182, + \"154\": 32, + \"155\": 212342, + \"156\": 32, + \"157\": 31220, + \"158\": 32, + \"159\": 32696, + \"16\": 4, + \"160\": 32, + \"161\": 43357, + \"162\": 32, + \"163\": 32247, + \"164\": 32, + \"165\": 38314, + \"166\": 32, + \"167\": 35190005, + \"168\": 10, + \"169\": 57996947, + \"17\": 23000, + \"170\": 18975, + \"171\": 10, + \"172\": 39121781, + \"173\": 32260, + \"174\": 10, + \"175\": 23000, + \"176\": 100, + \"177\": 23000, + \"178\": 100, + \"179\": 832808, + \"18\": 100, + \"180\": 18, + \"181\": 3209094, + \"182\": 6, + \"183\": 331451, + \"184\": 1, + \"185\": 65990684, + \"186\": 23097, + \"187\": 18, + \"188\": 114242, + \"189\": 18, + \"19\": 23000, + \"190\": 94393407, + \"191\": 87060, + \"192\": 18, + \"193\": 16420089, + \"194\": 18, + \"195\": 2145798, + \"196\": 36, + \"197\": 3795345, + \"198\": 12, + \"199\": 889023, + \"2\": 1, + \"20\": 100, + \"200\": 1, + \"201\": 204237282, + \"202\": 23271, + \"203\": 36, + \"204\": 129165, + \"205\": 36, + \"206\": 189977790, + \"207\": 85902, + \"208\": 36, + \"209\": 33012864, + \"21\": 23000, + \"210\": 36, + \"211\": 388443360, + \"212\": 1, + \"213\": 401885761, + \"214\": 72, + \"215\": 2331379, + \"216\": 72, + \"217\": 1927926, + \"218\": 82523, + \"219\": 4, + \"22\": 100, + \"220\": 117366, + \"221\": 10475, + \"222\": 4, + \"23\": 23000, + \"24\": 100, + \"25\": 23000, + \"26\": 100, + \"27\": 23000, + \"28\": 100, + \"29\": 100, + \"3\": 1, + \"30\": 100, + \"31\": 23000, + \"32\": 100, + \"33\": 19537, + \"34\": 32, + \"35\": 175354, + \"36\": 32, + \"37\": 46417, + \"38\": 4, + \"39\": 221973, + \"4\": 1000, + \"40\": 511, + \"41\": 0, + \"42\": 1, + \"43\": 89141, + \"44\": 32, + \"45\": 497525, + \"46\": 14068, + \"47\": 4, + \"48\": 2, + \"49\": 196500, + \"5\": 571, + \"50\": 453240, + \"51\": 220, + \"52\": 0, + \"53\": 1, + \"54\": 1, + \"55\": 1000, + \"56\": 28662, + \"57\": 4, + \"58\": 2, + \"59\": 245000, + \"6\": 0, + \"60\": 216773, + \"61\": 62, + \"62\": 1, + \"63\": 1060367, + \"64\": 12586, + \"65\": 1, + \"66\": 208512, + \"67\": 421, + \"68\": 1, + \"69\": 187000, + \"7\": 1, + \"70\": 1000, + \"71\": 52998, + \"72\": 1, + \"73\": 80436, + \"74\": 32, + \"75\": 43249, + \"76\": 32, + \"77\": 1000, + \"78\": 32, + \"79\": 80556, + \"8\": 1000, + \"80\": 1, + \"81\": 57667, + \"82\": 4, + \"83\": 1000, + \"84\": 10, + \"85\": 197145, + \"86\": 156, + \"87\": 1, + \"88\": 197145, + \"89\": 156, + \"9\": 24177, + \"90\": 1, + \"91\": 204924, + \"92\": 473, + \"93\": 1, + \"94\": 208896, + \"95\": 511, + \"96\": 1, + \"97\": 52467, + \"98\": 32, + \"99\": 64832 + } + }, + \"price_mem\": 0.0577, + \"price_step\": 0.0000721, + \"max_tx_ex_mem\": \"10000000\", + \"max_tx_ex_steps\": \"10000000000\", + \"max_block_ex_mem\": \"50000000\", + \"max_block_ex_steps\": \"40000000000\", + \"max_val_size\": \"5000\", + \"collateral_percent\": 150, + \"max_collateral_inputs\": 3, + \"coins_per_utxo_size\": \"34482\", + \"coins_per_utxo_word\": \"34482\" + }"; + // we can't unwrap(), print or even check is_ok() for Result<_, JsError> + // outside of wasm environments so we just test make_tx_builder_cfg_impl directly instead. + let params: serde_json::Value = serde_json::from_str(response_json).unwrap(); + let config_builder = make_tx_builder_cfg_impl(params).unwrap(); + let _config = config_builder.build().unwrap(); + // TransactionBuilderConfig / TransactionBuilderConfigBuilder have no pub fields + // so we can't directly check them. + // TODO: check this somehow + } +} diff --git a/chain/rust/src/builders/tx_builder.rs b/chain/rust/src/builders/tx_builder.rs index 7ce221ff..4e91eb15 100644 --- a/chain/rust/src/builders/tx_builder.rs +++ b/chain/rust/src/builders/tx_builder.rs @@ -162,6 +162,7 @@ pub enum TxBuilderConfigField { ExUnitPrices, CollateralPercentage, MaxCollateralInputs, + CostModels, } #[derive(Debug, thiserror::Error)] diff --git a/chain/rust/src/plutus/utils.rs b/chain/rust/src/plutus/utils.rs index 66bfbdf4..c53330b7 100644 --- a/chain/rust/src/plutus/utils.rs +++ b/chain/rust/src/plutus/utils.rs @@ -271,6 +271,19 @@ impl Deserialize for ConstrPlutusData { } impl CostModels { + pub const PLUTUS_V1_COUNT: usize = 166; + pub const PLUTUS_V2_COUNT: usize = 175; + pub const PLUTUS_V3_COUNT: usize = 179; + + /// Total number of operations for the cost model of the given language + pub fn op_count(language: Language) -> usize { + match language { + Language::PlutusV1 => Self::PLUTUS_V1_COUNT, + Language::PlutusV2 => Self::PLUTUS_V2_COUNT, + Language::PlutusV3 => Self::PLUTUS_V3_COUNT, + } + } + pub fn as_map(&self) -> BTreeMap { let mut map = BTreeMap::new(); if let Some(v1_costs) = &self.plutus_v1 { diff --git a/chain/rust/src/utils.rs b/chain/rust/src/utils.rs index bd20c90d..c1a356ca 100644 --- a/chain/rust/src/utils.rs +++ b/chain/rust/src/utils.rs @@ -11,7 +11,7 @@ use std::io::{BufRead, Seek, Write}; use crate::{ crypto::hash::{hash_script, ScriptHashNamespace}, plutus::{Language, PlutusScript, PlutusV1Script, PlutusV2Script}, - NativeScript, Script, + NativeScript, Script, SubCoin, }; impl Script { @@ -569,6 +569,20 @@ impl Deserialize for NetworkId { } } +impl SubCoin { + /// Converts base 10 floats to SubCoin. + /// This is the format used by blockfrost for ex units + /// Warning: If the passed in float was not meant to be base 10 + /// this might result in a slightly inaccurate fraction. + pub fn from_base10_f32(f: f32) -> Self { + let mut denom = 1u64; + while (f * (denom as f32)).fract().abs() > f32::EPSILON { + denom *= 10; + } + Self::new((f * (denom as f32)).ceil() as u64, denom) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/chain/wasm/src/utils.rs b/chain/wasm/src/utils.rs index be2c0811..fafed94f 100644 --- a/chain/wasm/src/utils.rs +++ b/chain/wasm/src/utils.rs @@ -1,4 +1,4 @@ -use super::{Int, Script, ScriptHash}; +use super::{Int, Script, ScriptHash, SubCoin}; use cml_chain::plutus::Language; use wasm_bindgen::prelude::{wasm_bindgen, JsError, JsValue}; @@ -85,3 +85,14 @@ impl NetworkId { self.0.network } } + +#[wasm_bindgen] +impl SubCoin { + /// Converts base 10 floats to SubCoin. + /// This is the format used by blockfrost for ex units + /// Warning: If the passed in float was not meant to be base 10 + /// this might result in a slightly inaccurate fraction. + pub fn from_base10_f32(f: f32) -> Self { + cml_chain::SubCoin::from_base10_f32(f).into() + } +} diff --git a/cml/wasm/Cargo.toml b/cml/wasm/Cargo.toml index 63fc4254..92d2e7de 100644 --- a/cml/wasm/Cargo.toml +++ b/cml/wasm/Cargo.toml @@ -13,6 +13,7 @@ readme = "../../README.md" crate-type = ["cdylib", "rlib"] [dependencies] +cml-blockfrost-wasm = { path = "../../blockfrost/wasm", version = "0.1.0" } cml-chain-wasm = { path = "../../chain/wasm", version = "5.1.0" } cml-cip25-wasm = { path = "../../cip25/wasm", version = "5.1.0" } cml-cip36-wasm = { path = "../../cip36/wasm", version = "5.1.0" } diff --git a/cml/wasm/src/lib.rs b/cml/wasm/src/lib.rs index 52971eef..5c7a50e7 100644 --- a/cml/wasm/src/lib.rs +++ b/cml/wasm/src/lib.rs @@ -4,6 +4,7 @@ // multiple of them including a utils module so we just import an arbitrary type. // We don't need to worry about cml_core_wasm and cml_crypto_wasm since they // will be exported by the other crates here. +pub use cml_blockfrost_wasm::make_tx_builder_cfg; pub use cml_chain_wasm::AssetNameList; pub use cml_cip25_wasm::CIP25Metadata; pub use cml_cip36_wasm::CIP36DeregistrationCbor; From d8f66c45d87749e08d4f18c7d7bd85bfa96a8c87 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Mon, 4 Mar 2024 23:39:56 -0800 Subject: [PATCH 2/5] rename fix --- cml/wasm/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cml/wasm/src/lib.rs b/cml/wasm/src/lib.rs index 5c7a50e7..20ca3fe3 100644 --- a/cml/wasm/src/lib.rs +++ b/cml/wasm/src/lib.rs @@ -4,7 +4,7 @@ // multiple of them including a utils module so we just import an arbitrary type. // We don't need to worry about cml_core_wasm and cml_crypto_wasm since they // will be exported by the other crates here. -pub use cml_blockfrost_wasm::make_tx_builder_cfg; +pub use cml_blockfrost_wasm::blockfrost_make_tx_builder_cfg; pub use cml_chain_wasm::AssetNameList; pub use cml_cip25_wasm::CIP25Metadata; pub use cml_cip36_wasm::CIP36DeregistrationCbor; From 62607f7c3c2db9660fedf6bb5453de9687bc8596 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Tue, 5 Mar 2024 05:20:16 -0800 Subject: [PATCH 3/5] Add tests checks + expose tx builder cfg fields + fix clippy warnings --- blockfrost/wasm/Cargo.toml | 2 +- blockfrost/wasm/src/lib.rs | 81 +++++++++++++++++++++++++-- chain/rust/src/builders/tx_builder.rs | 20 +++---- chain/rust/src/utils.rs | 10 +++- 4 files changed, 95 insertions(+), 18 deletions(-) diff --git a/blockfrost/wasm/Cargo.toml b/blockfrost/wasm/Cargo.toml index 52c0d083..e99df592 100644 --- a/blockfrost/wasm/Cargo.toml +++ b/blockfrost/wasm/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" crate-type = ["cdylib", "rlib"] [dependencies] -cml-blockfrost = { path = "../rust", version = "0.1.0", direct_api = false } +cml-blockfrost = { path = "../rust", version = "0.1.0" } cml-chain = { path = "../../chain/rust", version = "5.1.0" } cml-chain-wasm = { path = "../../chain/wasm", version = "5.1.0" } serde = { version = "1.0", features = ["derive"] } diff --git a/blockfrost/wasm/src/lib.rs b/blockfrost/wasm/src/lib.rs index 0ea66dee..2a133bdd 100644 --- a/blockfrost/wasm/src/lib.rs +++ b/blockfrost/wasm/src/lib.rs @@ -18,7 +18,7 @@ fn parse_string_coin_param( .ok_or(BlockfrostParamsParseError::MissingField(field)) .and_then(|v| { match v { - serde_json::Value::String(c) => Coin::from_str(&c).ok(), + serde_json::Value::String(c) => Coin::from_str(c).ok(), _ => None, } .ok_or(BlockfrostParamsParseError::IncorrectFormat(field)) @@ -114,7 +114,7 @@ fn make_tx_builder_cfg_impl( )); } } - Ok(config_builder.into()) + Ok(config_builder) } /** @@ -135,6 +135,8 @@ pub fn blockfrost_make_tx_builder_cfg( #[cfg(test)] mod tests { + use cml_chain::{plutus::CostModels, Int}; + use super::*; #[test] @@ -748,9 +750,76 @@ mod tests { // outside of wasm environments so we just test make_tx_builder_cfg_impl directly instead. let params: serde_json::Value = serde_json::from_str(response_json).unwrap(); let config_builder = make_tx_builder_cfg_impl(params).unwrap(); - let _config = config_builder.build().unwrap(); - // TransactionBuilderConfig / TransactionBuilderConfigBuilder have no pub fields - // so we can't directly check them. - // TODO: check this somehow + let config = config_builder.build().unwrap(); + assert_eq!(config.fee_algo.coefficient, 44); + assert_eq!(config.fee_algo.constant, 155381); + assert_eq!(config.pool_deposit, 500000000); + assert_eq!(config.key_deposit, 2000000); + assert_eq!(config.max_value_size, 5000); + assert_eq!(config.max_tx_size, 16384); + assert_eq!(config.coins_per_utxo_byte, 34482); + assert_eq!(config.ex_unit_prices.mem_price.numerator, 577); + assert_eq!(config.ex_unit_prices.mem_price.denominator, 10000); + assert_eq!(config.ex_unit_prices.step_price.numerator, 721); + assert_eq!(config.ex_unit_prices.step_price.denominator, 10000000); + assert_eq!(config.max_collateral_inputs, 3); + let plutus_v1_cost_models = [ + 205665i64, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, + 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, + 23000, 100, 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, + 14068, 4, 2, 196500, 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, 1, + 1060367, 12586, 1, 208512, 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, 1000, + 32, 80556, 1, 57667, 4, 1000, 10, 197145, 156, 1, 197145, 156, 1, 204924, 473, 1, + 208896, 511, 1, 52467, 32, 64832, 32, 65493, 32, 22558, 32, 16563, 32, 76511, 32, + 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, 1, 60091, 32, 196500, 453240, 220, 0, 1, + 1, 196500, 453240, 220, 0, 1, 1, 806990, 30482, 4, 1927926, 82523, 4, 265318, 0, 4, 0, + 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, 31220, 32, 32696, 32, 43357, 32, + 32247, 32, 38314, 32, 57996947, 18975, 10, + ] + .iter() + .map(|c| Int::from(*c)) + .collect(); + let plutus_v2_cost_models = [ + 205665i64, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, + 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, + 23000, 100, 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, + 14068, 4, 2, 196500, 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, 1, + 1060367, 12586, 1, 208512, 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, 1000, + 32, 80556, 1, 57667, 4, 1000, 10, 197145, 156, 1, 197145, 156, 1, 204924, 473, 1, + 208896, 511, 1, 52467, 32, 64832, 32, 65493, 32, 22558, 32, 16563, 32, 76511, 32, + 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, 1, 60091, 32, 196500, 453240, 220, 0, 1, + 1, 196500, 453240, 220, 0, 1, 1, 1159724, 392670, 0, 2, 806990, 30482, 4, 1927926, + 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, 31220, + 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 35892428, 10, 57996947, 18975, 10, + 38887044, 32947, 10, + ] + .iter() + .map(|c| Int::from(*c)) + .collect(); + let plutus_v3_cost_models: Vec = [ + 205665i64, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, + 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, + 23000, 100, 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, + 14068, 4, 2, 196500, 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, 1, + 1060367, 12586, 1, 208512, 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, 1000, + 32, 80556, 1, 57667, 4, 1000, 10, 197145, 156, 1, 197145, 156, 1, 204924, 473, 1, + 208896, 511, 1, 52467, 32, 64832, 32, 65493, 32, 22558, 32, 16563, 32, 76511, 32, + 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, 1, 60091, 32, 196500, 453240, 220, 0, 1, + 1, 196500, 453240, 220, 0, 1, 1, 1159724, 392670, 0, 2, 806990, 30482, 4, 1927926, + 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, 31220, + 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 35190005, 10, 57996947, 18975, 10, + 39121781, 32260, 10, 23000, 100, 23000, 100, 832808, 18, 3209094, 6, 331451, 1, + 65990684, 23097, 18, 114242, 18, 94393407, 87060, 18, 16420089, 18, 2145798, 36, + 3795345, 12, 889023, 1, 204237282, 23271, 36, 129165, 36, 189977790, 85902, 36, + 33012864, 36, 388443360, 1, 401885761, 72, 2331379, 72, 1927926, 82523, 4, 117366, + 10475, 4, + ] + .iter() + .take(CostModels::PLUTUS_V3_COUNT) + .map(|c| Int::from(*c)) + .collect(); + assert_eq!(config.cost_models.plutus_v1, Some(plutus_v1_cost_models)); + assert_eq!(config.cost_models.plutus_v2, Some(plutus_v2_cost_models)); + assert_eq!(config.cost_models.plutus_v3, Some(plutus_v3_cost_models)); } } diff --git a/chain/rust/src/builders/tx_builder.rs b/chain/rust/src/builders/tx_builder.rs index 4e91eb15..0cc75530 100644 --- a/chain/rust/src/builders/tx_builder.rs +++ b/chain/rust/src/builders/tx_builder.rs @@ -244,17 +244,17 @@ pub enum CoinSelectionStrategyCIP2 { #[derive(Clone, Debug)] pub struct TransactionBuilderConfig { - fee_algo: LinearFee, - pool_deposit: u64, // protocol parameter - key_deposit: u64, // protocol parameter - max_value_size: u32, // protocol parameter - max_tx_size: u32, // protocol parameter - coins_per_utxo_byte: Coin, // protocol parameter - ex_unit_prices: ExUnitPrices, // protocol parameter - cost_models: CostModels, // protocol parameter + pub fee_algo: LinearFee, + pub pool_deposit: u64, // protocol parameter + pub key_deposit: u64, // protocol parameter + pub max_value_size: u32, // protocol parameter + pub max_tx_size: u32, // protocol parameter + pub coins_per_utxo_byte: Coin, // protocol parameter + pub ex_unit_prices: ExUnitPrices, // protocol parameter + pub cost_models: CostModels, // protocol parameter _collateral_percentage: u32, // protocol parameter - max_collateral_inputs: u32, // protocol parameter - prefer_pure_change: bool, + pub max_collateral_inputs: u32, // protocol parameter + pub prefer_pure_change: bool, } #[derive(Clone, Debug, Default)] diff --git a/chain/rust/src/utils.rs b/chain/rust/src/utils.rs index c1a356ca..d384b234 100644 --- a/chain/rust/src/utils.rs +++ b/chain/rust/src/utils.rs @@ -579,7 +579,15 @@ impl SubCoin { while (f * (denom as f32)).fract().abs() > f32::EPSILON { denom *= 10; } - Self::new((f * (denom as f32)).ceil() as u64, denom) + // we check against episilon to be sure we get all info + // but this could result in over-extending with respect to the original float + // so run it back after the fact. + let mut numerator = (f * (denom as f32)).ceil() as u64; + while numerator % 10 == 0 && denom % 10 == 0 { + numerator /= 10; + denom /= 10; + } + Self::new(numerator, denom) } } From 224d133ffbb1d407d104ccf571d125bdff174a3d Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Tue, 5 Mar 2024 05:57:14 -0800 Subject: [PATCH 4/5] cargo fmt --- chain/rust/src/builders/tx_builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/rust/src/builders/tx_builder.rs b/chain/rust/src/builders/tx_builder.rs index 0cc75530..41e08a8f 100644 --- a/chain/rust/src/builders/tx_builder.rs +++ b/chain/rust/src/builders/tx_builder.rs @@ -252,7 +252,7 @@ pub struct TransactionBuilderConfig { pub coins_per_utxo_byte: Coin, // protocol parameter pub ex_unit_prices: ExUnitPrices, // protocol parameter pub cost_models: CostModels, // protocol parameter - _collateral_percentage: u32, // protocol parameter + _collateral_percentage: u32, // protocol parameter pub max_collateral_inputs: u32, // protocol parameter pub prefer_pure_change: bool, } From 16e6bdbd0133b4baaf5fa70b29e46ba65b6a0b13 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Tue, 23 Jul 2024 09:13:17 -0700 Subject: [PATCH 5/5] fixes for changes from #336 --- blockfrost/rust/src/lib.rs | 45 +++++++++++++++++++------------- blockfrost/wasm/src/lib.rs | 47 +++++++++++++++++++++------------- chain/rust/src/plutus/utils.rs | 42 ++++++++++++++++++++++++++++++ chain/wasm/src/plutus/utils.rs | 17 +++++++++++- 4 files changed, 114 insertions(+), 37 deletions(-) diff --git a/blockfrost/rust/src/lib.rs b/blockfrost/rust/src/lib.rs index fac079f9..6334b869 100644 --- a/blockfrost/rust/src/lib.rs +++ b/blockfrost/rust/src/lib.rs @@ -1,7 +1,7 @@ use cml_chain::{ builders::tx_builder::TxBuilderConfigField, plutus::{CostModels, Language}, - Int, + DeserializeError, }; use std::collections::HashMap; @@ -16,6 +16,9 @@ pub enum CostModelsError { InvalidCost(Option), #[error("Invalid op count: {0}")] OpCount(usize), + // likely shouldn't happen as this would be checked earlier + #[error("Couldn't set: {0}")] + Setting(#[from] DeserializeError), } #[derive(Debug, thiserror::Error)] @@ -31,7 +34,7 @@ pub enum BlockfrostParamsParseError { pub fn parse_cost_model( serde_value: &serde_json::Value, language: Language, -) -> Result, CostModelsError> { +) -> Result, CostModelsError> { if let serde_json::Value::Object(cost_obj) = serde_value { let mut costs = vec![]; // bad idea to assume it's ordered - depends on feature enabled @@ -46,7 +49,7 @@ pub fn parse_cost_model( .get(&key) .and_then(|c| c.as_i64()) .ok_or_else(|| CostModelsError::InvalidCost(cost_obj.get(&key).cloned()))?; - costs.push(Int::from(cost)); + costs.push(cost); } Ok(costs) } else { @@ -56,7 +59,7 @@ pub fn parse_cost_model( pub fn parse_sancho_cost_model( serde_value: &serde_json::Value, -) -> Result, CostModelsError> { +) -> Result, CostModelsError> { if let serde_json::Value::Object(cost_obj) = serde_value { let mut costs = vec![]; for i in 0..CostModels::PLUTUS_V3_COUNT { @@ -66,7 +69,7 @@ pub fn parse_sancho_cost_model( .ok_or_else(|| { CostModelsError::InvalidCost(cost_obj.get(&i.to_string()).cloned()) })?; - costs.push(cml_chain::Int::from(cost)); + costs.push(cost); } Ok(costs) } else { @@ -77,25 +80,31 @@ pub fn parse_sancho_cost_model( pub fn parse_cost_models( costs: &HashMap, ) -> Result { - let mut cost_models = CostModels::new(); + let mut cost_models = CostModels::default(); if let Some(plutus_v1) = costs.get("PlutusV1") { - cost_models.plutus_v1 = Some( - parse_cost_model(plutus_v1, Language::PlutusV1) - .map_err(|e| BlockfrostParamsParseError::CostModels(e, Language::PlutusV1))?, - ); + cost_models + .set_plutus_v1( + parse_cost_model(plutus_v1, Language::PlutusV1) + .map_err(|e| BlockfrostParamsParseError::CostModels(e, Language::PlutusV1))?, + ) + .map_err(|e| BlockfrostParamsParseError::CostModels(e.into(), Language::PlutusV1))?; } if let Some(plutus_v2) = costs.get("PlutusV2") { - cost_models.plutus_v2 = Some( - parse_cost_model(plutus_v2, Language::PlutusV2) - .map_err(|e| BlockfrostParamsParseError::CostModels(e, Language::PlutusV2))?, - ); + cost_models + .set_plutus_v2( + parse_cost_model(plutus_v2, Language::PlutusV2) + .map_err(|e| BlockfrostParamsParseError::CostModels(e, Language::PlutusV2))?, + ) + .map_err(|e| BlockfrostParamsParseError::CostModels(e.into(), Language::PlutusV2))?; } // Sancho testnet has a very different format for some reason if let Some(plutus_v3) = costs.get("PlutusV3") { - cost_models.plutus_v3 = Some( - parse_sancho_cost_model(plutus_v3) - .map_err(|e| BlockfrostParamsParseError::CostModels(e, Language::PlutusV3))?, - ); + cost_models + .set_plutus_v3( + parse_sancho_cost_model(plutus_v3) + .map_err(|e| BlockfrostParamsParseError::CostModels(e, Language::PlutusV3))?, + ) + .map_err(|e| BlockfrostParamsParseError::CostModels(e.into(), Language::PlutusV3))?; } Ok(cost_models) } diff --git a/blockfrost/wasm/src/lib.rs b/blockfrost/wasm/src/lib.rs index 2a133bdd..0d0148de 100644 --- a/blockfrost/wasm/src/lib.rs +++ b/blockfrost/wasm/src/lib.rs @@ -135,7 +135,7 @@ pub fn blockfrost_make_tx_builder_cfg( #[cfg(test)] mod tests { - use cml_chain::{plutus::CostModels, Int}; + use cml_chain::plutus::{CostModels, Language}; use super::*; @@ -775,10 +775,7 @@ mod tests { 1, 196500, 453240, 220, 0, 1, 1, 806990, 30482, 4, 1927926, 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 57996947, 18975, 10, - ] - .iter() - .map(|c| Int::from(*c)) - .collect(); + ]; let plutus_v2_cost_models = [ 205665i64, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, @@ -792,11 +789,8 @@ mod tests { 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 35892428, 10, 57996947, 18975, 10, 38887044, 32947, 10, - ] - .iter() - .map(|c| Int::from(*c)) - .collect(); - let plutus_v3_cost_models: Vec = [ + ]; + let plutus_v3_cost_models = [ 205665i64, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, 23000, 100, 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, @@ -813,13 +807,30 @@ mod tests { 3795345, 12, 889023, 1, 204237282, 23271, 36, 129165, 36, 189977790, 85902, 36, 33012864, 36, 388443360, 1, 401885761, 72, 2331379, 72, 1927926, 82523, 4, 117366, 10475, 4, - ] - .iter() - .take(CostModels::PLUTUS_V3_COUNT) - .map(|c| Int::from(*c)) - .collect(); - assert_eq!(config.cost_models.plutus_v1, Some(plutus_v1_cost_models)); - assert_eq!(config.cost_models.plutus_v2, Some(plutus_v2_cost_models)); - assert_eq!(config.cost_models.plutus_v3, Some(plutus_v3_cost_models)); + ]; + assert_eq!( + *config + .cost_models + .inner + .get(&Language::PlutusV1.into()) + .unwrap(), + plutus_v1_cost_models + ); + assert_eq!( + *config + .cost_models + .inner + .get(&Language::PlutusV2.into()) + .unwrap(), + plutus_v2_cost_models + ); + assert_eq!( + *config + .cost_models + .inner + .get(&Language::PlutusV3.into()) + .unwrap(), + plutus_v3_cost_models[..CostModels::PLUTUS_V3_COUNT] + ); } } diff --git a/chain/rust/src/plutus/utils.rs b/chain/rust/src/plutus/utils.rs index 3a3b6425..f5fceb63 100644 --- a/chain/rust/src/plutus/utils.rs +++ b/chain/rust/src/plutus/utils.rs @@ -330,6 +330,48 @@ impl CostModels { pub const PLUTUS_V2_COUNT: usize = 175; pub const PLUTUS_V3_COUNT: usize = 179; + pub fn set_plutus_v1(&mut self, costs: Vec) -> Result<(), DeserializeError> { + // on-chain there is no restriction on length but this will help users avoid problems + if costs.len() != Self::PLUTUS_V1_COUNT { + return Err(DeserializeFailure::RangeCheck { + found: costs.len() as isize, + min: Some(Self::PLUTUS_V1_COUNT as isize), + max: Some(Self::PLUTUS_V1_COUNT as isize), + } + .into()); + } + self.inner.insert(Language::PlutusV1.into(), costs); + Ok(()) + } + + pub fn set_plutus_v2(&mut self, costs: Vec) -> Result<(), DeserializeError> { + // on-chain there is no restriction on length but this will help users avoid problems + if costs.len() != Self::PLUTUS_V2_COUNT { + return Err(DeserializeFailure::RangeCheck { + found: costs.len() as isize, + min: Some(Self::PLUTUS_V2_COUNT as isize), + max: Some(Self::PLUTUS_V2_COUNT as isize), + } + .into()); + } + self.inner.insert(Language::PlutusV2.into(), costs); + Ok(()) + } + + pub fn set_plutus_v3(&mut self, costs: Vec) -> Result<(), DeserializeError> { + // on-chain there is no restriction on length but this will help users avoid problems + if costs.len() != Self::PLUTUS_V3_COUNT { + return Err(DeserializeFailure::RangeCheck { + found: costs.len() as isize, + min: Some(Self::PLUTUS_V3_COUNT as isize), + max: Some(Self::PLUTUS_V3_COUNT as isize), + } + .into()); + } + self.inner.insert(Language::PlutusV3.into(), costs); + Ok(()) + } + /// Total number of operations for the cost model of the given language pub fn op_count(language: Language) -> usize { match language { diff --git a/chain/wasm/src/plutus/utils.rs b/chain/wasm/src/plutus/utils.rs index 2b458235..86a29635 100644 --- a/chain/wasm/src/plutus/utils.rs +++ b/chain/wasm/src/plutus/utils.rs @@ -1,5 +1,5 @@ use crate::{ - plutus::{PlutusData, Redeemers}, + plutus::{CostModels, PlutusData, Redeemers}, LegacyRedeemerList, PlutusDataList, }; use cml_chain::plutus::Language; @@ -37,6 +37,21 @@ impl ConstrPlutusData { } } +#[wasm_bindgen] +impl CostModels { + pub fn set_plutus_v1(&mut self, costs: Vec) -> Result<(), JsError> { + self.0.set_plutus_v1(costs).map_err(Into::into) + } + + pub fn set_plutus_v2(&mut self, costs: Vec) -> Result<(), JsError> { + self.0.set_plutus_v2(costs).map_err(Into::into) + } + + pub fn set_plutus_v3(&mut self, costs: Vec) -> Result<(), JsError> { + self.0.set_plutus_v3(costs).map_err(Into::into) + } +} + #[derive(Clone, Debug)] #[wasm_bindgen] pub struct PlutusMap(cml_chain::plutus::PlutusMap);