From d979c399b7a38f9a3e302edd2f63a066629833fd Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Mon, 17 Jun 2024 15:52:06 -0700 Subject: [PATCH 1/2] Conway CostModels update Due to changes here: IntersectMBO/cardano-ledger#4284 Clarification here: IntersectMBO/cardano-ledger#4383 Allows non-Plutus languages according to the spec and changes to an indexable map type. --- chain/rust/src/builders/tx_builder.rs | 2 +- chain/rust/src/crypto/hash.rs | 38 +-- chain/rust/src/genesis/network_info.rs | 12 +- chain/rust/src/plutus/cbor_encodings.rs | 12 +- chain/rust/src/plutus/mod.rs | 57 +++- chain/rust/src/plutus/serialization.rs | 391 ++++++++---------------- chain/rust/src/plutus/utils.rs | 148 +++++---- chain/wasm/src/plutus/mod.rs | 45 ++- specs/conway/plutus.cddl | 11 +- 9 files changed, 301 insertions(+), 415 deletions(-) diff --git a/chain/rust/src/builders/tx_builder.rs b/chain/rust/src/builders/tx_builder.rs index 7a6dd4bd..80d15cfb 100644 --- a/chain/rust/src/builders/tx_builder.rs +++ b/chain/rust/src/builders/tx_builder.rs @@ -363,7 +363,7 @@ impl TransactionBuilderConfigBuilder { cost_models: if self.cost_models.is_some() { self.cost_models.unwrap() } else { - CostModels::new() + CostModels::default() }, _collateral_percentage: self.collateral_percentage.ok_or( TxBuilderError::UninitializedField(TxBuilderConfigField::CollateralPercentage), diff --git a/chain/rust/src/crypto/hash.rs b/chain/rust/src/crypto/hash.rs index d07f6e03..ceecab1c 100644 --- a/chain/rust/src/crypto/hash.rs +++ b/chain/rust/src/crypto/hash.rs @@ -88,37 +88,15 @@ pub fn calc_script_data_hash( encoding: Option<&TransactionWitnessSetEncoding>, ) -> Result, ScriptDataHashError> { if !redeemers.is_empty() || !datums.is_empty() { - let mut required_costmdls = CostModels::new(); + let mut required_costmdls = CostModels::default(); for lang in used_langs { - match lang { - Language::PlutusV1 => { - required_costmdls.plutus_v1 = Some( - cost_models - .plutus_v1 - .as_ref() - .ok_or(ScriptDataHashError::MissingCostModel(*lang))? - .clone(), - ); - } - Language::PlutusV2 => { - required_costmdls.plutus_v2 = Some( - cost_models - .plutus_v2 - .as_ref() - .ok_or(ScriptDataHashError::MissingCostModel(*lang))? - .clone(), - ); - } - Language::PlutusV3 => { - required_costmdls.plutus_v3 = Some( - cost_models - .plutus_v3 - .as_ref() - .ok_or(ScriptDataHashError::MissingCostModel(*lang))? - .clone(), - ); - } - } + required_costmdls.inner.insert( + *lang as u64, + cost_models + .inner.get(&(*lang).into()) + .ok_or(ScriptDataHashError::MissingCostModel(*lang))? + .clone(), + ); } Ok(Some(hash_script_data( diff --git a/chain/rust/src/genesis/network_info.rs b/chain/rust/src/genesis/network_info.rs index eb9aef0d..e408c461 100644 --- a/chain/rust/src/genesis/network_info.rs +++ b/chain/rust/src/genesis/network_info.rs @@ -1,10 +1,9 @@ -use crate::{byron::ProtocolMagic, plutus::CostModels}; +use crate::{byron::ProtocolMagic, plutus::{CostModels, Language}}; use cml_core::{ network::{ BYRON_MAINNET_NETWORK_MAGIC, BYRON_TESTNET_NETWORK_MAGIC, PREPROD_NETWORK_MAGIC, PREVIEW_NETWORK_MAGIC, SANCHO_TESTNET_NETWORK_MAGIC, }, - Int, }; #[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -67,7 +66,7 @@ impl NetworkInfo { // TODO: https://github.com/dcSpark/cardano-multiplatform-lib/issues/92 pub fn plutus_alonzo_cost_models() -> CostModels { - let ops: [u64; 166] = [ + let ops = vec![ 197209, 0, 1, 1, 396231, 621, 0, 1, 150000, 1000, 0, 1, 150000, 32, 2477736, 29175, 4, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 100, 100, 29773, 100, 150000, 32, 150000, 32, 150000, 32, 150000, 1000, 0, 1, 150000, 32, 150000, 1000, 0, @@ -81,7 +80,10 @@ pub fn plutus_alonzo_cost_models() -> CostModels { 3345831, 1, 1, ]; - let mut res = CostModels::new(); - res.plutus_v1 = Some(ops.iter().map(|&i| Int::from(i)).collect()); + let mut res = CostModels::default(); + res.inner.insert( + Language::PlutusV1 as u64, + ops + ); res } diff --git a/chain/rust/src/plutus/cbor_encodings.rs b/chain/rust/src/plutus/cbor_encodings.rs index 2ae27ca4..5e894c0a 100644 --- a/chain/rust/src/plutus/cbor_encodings.rs +++ b/chain/rust/src/plutus/cbor_encodings.rs @@ -2,17 +2,13 @@ // https://github.com/dcSpark/cddl-codegen use cml_core::serialization::{LenEncoding, StringEncoding}; +use std::collections::BTreeMap; #[derive(Clone, Debug, Default)] pub struct CostModelsEncoding { - pub len_encoding: LenEncoding, - pub orig_deser_order: Vec, - pub plutus_v1_encoding: LenEncoding, - pub plutus_v1_key_encoding: Option, - pub plutus_v2_encoding: LenEncoding, - pub plutus_v2_key_encoding: Option, - pub plutus_v3_encoding: LenEncoding, - pub plutus_v3_key_encoding: Option, + pub inner_encoding: LenEncoding, + pub inner_key_encodings: BTreeMap>, + pub inner_value_encodings: BTreeMap>)>, } #[derive(Clone, Debug, Default)] diff --git a/chain/rust/src/plutus/mod.rs b/chain/rust/src/plutus/mod.rs index 79978f1e..267ce228 100644 --- a/chain/rust/src/plutus/mod.rs +++ b/chain/rust/src/plutus/mod.rs @@ -18,34 +18,65 @@ use cbor_encodings::{ use cml_core::ordered_hash_map::OrderedHashMap; use cml_core::serialization::{LenEncoding, Serialize, StringEncoding}; -use cml_core::Int; use cml_crypto::{blake2b256, DatumHash}; pub use utils::{ConstrPlutusData, PlutusMap, PlutusScript}; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +#[derive(Clone, Debug)] pub struct CostModels { - pub plutus_v1: Option>, - pub plutus_v2: Option>, - pub plutus_v3: Option>, - #[serde(skip)] + pub inner: OrderedHashMap>, pub encodings: Option, } impl CostModels { - pub fn new() -> Self { + pub fn new(inner: OrderedHashMap>) -> Self { Self { - plutus_v1: None, - plutus_v2: None, - plutus_v3: None, + inner, encodings: None, } } } -impl Default for CostModels { - fn default() -> Self { - Self::new() +impl From>> for CostModels { + fn from(inner: OrderedHashMap>) -> Self { + CostModels::new(inner.clone().into()) + } +} + +impl From for OrderedHashMap> { + fn from(wrapper: CostModels) -> Self { + wrapper.inner + } +} + +impl serde::Serialize for CostModels { + fn serialize(&self, serializer: S) -> Result + where S: serde::Serializer, + { + self.inner.serialize(serializer) + } +} + +impl<'de> serde :: de :: Deserialize < 'de > for CostModels { + fn deserialize(deserializer: D) -> Result + where D: serde :: de :: Deserializer < 'de >, + { + let inner = > as serde::de::Deserialize>::deserialize(deserializer)?; + Ok(Self::new(inner)) + } +} + +impl schemars::JsonSchema for CostModels { + fn schema_name() -> String { + String::from("CostModels") + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + OrderedHashMap::>::json_schema(gen) + } + + fn is_referenceable() -> bool { + OrderedHashMap::>::is_referenceable() } } diff --git a/chain/rust/src/plutus/serialization.rs b/chain/rust/src/plutus/serialization.rs index 8e187e26..9c229084 100644 --- a/chain/rust/src/plutus/serialization.rs +++ b/chain/rust/src/plutus/serialization.rs @@ -7,6 +7,7 @@ use cbor_event::de::Deserializer; use cbor_event::se::Serializer; use cml_core::error::*; use cml_core::serialization::*; +use std::collections::BTreeMap; use std::io::{BufRead, Seek, SeekFrom, Write}; // PlutusData::Bytes uses this specific encoding: @@ -21,139 +22,70 @@ impl Serialize for CostModels { serializer.write_map_sz( self.encodings .as_ref() - .map(|encs| encs.len_encoding) + .map(|encs| encs.inner_encoding) .unwrap_or_default() - .to_len_sz( - match &self.plutus_v1 { - Some(_) => 1, - None => 0, - } + match &self.plutus_v2 { - Some(_) => 1, - None => 0, - } + match &self.plutus_v3 { - Some(_) => 1, - None => 0, - }, - force_canonical, - ), + .to_len_sz(self.inner.len() as u64, force_canonical), )?; - let deser_order = self - .encodings - .as_ref() - .filter(|encs| { - !force_canonical - && encs.orig_deser_order.len() - == match &self.plutus_v1 { - Some(_) => 1, - None => 0, - } + match &self.plutus_v2 { - Some(_) => 1, - None => 0, - } + match &self.plutus_v3 { - Some(_) => 1, - None => 0, - } + let mut key_order = self + .inner + .iter() + .map(|(k, v)| { + let mut buf = cbor_event::se::Serializer::new_vec(); + let inner_key_encoding = self + .encodings + .as_ref() + .and_then(|encs| encs.inner_key_encodings.get(k)) + .cloned() + .unwrap_or_default(); + buf.write_unsigned_integer_sz(*k, fit_sz(*k, inner_key_encoding, force_canonical))?; + Ok((buf.finalize(), k, v)) }) - .map(|encs| encs.orig_deser_order.clone()) - .unwrap_or_else(|| vec![0, 1, 2]); - for field_index in deser_order { - match field_index { - 0 => { - if let Some(field) = &self.plutus_v1 { - serializer.write_unsigned_integer_sz( - 0u64, - fit_sz( - 0u64, - self.encodings - .as_ref() - .map(|encs| encs.plutus_v1_key_encoding) - .unwrap_or_default(), - force_canonical, - ), - )?; - serializer.write_array_sz( - self.encodings - .as_ref() - .map(|encs| encs.plutus_v1_encoding) - .unwrap_or_default() - .to_len_sz(field.len() as u64, force_canonical), - )?; - for element in field.iter() { - element.serialize(serializer, force_canonical)?; - } - self.encodings - .as_ref() - .map(|encs| encs.plutus_v1_encoding) - .unwrap_or_default() - .end(serializer, force_canonical)?; - } - } - 1 => { - if let Some(field) = &self.plutus_v2 { - serializer.write_unsigned_integer_sz( - 1u64, - fit_sz( - 1u64, - self.encodings - .as_ref() - .map(|encs| encs.plutus_v2_key_encoding) - .unwrap_or_default(), - force_canonical, - ), - )?; - serializer.write_array_sz( - self.encodings - .as_ref() - .map(|encs| encs.plutus_v2_encoding) - .unwrap_or_default() - .to_len_sz(field.len() as u64, force_canonical), - )?; - for element in field.iter() { - element.serialize(serializer, force_canonical)?; - } - self.encodings - .as_ref() - .map(|encs| encs.plutus_v2_encoding) - .unwrap_or_default() - .end(serializer, force_canonical)?; - } + .collect::, &_, &_)>, cbor_event::Error>>()?; + if force_canonical { + key_order.sort_by(|(lhs_bytes, _, _), (rhs_bytes, _, _)| { + match lhs_bytes.len().cmp(&rhs_bytes.len()) { + std::cmp::Ordering::Equal => lhs_bytes.cmp(rhs_bytes), + diff_ord => diff_ord, } - 2 => { - if let Some(field) = &self.plutus_v3 { - serializer.write_unsigned_integer_sz( - 2u64, - fit_sz( - 2u64, - self.encodings - .as_ref() - .map(|encs| encs.plutus_v3_key_encoding) - .unwrap_or_default(), - force_canonical, - ), - )?; - serializer.write_array_sz( - self.encodings - .as_ref() - .map(|encs| encs.plutus_v3_encoding) - .unwrap_or_default() - .to_len_sz(field.len() as u64, force_canonical), - )?; - for element in field.iter() { - element.serialize(serializer, force_canonical)?; - } - self.encodings - .as_ref() - .map(|encs| encs.plutus_v3_encoding) - .unwrap_or_default() - .end(serializer, force_canonical)?; - } + }); + } + for (key_bytes, key, value) in key_order { + serializer.write_raw_bytes(&key_bytes)?; + let (inner_value_encoding, inner_value_elem_encodings) = self + .encodings + .as_ref() + .and_then(|encs| encs.inner_value_encodings.get(key)) + .cloned() + .unwrap_or_else(|| (LenEncoding::default(), Vec::new())); + serializer.write_array_sz( + inner_value_encoding.to_len_sz(value.len() as u64, force_canonical), + )?; + for (i, element) in value.iter().enumerate() { + let inner_value_elem_encoding = inner_value_elem_encodings + .get(i) + .cloned() + .unwrap_or_default(); + if *element >= 0 { + serializer.write_unsigned_integer_sz( + *element as u64, + fit_sz(*element as u64, inner_value_elem_encoding, force_canonical), + )?; + } else { + serializer.write_negative_integer_sz( + *element as i128, + fit_sz( + (*element + 1).abs() as u64, + inner_value_elem_encoding, + force_canonical, + ), + )?; } - _ => unreachable!(), - }; + } + inner_value_encoding.end(serializer, force_canonical)?; } self.encodings .as_ref() - .map(|encs| encs.len_encoding) + .map(|encs| encs.inner_encoding) .unwrap_or_default() .end(serializer, force_canonical) } @@ -161,159 +93,74 @@ impl Serialize for CostModels { impl Deserialize for CostModels { fn deserialize(raw: &mut Deserializer) -> Result { - let len = raw.map_sz()?; - let len_encoding: LenEncoding = len.into(); - let mut read_len = CBORReadLen::new(len); - (|| -> Result<_, DeserializeError> { - let mut orig_deser_order = Vec::new(); - let mut plutus_v1_encoding = LenEncoding::default(); - let mut plutus_v1_key_encoding = None; - let mut plutus_v1 = None; - let mut plutus_v2_encoding = LenEncoding::default(); - let mut plutus_v2_key_encoding = None; - let mut plutus_v2 = None; - let mut plutus_v3_encoding = LenEncoding::default(); - let mut plutus_v3_key_encoding = None; - let mut plutus_v3 = None; - let mut read = 0; + let mut inner_table = OrderedHashMap::new(); + let inner_len = raw.map_sz()?; + let inner_encoding = inner_len.into(); + let mut inner_key_encodings = BTreeMap::new(); + let mut inner_value_encodings = BTreeMap::new(); + while match inner_len { + cbor_event::LenSz::Len(n, _) => (inner_table.len() as u64) < n, + cbor_event::LenSz::Indefinite => true, + } { + if raw.cbor_type()? == cbor_event::Type::Special { + assert_eq!(raw.special()?, cbor_event::Special::Break); + break; + } + let (inner_key, inner_key_encoding) = + raw.unsigned_integer_sz().map(|(x, enc)| (x, Some(enc)))?; + let mut inner_value_arr = Vec::new(); + let len = raw.array_sz()?; + let inner_value_encoding = len.into(); + let mut inner_value_elem_encodings = Vec::new(); while match len { - cbor_event::LenSz::Len(n, _) => read < n, + cbor_event::LenSz::Len(n, _) => (inner_value_arr.len() as u64) < n, cbor_event::LenSz::Indefinite => true, } { - match raw.cbor_type()? { - cbor_event::Type::UnsignedInteger => match raw.unsigned_integer_sz()? { - (0, key_enc) => { - if plutus_v1.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); - } - let (tmp_plutus_v1, tmp_plutus_v1_encoding) = - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - let mut plutus_v1_arr = Vec::new(); - let len = raw.array_sz()?; - let plutus_v1_encoding = len.into(); - while match len { - cbor_event::LenSz::Len(n, _) => { - (plutus_v1_arr.len() as u64) < n - } - cbor_event::LenSz::Indefinite => true, - } { - if raw.cbor_type()? == cbor_event::Type::Special { - assert_eq!(raw.special()?, cbor_event::Special::Break); - break; - } - plutus_v1_arr.push(Int::deserialize(raw)?); - } - Ok((plutus_v1_arr, plutus_v1_encoding)) - })() - .map_err(|e| e.annotate("plutus_v1"))?; - plutus_v1 = Some(tmp_plutus_v1); - plutus_v1_encoding = tmp_plutus_v1_encoding; - plutus_v1_key_encoding = Some(key_enc); - orig_deser_order.push(0); - } - (1, key_enc) => { - if plutus_v2.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); - } - let (tmp_plutus_v2, tmp_plutus_v2_encoding) = - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - let mut plutus_v2_arr = Vec::new(); - let len = raw.array_sz()?; - let plutus_v2_encoding = len.into(); - while match len { - cbor_event::LenSz::Len(n, _) => { - (plutus_v2_arr.len() as u64) < n - } - cbor_event::LenSz::Indefinite => true, - } { - if raw.cbor_type()? == cbor_event::Type::Special { - assert_eq!(raw.special()?, cbor_event::Special::Break); - break; - } - plutus_v2_arr.push(Int::deserialize(raw)?); - } - Ok((plutus_v2_arr, plutus_v2_encoding)) - })() - .map_err(|e| e.annotate("plutus_v2"))?; - plutus_v2 = Some(tmp_plutus_v2); - plutus_v2_encoding = tmp_plutus_v2_encoding; - plutus_v2_key_encoding = Some(key_enc); - orig_deser_order.push(1); - } - (2, key_enc) => { - if plutus_v3.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); - } - let (tmp_plutus_v3, tmp_plutus_v3_encoding) = - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - let mut plutus_v3_arr = Vec::new(); - let len = raw.array_sz()?; - let plutus_v3_encoding = len.into(); - while match len { - cbor_event::LenSz::Len(n, _) => { - (plutus_v3_arr.len() as u64) < n - } - cbor_event::LenSz::Indefinite => true, - } { - if raw.cbor_type()? == cbor_event::Type::Special { - assert_eq!(raw.special()?, cbor_event::Special::Break); - break; - } - plutus_v3_arr.push(Int::deserialize(raw)?); - } - Ok((plutus_v3_arr, plutus_v3_encoding)) - })() - .map_err(|e| e.annotate("plutus_v3"))?; - plutus_v3 = Some(tmp_plutus_v3); - plutus_v3_encoding = tmp_plutus_v3_encoding; - plutus_v3_key_encoding = Some(key_enc); - orig_deser_order.push(2); - } - (unknown_key, _enc) => { - return Err( - DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into() - ) - } - }, - cbor_event::Type::Text => { - return Err(DeserializeFailure::UnknownKey(Key::Str(raw.text()?)).into()) + if raw.cbor_type()? == cbor_event::Type::Special { + assert_eq!(raw.special()?, cbor_event::Special::Break); + break; + } + let (inner_value_elem, inner_value_elem_encoding) = match raw.cbor_type()? { + cbor_event::Type::UnsignedInteger => { + let (x, enc) = raw.unsigned_integer_sz()?; + (x as i64, Some(enc)) } - cbor_event::Type::Special => match len { - cbor_event::LenSz::Len(_, _) => { - return Err(DeserializeFailure::BreakInDefiniteLen.into()) - } - cbor_event::LenSz::Indefinite => match raw.special()? { - cbor_event::Special::Break => break, - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - }, - other_type => { - return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()) + _ => { + let (x, enc) = raw.negative_integer_sz()?; + (x as i64, Some(enc)) } - } - read += 1; + }; + inner_value_arr.push(inner_value_elem); + inner_value_elem_encodings.push(inner_value_elem_encoding); } - read_len.finish()?; - Ok(Self { - plutus_v1, - plutus_v2, - plutus_v3, - encodings: Some(CostModelsEncoding { - len_encoding, - orig_deser_order, - plutus_v1_key_encoding, - plutus_v1_encoding, - plutus_v2_key_encoding, - plutus_v2_encoding, - plutus_v3_key_encoding, - plutus_v3_encoding, - }), - }) - })() - .map_err(|e| e.annotate("CostModels")) + let (inner_value, inner_value_encoding, inner_value_elem_encodings) = ( + inner_value_arr, + inner_value_encoding, + inner_value_elem_encodings, + ); + if inner_table.insert(inner_key, inner_value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(inner_key.into())).into()); + } + inner_key_encodings.insert(inner_key, inner_key_encoding); + inner_value_encodings.insert( + inner_key, + (inner_value_encoding, inner_value_elem_encodings), + ); + } + let (inner, inner_encoding, inner_key_encodings, inner_value_encodings) = ( + inner_table, + inner_encoding, + inner_key_encodings, + inner_value_encodings, + ); + Ok(Self { + inner, + encodings: Some(CostModelsEncoding { + inner_encoding, + inner_key_encodings, + inner_value_encodings, + }), + }) } } diff --git a/chain/rust/src/plutus/utils.rs b/chain/rust/src/plutus/utils.rs index 86a43d06..d85bafce 100644 --- a/chain/rust/src/plutus/utils.rs +++ b/chain/rust/src/plutus/utils.rs @@ -9,10 +9,10 @@ use cbor_event::de::Deserializer; use cbor_event::se::Serializer; use cml_core::ordered_hash_map::OrderedHashMap; use cml_core::serialization::*; -use cml_core::{error::*, Int}; +use cml_core::error::*; use cml_crypto::ScriptHash; use itertools::Itertools; -use std::collections::BTreeMap; +use std::convert::{TryFrom, TryInto}; use std::io::{BufRead, Seek, Write}; impl serde::Serialize for PlutusData { @@ -326,17 +326,6 @@ impl Deserialize for ConstrPlutusData { } impl CostModels { - pub fn as_map(&self) -> BTreeMap { - let mut map = BTreeMap::new(); - if let Some(v1_costs) = &self.plutus_v1 { - map.insert(Language::PlutusV1, &v1_costs[..]); - } - if let Some(v2_costs) = &self.plutus_v2 { - map.insert(Language::PlutusV1, &v2_costs[..]); - } - map - } - pub(crate) fn language_views_encoding(&self) -> Result, cbor_event::Error> { // ; language views CDDL: // ; { * language => script_integrity_data } @@ -354,52 +343,78 @@ impl CostModels { let mut serializer = Serializer::new_vec(); // as canonical encodings are used, we odn't need to check the keys' bytes encodings // and can order this statically. - serializer.write_map(cbor_event::Len::Len( - if self.plutus_v1.is_some() { 1 } else { 0 } - + if self.plutus_v2.is_some() { 1 } else { 0 }, - ))?; - if let Some(v1_costs) = &self.plutus_v1 { - // For PlutusV1 (language id 0), the language view is the following: - // * the value of costmdls map at key 0 is encoded as an indefinite length - // list and the result is encoded as a bytestring. (our apologies) - // * the language ID tag is also encoded twice. first as a uint then as - // a bytestring. (our apologies) - let v1_key_canonical_bytes = [0]; - serializer.write_bytes(v1_key_canonical_bytes)?; - // Due to a bug in the cardano-node input-output-hk/cardano-ledger-specs/issues/2512 - // we must use indefinite length serialization in this inner bytestring to match it - let mut cost_model_serializer = Serializer::new_vec(); - cost_model_serializer.write_array(cbor_event::Len::Indefinite)?; - for cost in v1_costs { - cost.serialize(&mut cost_model_serializer, true)?; - } - cost_model_serializer.write_special(cbor_event::Special::Break)?; - serializer.write_bytes(cost_model_serializer.finalize())?; - } - if let Some(v2_costs) = &self.plutus_v2 { - // For PlutusV2 (language id 1), the language view is the following: - // * the value of costmdls map at key 1 is encoded as an definite length list. - let v2_key = 1; - serializer.write_unsigned_integer(v2_key)?; - serializer.write_array(cbor_event::Len::Len(v2_costs.len() as u64))?; - for cost in v2_costs { - cost.serialize(&mut serializer, true)?; - } - } - if let Some(v3_costs) = &self.plutus_v3 { - // For PlutusV3 (language id 2), the language view is the following: - // * the value of costmdls map at key 2 is encoded as a definite length list. - let v3_key = 2; - serializer.write_unsigned_integer(v3_key)?; - serializer.write_array(cbor_event::Len::Len(v3_costs.len() as u64))?; - for cost in v3_costs { - cost.serialize(&mut serializer, true)?; + // Due to PlutusV1 bug we can't re-use the generated serialization code + // as it requires it to be encoded as CBOR bytes in CBOR + serializer.write_map(cbor_event::Len::Len(self.inner.len() as u64))?; + for (language, costs) in self.inner.iter() { + match (*language).try_into() { + Ok(Language::PlutusV1) => { + // For PlutusV1 (language id 0), the language view is the following: + // * the value of costmdls map at key 0 is encoded as an indefinite length + // list and the result is encoded as a bytestring. (our apologies) + // * the language ID tag is also encoded twice. first as a uint then as + // a bytestring. (our apologies) + let v1_key_canonical_bytes = [0]; + serializer.write_bytes(v1_key_canonical_bytes)?; + // Due to a bug in the cardano-node input-output-hk/cardano-ledger-specs/issues/2512 + // we must use indefinite length serialization in this inner bytestring to match it + let mut cost_model_serializer = Serializer::new_vec(); + cost_model_serializer.write_array(cbor_event::Len::Indefinite)?; + for cost in costs { + if *cost >= 0 { + cost_model_serializer.write_unsigned_integer(cost.unsigned_abs())?; + } else { + cost_model_serializer.write_negative_integer(*cost)?; + } + } + cost_model_serializer.write_special(cbor_event::Special::Break)?; + serializer.write_bytes(cost_model_serializer.finalize())?; + }, + _ => { + // For PlutusV2 (language id 1), the language view is the following: + // * the value of costmdls map at key 1 is encoded as an definite length list. + // For PlutusV3 (language id 2), the language view is the following: + // * the value of costmdls map at key 2 is encoded as a definite length list. + // + // We will assume all other languages also follow this and that the non-canonical + // encoding is just a bug pertaining to Plutus V1 + serializer.write_unsigned_integer(*language)?; + serializer.write_array(cbor_event::Len::Len(costs.len() as u64))?; + for cost in costs { + if *cost >= 0 { + serializer.write_unsigned_integer(cost.unsigned_abs())?; + } else { + serializer.write_negative_integer(*cost)?; + } + } + }, } } Ok(serializer.finalize()) } } +impl Default for CostModels { + fn default() -> Self { + CostModels { + inner: OrderedHashMap::default(), + encodings: None, + } + } +} + +impl AsRef>> for CostModels { + fn as_ref(&self) -> &OrderedHashMap> { + &self.inner + } +} + +impl AsMut>> for CostModels { + fn as_mut(&mut self) -> &mut OrderedHashMap> { + &mut self.inner + } +} + /// Version-agnostic Plutus script #[derive(Clone, Debug)] pub enum PlutusScript { @@ -489,6 +504,33 @@ pub fn compute_total_ex_units(redeemers: &[LegacyRedeemer]) -> Result for Language { + type Error = DeserializeError; + + fn try_from(language: u64) -> Result { + match language { + 0 => Ok(Language::PlutusV1), + 1 => Ok(Language::PlutusV2), + 2 => Ok(Language::PlutusV3), + _ => Err(DeserializeFailure::OutOfRange { + found: language as usize, + min: 0, + max: 2, + }.into()) + } + } +} + +impl From for u64 { + fn from(language: Language) -> u64 { + match language { + Language::PlutusV1 => 0, + Language::PlutusV2 => 1, + Language::PlutusV3 => 2, + } + } +} + #[derive(Clone, Debug, Default, derivative::Derivative)] #[derivative(Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct PlutusMap { diff --git a/chain/wasm/src/plutus/mod.rs b/chain/wasm/src/plutus/mod.rs index ccf194fd..33df2ea2 100644 --- a/chain/wasm/src/plutus/mod.rs +++ b/chain/wasm/src/plutus/mod.rs @@ -5,13 +5,26 @@ pub mod utils; use crate::utils::BigInteger; -use super::{IntList, PlutusDataList, SubCoin}; +use super::{PlutusDataList, SubCoin}; use crate::{LegacyRedeemerList, MapRedeemerKeyToRedeemerVal}; pub use cml_chain::plutus::{Language, RedeemerTag}; -use cml_core_wasm::{impl_wasm_cbor_json_api, impl_wasm_conversions}; +use cml_core_wasm::{impl_wasm_cbor_json_api, impl_wasm_conversions, impl_wasm_map}; pub use utils::{ConstrPlutusData, PlutusMap}; use wasm_bindgen::prelude::{wasm_bindgen, JsError, JsValue}; +impl_wasm_map!( + u64, + Vec, + u64, + Vec, + Vec, + MapU64ToArrI64, + true, + true, + true, + false +); + #[derive(Clone, Debug)] #[wasm_bindgen] pub struct CostModels(cml_chain::plutus::CostModels); @@ -22,32 +35,8 @@ impl_wasm_conversions!(cml_chain::plutus::CostModels, CostModels); #[wasm_bindgen] impl CostModels { - pub fn set_plutus_v1(&mut self, plutus_v1: &IntList) { - self.0.plutus_v1 = Some(plutus_v1.clone().into()) - } - - pub fn plutus_v1(&self) -> Option { - self.0.plutus_v1.clone().map(std::convert::Into::into) - } - - pub fn set_plutus_v2(&mut self, plutus_v2: &IntList) { - self.0.plutus_v2 = Some(plutus_v2.clone().into()) - } - - pub fn plutus_v2(&self) -> Option { - self.0.plutus_v2.clone().map(std::convert::Into::into) - } - - pub fn set_plutus_v3(&mut self, plutus_v3: &IntList) { - self.0.plutus_v3 = Some(plutus_v3.clone().into()) - } - - pub fn plutus_v3(&self) -> Option { - self.0.plutus_v3.clone().map(std::convert::Into::into) - } - - pub fn new() -> Self { - Self(cml_chain::plutus::CostModels::new()) + pub fn inner(&self) -> MapU64ToArrI64 { + self.0.inner.clone().into() } } diff --git a/specs/conway/plutus.cddl b/specs/conway/plutus.cddl index f045cfbe..a0b85994 100644 --- a/specs/conway/plutus.cddl +++ b/specs/conway/plutus.cddl @@ -67,8 +67,9 @@ language = 0 ; @name plutus_v1 / 1 ; @name plutus_v2 / 2 ; @name plutus_v3 -cost_models = { - ? 0 : [ 166*166 int ], ; @name plutus_v1 - ? 1 : [ 175*175 int ], ; @name plutus_v2 - ? 2 : [ 233*233 int ], ; @name plutus_v3 -} +cost_models = { * uint => [* int64] } ; @newtype +;cost_models = { +; ? 0 : [ 166*166 int ], ; @name plutus_v1 +; ? 1 : [ 175*175 int ], ; @name plutus_v2 +; ? 2 : [ 233*233 int ], ; @name plutus_v3 +;} From 645aad01e844a7ca32561750bc89c4c63d3efc3f Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Mon, 17 Jun 2024 16:44:16 -0700 Subject: [PATCH 2/2] clippy --- chain/rust/src/builders/tx_builder.rs | 5 ++-- chain/rust/src/builders/witness_builder.rs | 6 ++--- chain/rust/src/crypto/hash.rs | 5 ++-- chain/rust/src/genesis/network_info.rs | 18 +++++++-------- chain/rust/src/plutus/mod.rs | 15 +++++++----- chain/rust/src/plutus/serialization.rs | 4 ++-- chain/rust/src/plutus/utils.rs | 27 ++++++++-------------- multi-era/rust/src/alonzo/utils.rs | 6 ++--- multi-era/rust/src/babbage/utils.rs | 8 +++---- 9 files changed, 43 insertions(+), 51 deletions(-) diff --git a/chain/rust/src/builders/tx_builder.rs b/chain/rust/src/builders/tx_builder.rs index 80d15cfb..d91b1981 100644 --- a/chain/rust/src/builders/tx_builder.rs +++ b/chain/rust/src/builders/tx_builder.rs @@ -720,13 +720,12 @@ impl TransactionBuilder { match &script_witness.script { PlutusScriptWitness::Ref(ref_script) => { - if self + if !self .witness_builders .witness_set_builder .required_wits .script_refs - .get(ref_script) - .is_none() + .contains(ref_script) { Err(TxBuilderError::RefScriptNotFound( *ref_script, diff --git a/chain/rust/src/builders/witness_builder.rs b/chain/rust/src/builders/witness_builder.rs index 3baebe67..299f9955 100644 --- a/chain/rust/src/builders/witness_builder.rs +++ b/chain/rust/src/builders/witness_builder.rs @@ -275,7 +275,7 @@ impl TransactionWitnessSetBuilder { pub fn get_native_script(&self) -> Vec { self.scripts .iter() - .filter(|entry| self.required_wits.script_refs.get(entry.0).is_none()) + .filter(|entry| !self.required_wits.script_refs.contains(entry.0)) .fold( Vec::::new(), |mut acc, script| match &script.1 { @@ -291,7 +291,7 @@ impl TransactionWitnessSetBuilder { pub fn get_plutus_v1_script(&self) -> Vec { self.scripts .iter() - .filter(|entry| self.required_wits.script_refs.get(entry.0).is_none()) + .filter(|entry| !self.required_wits.script_refs.contains(entry.0)) .fold( Vec::::new(), |mut acc, script| match &script.1 { @@ -307,7 +307,7 @@ impl TransactionWitnessSetBuilder { pub fn get_plutus_v2_script(&self) -> Vec { self.scripts .iter() - .filter(|entry| self.required_wits.script_refs.get(entry.0).is_none()) + .filter(|entry| !self.required_wits.script_refs.contains(entry.0)) .fold( Vec::::new(), |mut acc, script| match &script.1 { diff --git a/chain/rust/src/crypto/hash.rs b/chain/rust/src/crypto/hash.rs index ceecab1c..c8fbe5f0 100644 --- a/chain/rust/src/crypto/hash.rs +++ b/chain/rust/src/crypto/hash.rs @@ -91,9 +91,10 @@ pub fn calc_script_data_hash( let mut required_costmdls = CostModels::default(); for lang in used_langs { required_costmdls.inner.insert( - *lang as u64, + *lang as u64, cost_models - .inner.get(&(*lang).into()) + .inner + .get(&(*lang).into()) .ok_or(ScriptDataHashError::MissingCostModel(*lang))? .clone(), ); diff --git a/chain/rust/src/genesis/network_info.rs b/chain/rust/src/genesis/network_info.rs index e408c461..6e942d91 100644 --- a/chain/rust/src/genesis/network_info.rs +++ b/chain/rust/src/genesis/network_info.rs @@ -1,9 +1,10 @@ -use crate::{byron::ProtocolMagic, plutus::{CostModels, Language}}; -use cml_core::{ - network::{ - BYRON_MAINNET_NETWORK_MAGIC, BYRON_TESTNET_NETWORK_MAGIC, PREPROD_NETWORK_MAGIC, - PREVIEW_NETWORK_MAGIC, SANCHO_TESTNET_NETWORK_MAGIC, - }, +use crate::{ + byron::ProtocolMagic, + plutus::{CostModels, Language}, +}; +use cml_core::network::{ + BYRON_MAINNET_NETWORK_MAGIC, BYRON_TESTNET_NETWORK_MAGIC, PREPROD_NETWORK_MAGIC, + PREVIEW_NETWORK_MAGIC, SANCHO_TESTNET_NETWORK_MAGIC, }; #[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -81,9 +82,6 @@ pub fn plutus_alonzo_cost_models() -> CostModels { ]; let mut res = CostModels::default(); - res.inner.insert( - Language::PlutusV1 as u64, - ops - ); + res.inner.insert(Language::PlutusV1 as u64, ops); res } diff --git a/chain/rust/src/plutus/mod.rs b/chain/rust/src/plutus/mod.rs index 267ce228..e1941b42 100644 --- a/chain/rust/src/plutus/mod.rs +++ b/chain/rust/src/plutus/mod.rs @@ -22,7 +22,7 @@ use cml_crypto::{blake2b256, DatumHash}; pub use utils::{ConstrPlutusData, PlutusMap, PlutusScript}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct CostModels { pub inner: OrderedHashMap>, pub encodings: Option, @@ -39,7 +39,7 @@ impl CostModels { impl From>> for CostModels { fn from(inner: OrderedHashMap>) -> Self { - CostModels::new(inner.clone().into()) + CostModels::new(inner.clone()) } } @@ -51,17 +51,20 @@ impl From for OrderedHashMap> { impl serde::Serialize for CostModels { fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer, + where + S: serde::Serializer, { self.inner.serialize(serializer) } } -impl<'de> serde :: de :: Deserialize < 'de > for CostModels { +impl<'de> serde::de::Deserialize<'de> for CostModels { fn deserialize(deserializer: D) -> Result - where D: serde :: de :: Deserializer < 'de >, + where + D: serde::de::Deserializer<'de>, { - let inner = > as serde::de::Deserialize>::deserialize(deserializer)?; + let inner = + > as serde::de::Deserialize>::deserialize(deserializer)?; Ok(Self::new(inner)) } } diff --git a/chain/rust/src/plutus/serialization.rs b/chain/rust/src/plutus/serialization.rs index 9c229084..c7fecb5c 100644 --- a/chain/rust/src/plutus/serialization.rs +++ b/chain/rust/src/plutus/serialization.rs @@ -74,7 +74,7 @@ impl Serialize for CostModels { serializer.write_negative_integer_sz( *element as i128, fit_sz( - (*element + 1).abs() as u64, + (*element + 1).unsigned_abs(), inner_value_elem_encoding, force_canonical, ), @@ -139,7 +139,7 @@ impl Deserialize for CostModels { inner_value_elem_encodings, ); if inner_table.insert(inner_key, inner_value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(inner_key.into())).into()); + return Err(DeserializeFailure::DuplicateKey(Key::Uint(inner_key)).into()); } inner_key_encodings.insert(inner_key, inner_key_encoding); inner_value_encodings.insert( diff --git a/chain/rust/src/plutus/utils.rs b/chain/rust/src/plutus/utils.rs index d85bafce..ae228c23 100644 --- a/chain/rust/src/plutus/utils.rs +++ b/chain/rust/src/plutus/utils.rs @@ -7,9 +7,9 @@ use crate::json::plutus_datums::{ }; use cbor_event::de::Deserializer; use cbor_event::se::Serializer; +use cml_core::error::*; use cml_core::ordered_hash_map::OrderedHashMap; use cml_core::serialization::*; -use cml_core::error::*; use cml_crypto::ScriptHash; use itertools::Itertools; use std::convert::{TryFrom, TryInto}; @@ -369,7 +369,7 @@ impl CostModels { } cost_model_serializer.write_special(cbor_event::Special::Break)?; serializer.write_bytes(cost_model_serializer.finalize())?; - }, + } _ => { // For PlutusV2 (language id 1), the language view is the following: // * the value of costmdls map at key 1 is encoded as an definite length list. @@ -387,22 +387,13 @@ impl CostModels { serializer.write_negative_integer(*cost)?; } } - }, + } } } Ok(serializer.finalize()) } } -impl Default for CostModels { - fn default() -> Self { - CostModels { - inner: OrderedHashMap::default(), - encodings: None, - } - } -} - impl AsRef>> for CostModels { fn as_ref(&self) -> &OrderedHashMap> { &self.inner @@ -516,7 +507,8 @@ impl TryFrom for Language { found: language as usize, min: 0, max: 2, - }.into()) + } + .into()), } } } @@ -721,12 +713,11 @@ impl Redeemers { #[cfg(test)] mod tests { - use crate::plutus::CostModels; - use cml_core::Int; + use crate::plutus::{CostModels, Language}; #[test] pub fn test_cost_model() { - let arr = vec![ + let v1_costs = vec![ 197209, 0, 1, 1, 396231, 621, 0, 1, 150000, 1000, 0, 1, 150000, 32, 2477736, 29175, 4, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 100, 100, 29773, 100, 150000, 32, 150000, 32, 150000, 32, 150000, 1000, 0, 1, 150000, 32, 150000, @@ -739,8 +730,8 @@ mod tests { 1, 150000, 32, 197209, 0, 1, 1, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 3345831, 1, 1, ]; - let mut cms = CostModels::new(); - cms.plutus_v1 = Some(arr.iter().map(|&i| Int::new_uint(i)).collect()); + let mut cms = CostModels::default(); + cms.inner.insert(Language::PlutusV1.into(), v1_costs); assert_eq!( hex::encode(cms.language_views_encoding().unwrap()), "a141005901d59f1a000302590001011a00060bc719026d00011a000249f01903e800011a000249f018201a0025cea81971f70419744d186419744d186419744d186419744d186419744d186419744d18641864186419744d18641a000249f018201a000249f018201a000249f018201a000249f01903e800011a000249f018201a000249f01903e800081a000242201a00067e2318760001011a000249f01903e800081a000249f01a0001b79818f7011a000249f0192710011a0002155e19052e011903e81a000249f01903e8011a000249f018201a000249f018201a000249f0182001011a000249f0011a000249f0041a000194af18f8011a000194af18f8011a0002377c190556011a0002bdea1901f1011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000242201a00067e23187600010119f04c192bd200011a000249f018201a000242201a00067e2318760001011a000242201a00067e2318760001011a0025cea81971f704001a000141bb041a000249f019138800011a000249f018201a000302590001011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a00330da70101ff" diff --git a/multi-era/rust/src/alonzo/utils.rs b/multi-era/rust/src/alonzo/utils.rs index 802d8207..43a6ea18 100644 --- a/multi-era/rust/src/alonzo/utils.rs +++ b/multi-era/rust/src/alonzo/utils.rs @@ -25,9 +25,9 @@ impl From for AuxiliaryData { AlonzoAuxiliaryData::ShelleyMA(md) => AuxiliaryData::new_shelley_m_a(md.clone()), AlonzoAuxiliaryData::Alonzo(md) => AuxiliaryData::new_conway({ let mut conway = ConwayFormatAuxData::new(); - conway.metadata = md.metadata.clone(); - conway.native_scripts = md.native_scripts.clone(); - conway.plutus_v1_scripts = md.plutus_v1_scripts.clone(); + conway.metadata.clone_from(&md.metadata); + conway.native_scripts.clone_from(&md.native_scripts); + conway.plutus_v1_scripts.clone_from(&md.plutus_v1_scripts); conway }), } diff --git a/multi-era/rust/src/babbage/utils.rs b/multi-era/rust/src/babbage/utils.rs index 9d060ecd..050a97d2 100644 --- a/multi-era/rust/src/babbage/utils.rs +++ b/multi-era/rust/src/babbage/utils.rs @@ -66,10 +66,10 @@ impl From for AuxiliaryData { BabbageAuxiliaryData::ShelleyMA(md) => AuxiliaryData::new_shelley_m_a(md.clone()), BabbageAuxiliaryData::Babbage(md) => AuxiliaryData::new_conway({ let mut conway = ConwayFormatAuxData::new(); - conway.metadata = md.metadata.clone(); - conway.native_scripts = md.native_scripts.clone(); - conway.plutus_v1_scripts = md.plutus_v1_scripts.clone(); - conway.plutus_v2_scripts = md.plutus_v2_scripts.clone(); + conway.metadata.clone_from(&md.metadata); + conway.native_scripts.clone_from(&md.native_scripts); + conway.plutus_v1_scripts.clone_from(&md.plutus_v1_scripts); + conway.plutus_v2_scripts.clone_from(&md.plutus_v2_scripts); conway }), }