diff --git a/chain/rust/Cargo.toml b/chain/rust/Cargo.toml index d12cb741..5b7f5f73 100644 --- a/chain/rust/Cargo.toml +++ b/chain/rust/Cargo.toml @@ -13,6 +13,9 @@ keywords = ["cardano"] [lib] crate-type = ["cdylib", "rlib"] +[features] +used_from_wasm = ["wasm-bindgen"] + [dependencies] cml-core = { "path" = "../../core/rust", version = "5.3.1" } cml-crypto = { "path" = "../../crypto/rust", version = "5.3.1" } @@ -32,29 +35,17 @@ fraction = "0.10.0" base64 = "0.21.5" num-bigint = "0.4.0" num-integer = "0.1.45" -#rand_os = "0.1" thiserror = "1.0.37" num = "0.4" unicode-segmentation = "1.10.1" -# These can be removed if we make wasm bindings for ALL functionality here. -# This was not done right now as there is a lot of existing legacy code e.g. -# for Byron that might need to be used from WASM and might not. -# We can remove this dependency when that is decided. -# -# The other use-case here is enums. Without this two enums would need to be defined -# despite wasm_bindgen supporting C-style enums (with non-negative values) 100% -# This could possibly be resolved with macros but maybe not. +serde-aux = "4.5.0" +chrono = "0.4.38" # non-wasm -#[target.'cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))'.dependencies] -#rand_os = "0.1" -#noop_proc_macro = "0.3.0" +noop_proc_macro = { version = "0.3.0", optional = false } # wasm -#[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] -wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] } -#rand_os = { version = "0.1", features = ["wasm-bindgen"] } -#js-sys = "=0.3.59" +wasm-bindgen = { version = "0.2.87", optional = true } [dev-dependencies] diff --git a/chain/rust/src/address.rs b/chain/rust/src/address.rs index cf26c16b..8f9a6253 100644 --- a/chain/rust/src/address.rs +++ b/chain/rust/src/address.rs @@ -7,7 +7,9 @@ use schemars::JsonSchema; use std::convert::{TryFrom, TryInto}; use std::io::{BufRead, Write}; -// for enums +#[cfg(not(feature = "used_from_wasm"))] +use noop_proc_macro::wasm_bindgen; +#[cfg(feature = "used_from_wasm")] use wasm_bindgen::prelude::wasm_bindgen; use cml_crypto::{Ed25519KeyHash, ScriptHash}; diff --git a/chain/rust/src/assets/mod.rs b/chain/rust/src/assets/mod.rs index adc3911b..1a6aee2f 100644 --- a/chain/rust/src/assets/mod.rs +++ b/chain/rust/src/assets/mod.rs @@ -12,6 +12,7 @@ use cml_core::error::*; use std::convert::TryFrom; +/// Use TryFrom<&str> / TryInto<&str> for utf8 text conversion and RawBytesEncoding for direct bytes access #[derive(Clone, Debug, derivative::Derivative)] #[derivative(Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct AssetName { @@ -26,10 +27,6 @@ pub struct AssetName { } impl AssetName { - pub fn get(&self) -> &Vec { - &self.inner - } - pub fn new(inner: Vec) -> Result { if inner.len() > 32 { return Err(DeserializeError::new( diff --git a/chain/rust/src/assets/utils.rs b/chain/rust/src/assets/utils.rs index c3d6a56f..39d83b19 100644 --- a/chain/rust/src/assets/utils.rs +++ b/chain/rust/src/assets/utils.rs @@ -51,7 +51,17 @@ impl<'a> TryInto<&'a str> for &'a AssetName { type Error = std::str::Utf8Error; fn try_into(self) -> Result<&'a str, Self::Error> { - std::str::from_utf8(self.get()) + std::str::from_utf8(self.to_raw_bytes()) + } +} + +impl RawBytesEncoding for AssetName { + fn to_raw_bytes(&self) -> &[u8] { + self.inner.as_ref() + } + + fn from_raw_bytes(bytes: &[u8]) -> Result { + Self::new(bytes.to_vec()) } } @@ -71,7 +81,7 @@ impl std::fmt::Debug for AssetBundle { for (pid, assets) in self.0.iter() { let pid_hex = hex::encode(pid.to_raw_bytes()); for (an, val) in assets.iter() { - let an_hex = hex::encode(an.get()); + let an_hex = hex::encode(an.to_raw_bytes()); let an_name = if an_hex.len() > 8 { format!( "{}..{}", diff --git a/chain/rust/src/auxdata/cbor_encodings.rs b/chain/rust/src/auxdata/cbor_encodings.rs index 1dd5616c..d0775878 100644 --- a/chain/rust/src/auxdata/cbor_encodings.rs +++ b/chain/rust/src/auxdata/cbor_encodings.rs @@ -20,7 +20,7 @@ pub struct ConwayFormatAuxDataEncoding { } #[derive(Clone, Debug, Default)] -pub struct ShelleyMaFormatAuxDataEncoding { +pub struct ShelleyMAFormatAuxDataEncoding { pub len_encoding: LenEncoding, pub auxiliary_scripts_encoding: LenEncoding, } diff --git a/chain/rust/src/auxdata/mod.rs b/chain/rust/src/auxdata/mod.rs index ace3e65b..fc024711 100644 --- a/chain/rust/src/auxdata/mod.rs +++ b/chain/rust/src/auxdata/mod.rs @@ -8,14 +8,14 @@ pub mod utils; use crate::plutus::{PlutusV1Script, PlutusV2Script, PlutusV3Script}; use crate::transaction::NativeScript; -use cbor_encodings::{ConwayFormatAuxDataEncoding, ShelleyMaFormatAuxDataEncoding}; +use cbor_encodings::{ConwayFormatAuxDataEncoding, ShelleyMAFormatAuxDataEncoding}; pub use metadata::*; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub enum AuxiliaryData { Shelley(ShelleyFormatAuxData), - ShelleyMA(ShelleyMaFormatAuxData), + ShelleyMA(ShelleyMAFormatAuxData), Conway(ConwayFormatAuxData), } @@ -24,8 +24,8 @@ impl AuxiliaryData { Self::Shelley(shelley) } - pub fn new_shelley_m_a(shelley_m_a: ShelleyMaFormatAuxData) -> Self { - Self::ShelleyMA(shelley_m_a) + pub fn new_shelley_ma(shelley_ma: ShelleyMAFormatAuxData) -> Self { + Self::ShelleyMA(shelley_ma) } pub fn new_conway(conway: ConwayFormatAuxData) -> Self { @@ -66,14 +66,14 @@ impl Default for ConwayFormatAuxData { pub type ShelleyFormatAuxData = Metadata; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct ShelleyMaFormatAuxData { +pub struct ShelleyMAFormatAuxData { pub transaction_metadata: Metadata, pub auxiliary_scripts: Vec, #[serde(skip)] - pub encodings: Option, + pub encodings: Option, } -impl ShelleyMaFormatAuxData { +impl ShelleyMAFormatAuxData { pub fn new(transaction_metadata: Metadata, auxiliary_scripts: Vec) -> Self { Self { transaction_metadata, diff --git a/chain/rust/src/auxdata/serialization.rs b/chain/rust/src/auxdata/serialization.rs index a54ee397..8ec38f50 100644 --- a/chain/rust/src/auxdata/serialization.rs +++ b/chain/rust/src/auxdata/serialization.rs @@ -19,8 +19,8 @@ impl Serialize for AuxiliaryData { ) -> cbor_event::Result<&'se mut Serializer> { match self { AuxiliaryData::Shelley(shelley) => shelley.serialize(serializer, force_canonical), - AuxiliaryData::ShelleyMA(shelley_m_a) => { - shelley_m_a.serialize(serializer, force_canonical) + AuxiliaryData::ShelleyMA(shelley_ma) => { + shelley_ma.serialize(serializer, force_canonical) } AuxiliaryData::Conway(conway) => conway.serialize(serializer, force_canonical), } @@ -43,9 +43,9 @@ impl Deserialize for AuxiliaryData { } }; let deser_variant: Result<_, DeserializeError> = - ShelleyMaFormatAuxData::deserialize(raw); + ShelleyMAFormatAuxData::deserialize(raw); match deser_variant { - Ok(shelley_m_a) => return Ok(Self::ShelleyMA(shelley_m_a)), + Ok(shelley_ma) => return Ok(Self::ShelleyMA(shelley_ma)), Err(e) => { errs.push(e.annotate("ShelleyMA")); raw.as_mut_ref() @@ -513,7 +513,7 @@ impl Deserialize for ConwayFormatAuxData { } } -impl Serialize for ShelleyMaFormatAuxData { +impl Serialize for ShelleyMAFormatAuxData { fn serialize<'se, W: Write>( &self, serializer: &'se mut Serializer, @@ -551,7 +551,7 @@ impl Serialize for ShelleyMaFormatAuxData { } } -impl Deserialize for ShelleyMaFormatAuxData { +impl Deserialize for ShelleyMAFormatAuxData { fn deserialize(raw: &mut Deserializer) -> Result { let len = raw.array_sz()?; let len_encoding: LenEncoding = len.into(); @@ -586,15 +586,15 @@ impl Deserialize for ShelleyMaFormatAuxData { _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, } - Ok(ShelleyMaFormatAuxData { + Ok(ShelleyMAFormatAuxData { transaction_metadata, auxiliary_scripts, - encodings: Some(ShelleyMaFormatAuxDataEncoding { + encodings: Some(ShelleyMAFormatAuxDataEncoding { len_encoding, auxiliary_scripts_encoding, }), }) })() - .map_err(|e| e.annotate("ShelleyMaFormatAuxData")) + .map_err(|e| e.annotate("ShelleyMAFormatAuxData")) } } diff --git a/chain/rust/src/auxdata/utils.rs b/chain/rust/src/auxdata/utils.rs index ffaae856..b31d8925 100644 --- a/chain/rust/src/auxdata/utils.rs +++ b/chain/rust/src/auxdata/utils.rs @@ -5,7 +5,7 @@ use crate::{ transaction::NativeScript, }; -use super::{AuxiliaryData, ConwayFormatAuxData, ShelleyMaFormatAuxData}; +use super::{AuxiliaryData, ConwayFormatAuxData, ShelleyMAFormatAuxData}; impl AuxiliaryData { pub fn new() -> Self { @@ -81,7 +81,7 @@ impl AuxiliaryData { pub fn add_native_scripts(&mut self, scripts: Vec) { match self { Self::Shelley(shelley) => { - *self = Self::ShelleyMA(ShelleyMaFormatAuxData::new(shelley.clone(), scripts)); + *self = Self::ShelleyMA(ShelleyMAFormatAuxData::new(shelley.clone(), scripts)); } Self::ShelleyMA(shelley_ma) => { shelley_ma.auxiliary_scripts.extend(scripts); diff --git a/chain/rust/src/builders/certificate_builder.rs b/chain/rust/src/builders/certificate_builder.rs index f20701e1..677ccd3d 100644 --- a/chain/rust/src/builders/certificate_builder.rs +++ b/chain/rust/src/builders/certificate_builder.rs @@ -183,9 +183,14 @@ pub fn add_cert_vkeys( vkeys.insert(*hash); } }, - Certificate::RegDrepCert(_cert) => { - // does not need a witness - } + Certificate::RegDrepCert(cert) => match &cert.drep_credential { + StakeCredential::Script { hash, .. } => { + return Err(CertBuilderError::ExpectedKeyHash(*hash)) + } + StakeCredential::PubKey { hash, .. } => { + vkeys.insert(*hash); + } + }, Certificate::UnregDrepCert(cert) => match &cert.drep_credential { StakeCredential::Script { hash, .. } => { return Err(CertBuilderError::ExpectedKeyHash(*hash)) diff --git a/chain/rust/src/builders/input_builder.rs b/chain/rust/src/builders/input_builder.rs index 5687f097..b17f7d03 100644 --- a/chain/rust/src/builders/input_builder.rs +++ b/chain/rust/src/builders/input_builder.rs @@ -2,6 +2,7 @@ use crate::builders::witness_builder::{InputAggregateWitnessData, PartialPlutusW use super::{ tx_builder::TransactionUnspentOutput, + utils::required_wits_from_required_signers, witness_builder::{NativeScriptWitnessInfo, RequiredWitnessSet}, }; @@ -138,11 +139,7 @@ impl SingleInputBuilder { required_signers: RequiredSigners, datum: Option, ) -> Result { - let mut required_wits = RequiredWitnessSet::default(); - required_signers - .as_ref() - .iter() - .for_each(|required_signer| required_wits.add_vkey_key_hash(*required_signer)); + let mut required_wits = required_wits_from_required_signers(&required_signers); input_required_wits(&self.utxo_info, &mut required_wits); let mut required_wits_left = required_wits.clone(); diff --git a/chain/rust/src/builders/mod.rs b/chain/rust/src/builders/mod.rs index 30911f48..13f63202 100644 --- a/chain/rust/src/builders/mod.rs +++ b/chain/rust/src/builders/mod.rs @@ -2,7 +2,10 @@ pub mod certificate_builder; pub mod input_builder; pub mod mint_builder; pub mod output_builder; +pub mod proposal_builder; pub mod redeemer_builder; pub mod tx_builder; +mod utils; +pub mod vote_builder; pub mod withdrawal_builder; pub mod witness_builder; diff --git a/chain/rust/src/builders/proposal_builder.rs b/chain/rust/src/builders/proposal_builder.rs new file mode 100644 index 00000000..93f7739c --- /dev/null +++ b/chain/rust/src/builders/proposal_builder.rs @@ -0,0 +1,165 @@ +use crate::{ + crypto::hash::hash_plutus_data, governance::ProposalProcedure, plutus::PlutusData, + transaction::NativeScript, RequiredSigners, +}; + +use super::{ + utils::required_wits_from_required_signers, + witness_builder::{ + InputAggregateWitnessData, NativeScriptWitnessInfo, PartialPlutusWitness, + RequiredWitnessSet, + }, +}; + +#[derive(Debug, thiserror::Error)] +pub enum ProposalBuilderError { + #[error("Proposal uses script. Call with_plutus_proposal() instead.")] + ProposalIsScript, + #[error("Proposal uses key hash. Call with_proposal() instead.")] + ProposalIsKeyHash, + #[error("Missing the following witnesses for the input: {0:?}")] + MissingWitnesses(Box), +} + +#[derive(Clone, Debug, Default)] +pub struct ProposalBuilderResult { + pub proposals: Vec, + pub required_wits: RequiredWitnessSet, + pub aggregate_witnesses: Vec, +} + +#[derive(Clone, Debug)] +pub struct ProposalBuilder { + result: ProposalBuilderResult, +} + +impl Default for ProposalBuilder { + fn default() -> Self { + Self::new() + } +} + +impl ProposalBuilder { + pub fn new() -> Self { + Self { + result: ProposalBuilderResult::default(), + } + } + + pub fn with_proposal( + mut self, + proposal: ProposalProcedure, + ) -> Result { + if proposal.gov_action.script_hash().is_some() { + return Err(ProposalBuilderError::ProposalIsScript); + } + + self.result.proposals.push(proposal.clone()); + + Ok(self) + } + + pub fn with_native_script_proposal( + mut self, + proposal: ProposalProcedure, + native_script: NativeScript, + witness_info: NativeScriptWitnessInfo, + ) -> Result { + if let Some(script_hash) = proposal.gov_action.script_hash() { + if *script_hash != native_script.hash() { + let mut err_req_wits = RequiredWitnessSet::new(); + err_req_wits.add_script_hash(*script_hash); + return Err(ProposalBuilderError::MissingWitnesses(Box::new( + err_req_wits, + ))); + } + self.result.required_wits.add_script_hash(*script_hash); + } else { + return Err(ProposalBuilderError::ProposalIsKeyHash); + } + + self.result.proposals.push(proposal); + + self.result + .aggregate_witnesses + .push(InputAggregateWitnessData::NativeScript( + native_script, + witness_info, + )); + + Ok(self) + } + + pub fn with_plutus_proposal( + self, + proposal: ProposalProcedure, + partial_witness: PartialPlutusWitness, + required_signers: RequiredSigners, + datum: PlutusData, + ) -> Result { + self.with_plutus_proposal_impl(proposal, partial_witness, required_signers, Some(datum)) + } + + pub fn with_plutus_proposal_inline_datum( + self, + proposal: ProposalProcedure, + partial_witness: PartialPlutusWitness, + required_signers: RequiredSigners, + ) -> Result { + self.with_plutus_proposal_impl(proposal, partial_witness, required_signers, None) + } + + fn with_plutus_proposal_impl( + mut self, + proposal: ProposalProcedure, + partial_witness: PartialPlutusWitness, + required_signers: RequiredSigners, + datum: Option, + ) -> Result { + let mut required_wits = required_wits_from_required_signers(&required_signers); + if let Some(script_hash) = proposal.gov_action.script_hash() { + required_wits.add_script_hash(*script_hash); + } else { + return Err(ProposalBuilderError::ProposalIsKeyHash); + } + + let mut required_wits_left = required_wits.clone(); + + // no way to know these at this time + required_wits_left.vkeys.clear(); + + let script_hash = partial_witness.script.hash(); + + // check the user provided all the required witnesses + required_wits_left.scripts.remove(&script_hash); + if let Some(datum) = &datum { + required_wits_left + .plutus_data + .remove(&hash_plutus_data(datum)); + } + + if required_wits_left.len() > 0 { + return Err(ProposalBuilderError::MissingWitnesses(Box::new( + required_wits_left, + ))); + } + + self.result.proposals.push(proposal); + + self.result.required_wits.add_all(required_wits); + + self.result + .aggregate_witnesses + .push(InputAggregateWitnessData::PlutusScript( + partial_witness, + required_signers, + datum, + )); + + Ok(self) + } + + pub fn build(self) -> ProposalBuilderResult { + self.result + } +} diff --git a/chain/rust/src/builders/redeemer_builder.rs b/chain/rust/src/builders/redeemer_builder.rs index d83e15ad..89e62138 100644 --- a/chain/rust/src/builders/redeemer_builder.rs +++ b/chain/rust/src/builders/redeemer_builder.rs @@ -1,6 +1,7 @@ use super::{ certificate_builder::CertificateBuilderResult, input_builder::InputBuilderResult, - mint_builder::MintBuilderResult, withdrawal_builder::WithdrawalBuilderResult, + mint_builder::MintBuilderResult, proposal_builder::ProposalBuilderResult, + vote_builder::VoteBuilderResult, withdrawal_builder::WithdrawalBuilderResult, }; use crate::{ address::RewardAddress, @@ -97,6 +98,10 @@ pub struct RedeemerSetBuilder { // certificates in the DCert list are indexed in the order in which they arranged in the (full, unfiltered) // list of certificates inside the transaction cert: Vec>, + + proposals: Vec>, + + votes: Vec>, } impl RedeemerSetBuilder { @@ -143,10 +148,18 @@ impl RedeemerSetBuilder { ))); } RedeemerTag::Proposing => { - todo!("https://github.com/dcSpark/cardano-multiplatform-lib/issues/323") + let entry = self.proposals.get_mut(key.index as usize).unwrap(); + *entry = Some(UntaggedRedeemerPlaceholder::Full(UntaggedRedeemer::new( + entry.as_ref().unwrap().data().clone(), + ex_units, + ))); } RedeemerTag::Voting => { - todo!("https://github.com/dcSpark/cardano-multiplatform-lib/issues/323") + let entry = self.votes.get_mut(key.index as usize).unwrap(); + *entry = Some(UntaggedRedeemerPlaceholder::Full(UntaggedRedeemer::new( + entry.as_ref().unwrap().data().clone(), + ex_units, + ))); } } } @@ -217,6 +230,28 @@ impl RedeemerSetBuilder { } } + pub fn add_proposal(&mut self, result: &ProposalBuilderResult) { + for aggregate_witness in &result.aggregate_witnesses { + if let Some(data) = aggregate_witness.redeemer_plutus_data() { + self.proposals + .push(Some(UntaggedRedeemerPlaceholder::JustData(data.clone()))); + } else { + self.proposals.push(None); + } + } + } + + pub fn add_vote(&mut self, result: &VoteBuilderResult) { + for aggregate_witness in &result.aggregate_witnesses { + if let Some(data) = aggregate_witness.redeemer_plutus_data() { + self.votes + .push(Some(UntaggedRedeemerPlaceholder::JustData(data.clone()))); + } else { + self.votes.push(None); + } + } + } + pub fn build(&self, default_to_dummy_exunits: bool) -> Result { let mut redeemers = Vec::new(); // Calling iter on a BTreeMap returns a list of sorted keys @@ -244,6 +279,18 @@ impl RedeemerSetBuilder { &mut self.cert.iter().map(|entry| (&(), entry)), default_to_dummy_exunits, )?; + self.remove_placeholders_and_tag( + &mut redeemers, + RedeemerTag::Proposing, + &mut self.proposals.iter().map(|entry| (&(), entry)), + default_to_dummy_exunits, + )?; + self.remove_placeholders_and_tag( + &mut redeemers, + RedeemerTag::Voting, + &mut self.votes.iter().map(|entry| (&(), entry)), + default_to_dummy_exunits, + )?; Ok(Redeemers::new_arr_legacy_redeemer(redeemers)) } diff --git a/chain/rust/src/builders/tx_builder.rs b/chain/rust/src/builders/tx_builder.rs index 7a6dd4bd..b6223a32 100644 --- a/chain/rust/src/builders/tx_builder.rs +++ b/chain/rust/src/builders/tx_builder.rs @@ -2,9 +2,11 @@ use super::certificate_builder::*; use super::input_builder::InputBuilderResult; use super::mint_builder::MintBuilderResult; use super::output_builder::{OutputBuilderError, SingleOutputBuilderResult}; +use super::proposal_builder::ProposalBuilderResult; use super::redeemer_builder::RedeemerBuilderError; use super::redeemer_builder::RedeemerSetBuilder; use super::redeemer_builder::RedeemerWitnessKey; +use super::vote_builder::VoteBuilderResult; use super::withdrawal_builder::WithdrawalBuilderResult; use super::witness_builder::merge_fake_witness; use super::witness_builder::PlutusScriptWitness; @@ -21,6 +23,7 @@ use crate::crypto::hash::{calc_script_data_hash, hash_auxiliary_data, ScriptData use crate::crypto::{BootstrapWitness, Vkeywitness}; use crate::deposit::{internal_get_deposit, internal_get_implicit_input}; use crate::fees::LinearFee; +use crate::governance::{ProposalProcedure, VotingProcedures}; use crate::min_ada::min_ada_required; use crate::plutus::{CostModels, ExUnits, Language}; use crate::plutus::{PlutusData, Redeemers}; @@ -41,7 +44,9 @@ use std::convert::TryInto; use std::io::{BufRead, Seek, Write}; use std::ops::DerefMut; -// for enums: +#[cfg(not(feature = "used_from_wasm"))] +use noop_proc_macro::wasm_bindgen; +#[cfg(feature = "used_from_wasm")] use wasm_bindgen::prelude::wasm_bindgen; /** @@ -363,7 +368,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), @@ -385,6 +390,8 @@ pub struct TransactionBuilder { ttl: Option, // absolute slot number certs: Option>, withdrawals: Option, + proposals: Option>, + votes: Option, auxiliary_data: Option, validity_start_interval: Option, mint: Option, @@ -720,13 +727,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, @@ -877,6 +883,60 @@ impl TransactionBuilder { .add_required_wits(result.required_wits); } + pub fn add_proposal(&mut self, mut result: ProposalBuilderResult) { + self.witness_builders + .redeemer_set_builder + .add_proposal(&result); + if self.proposals.is_none() { + self.proposals = Some(Vec::new()); + } + self.proposals + .as_mut() + .unwrap() + .append(&mut result.proposals); + for data in result.aggregate_witnesses { + self.witness_builders + .witness_set_builder + .add_input_aggregate_real_witness_data(&data); + self.witness_builders + .fake_required_witnesses + .add_input_aggregate_fake_witness_data(&data); + if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data { + required_signers + .iter() + .for_each(|signer| self.add_required_signer(*signer)); + } + } + self.witness_builders + .witness_set_builder + .add_required_wits(result.required_wits); + } + + pub fn add_vote(&mut self, result: VoteBuilderResult) { + self.witness_builders.redeemer_set_builder.add_vote(&result); + if let Some(votes) = self.votes.as_mut() { + votes.extend(result.votes.take()); + } else { + self.votes = Some(result.votes); + } + for data in result.aggregate_witnesses { + self.witness_builders + .witness_set_builder + .add_input_aggregate_real_witness_data(&data); + self.witness_builders + .fake_required_witnesses + .add_input_aggregate_fake_witness_data(&data); + if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data { + required_signers + .iter() + .for_each(|signer| self.add_required_signer(*signer)); + } + } + self.witness_builders + .witness_set_builder + .add_required_wits(result.required_wits); + } + pub fn get_withdrawals(&self) -> Option { self.withdrawals.clone() } @@ -978,6 +1038,8 @@ impl TransactionBuilder { ttl: None, certs: None, withdrawals: None, + proposals: None, + votes: None, auxiliary_data: None, validity_start_interval: None, mint: None, @@ -1125,6 +1187,7 @@ impl TransactionBuilder { pub fn get_deposit(&self) -> Result { internal_get_deposit( self.certs.as_deref(), + self.proposals.as_deref(), self.config.pool_deposit, self.config.key_deposit, ) @@ -1259,8 +1322,11 @@ impl TransactionBuilder { .collect::>() .into() }), - voting_procedures: None, - proposal_procedures: None, + voting_procedures: self.votes.clone(), + proposal_procedures: self + .proposals + .as_ref() + .map(|proposals| proposals.clone().into()), current_treasury_value: None, donation: None, encodings: None, diff --git a/chain/rust/src/builders/utils.rs b/chain/rust/src/builders/utils.rs new file mode 100644 index 00000000..f5be4b53 --- /dev/null +++ b/chain/rust/src/builders/utils.rs @@ -0,0 +1,14 @@ +use crate::RequiredSigners; + +use super::witness_builder::RequiredWitnessSet; + +pub(crate) fn required_wits_from_required_signers( + required_signers: &RequiredSigners, +) -> RequiredWitnessSet { + let mut required_wits = RequiredWitnessSet::default(); + required_signers + .as_ref() + .iter() + .for_each(|required_signer| required_wits.add_vkey_key_hash(*required_signer)); + required_wits +} diff --git a/chain/rust/src/builders/vote_builder.rs b/chain/rust/src/builders/vote_builder.rs new file mode 100644 index 00000000..4aef9c99 --- /dev/null +++ b/chain/rust/src/builders/vote_builder.rs @@ -0,0 +1,221 @@ +use crate::{ + crypto::hash::hash_plutus_data, + governance::{GovActionId, Voter, VotingProcedure, VotingProcedures}, + plutus::PlutusData, + transaction::NativeScript, + RequiredSigners, +}; + +use super::{ + utils::required_wits_from_required_signers, + witness_builder::{ + InputAggregateWitnessData, NativeScriptWitnessInfo, PartialPlutusWitness, + RequiredWitnessSet, + }, +}; + +#[derive(Debug, thiserror::Error)] +pub enum VoteBuilderError { + #[error("Voter is script. Call with_plutus_vote() instead.")] + VoterIsScript, + #[error("Voter is key hash. Call with_vote() instead.")] + VoterIsKeyHash, + #[error("Vote already exists")] + VoteAlreayExists, + #[error("Missing the following witnesses for the input: {0:?}")] + MissingWitnesses(Box), +} + +#[derive(Clone, Debug, Default)] +pub struct VoteBuilderResult { + pub votes: VotingProcedures, + pub required_wits: RequiredWitnessSet, + pub aggregate_witnesses: Vec, +} + +#[derive(Clone, Debug)] +pub struct VoteBuilder { + result: VoteBuilderResult, +} + +impl Default for VoteBuilder { + fn default() -> Self { + Self::new() + } +} + +impl VoteBuilder { + pub fn new() -> Self { + Self { + result: VoteBuilderResult::default(), + } + } + + /// Add a vote using a voter with a key hash + /// Will throw an error if the voter is script-hash based + pub fn with_vote( + mut self, + voter: Voter, + gov_action_id: GovActionId, + procedure: VotingProcedure, + ) -> Result { + if let Some(key_hash) = voter.key_hash() { + self.result.required_wits.add_vkey_key_hash(*key_hash); + } else { + return Err(VoteBuilderError::VoterIsScript); + } + if self + .result + .votes + .entry(voter) + .or_default() + .insert(gov_action_id, procedure) + .is_some() + { + return Err(VoteBuilderError::VoteAlreayExists); + } + Ok(self) + } + + pub fn with_native_script_vote( + mut self, + voter: Voter, + gov_action_id: GovActionId, + procedure: VotingProcedure, + native_script: NativeScript, + witness_info: NativeScriptWitnessInfo, + ) -> Result { + if let Some(script_hash) = voter.script_hash() { + if *script_hash != native_script.hash() { + let mut err_req_wits = RequiredWitnessSet::new(); + err_req_wits.add_script_hash(*script_hash); + return Err(VoteBuilderError::MissingWitnesses(Box::new(err_req_wits))); + } + self.result.required_wits.add_script_hash(*script_hash); + } else { + return Err(VoteBuilderError::VoterIsKeyHash); + } + + if self + .result + .votes + .entry(voter) + .or_default() + .insert(gov_action_id, procedure) + .is_some() + { + return Err(VoteBuilderError::VoteAlreayExists); + } + + self.result + .aggregate_witnesses + .push(InputAggregateWitnessData::NativeScript( + native_script, + witness_info, + )); + + Ok(self) + } + + pub fn with_plutus_vote( + self, + voter: Voter, + gov_action_id: GovActionId, + procedure: VotingProcedure, + partial_witness: PartialPlutusWitness, + required_signers: RequiredSigners, + datum: PlutusData, + ) -> Result { + self.with_plutus_vote_impl( + voter, + gov_action_id, + procedure, + partial_witness, + required_signers, + Some(datum), + ) + } + + pub fn with_plutus_vote_inline_datum( + self, + voter: Voter, + gov_action_id: GovActionId, + procedure: VotingProcedure, + partial_witness: PartialPlutusWitness, + required_signers: RequiredSigners, + ) -> Result { + self.with_plutus_vote_impl( + voter, + gov_action_id, + procedure, + partial_witness, + required_signers, + None, + ) + } + + fn with_plutus_vote_impl( + mut self, + voter: Voter, + gov_action_id: GovActionId, + procedure: VotingProcedure, + partial_witness: PartialPlutusWitness, + required_signers: RequiredSigners, + datum: Option, + ) -> Result { + let mut required_wits = required_wits_from_required_signers(&required_signers); + if let Some(script_hash) = voter.script_hash() { + required_wits.add_script_hash(*script_hash); + } else { + return Err(VoteBuilderError::VoterIsKeyHash); + } + + let mut required_wits_left = required_wits.clone(); + + // no way to know these at this time + required_wits_left.vkeys.clear(); + + let script_hash = partial_witness.script.hash(); + + // check the user provided all the required witnesses + required_wits_left.scripts.remove(&script_hash); + if let Some(datum) = &datum { + required_wits_left + .plutus_data + .remove(&hash_plutus_data(datum)); + } + + if required_wits_left.len() > 0 { + return Err(VoteBuilderError::MissingWitnesses(Box::new( + required_wits_left, + ))); + } + + if self + .result + .votes + .entry(voter) + .or_default() + .insert(gov_action_id, procedure) + .is_some() + { + return Err(VoteBuilderError::VoteAlreayExists); + } + + self.result.required_wits.add_all(required_wits); + + self.result + .aggregate_witnesses + .push(InputAggregateWitnessData::PlutusScript( + partial_witness, + required_signers, + datum, + )); + + Ok(self) + } + + pub fn build(self) -> VoteBuilderResult { + self.result + } +} 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/byron/mod.rs b/chain/rust/src/byron/mod.rs index 4701c158..1c455eea 100644 --- a/chain/rust/src/byron/mod.rs +++ b/chain/rust/src/byron/mod.rs @@ -1,5 +1,7 @@ -//#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] -//use noop_proc_macro::wasm_bindgen; +#[cfg(not(feature = "used_from_wasm"))] +use noop_proc_macro::wasm_bindgen; +#[cfg(feature = "used_from_wasm")] +use wasm_bindgen::prelude::wasm_bindgen; use std::io::{BufRead, Write}; @@ -76,7 +78,7 @@ impl Default for AddrAttributes { serde::Serialize, schemars::JsonSchema, )] -#[wasm_bindgen::prelude::wasm_bindgen] +#[wasm_bindgen] pub enum ByronAddrType { PublicKey = 0, Script = 1, diff --git a/chain/rust/src/certs/cbor_encodings.rs b/chain/rust/src/certs/cbor_encodings.rs index f902ce73..bc1b27e9 100644 --- a/chain/rust/src/certs/cbor_encodings.rs +++ b/chain/rust/src/certs/cbor_encodings.rs @@ -10,7 +10,7 @@ pub struct AuthCommitteeHotCertEncoding { } #[derive(Clone, Debug, Default)] -pub struct DnsNameEncoding { +pub struct DNSNameEncoding { pub inner_encoding: StringEncoding, } @@ -64,14 +64,14 @@ pub struct PoolRetirementEncoding { pub struct RegCertEncoding { pub len_encoding: LenEncoding, pub tag_encoding: Option, - pub coin_encoding: Option, + pub deposit_encoding: Option, } #[derive(Clone, Debug, Default)] pub struct RegDrepCertEncoding { pub len_encoding: LenEncoding, pub index_0_encoding: Option, - pub coin_encoding: Option, + pub deposit_encoding: Option, } #[derive(Clone, Debug, Default)] @@ -112,7 +112,7 @@ pub struct StakeRegDelegCertEncoding { pub len_encoding: LenEncoding, pub tag_encoding: Option, pub pool_encoding: StringEncoding, - pub coin_encoding: Option, + pub deposit_encoding: Option, } #[derive(Clone, Debug, Default)] @@ -133,21 +133,21 @@ pub struct StakeVoteRegDelegCertEncoding { pub len_encoding: LenEncoding, pub tag_encoding: Option, pub pool_encoding: StringEncoding, - pub coin_encoding: Option, + pub deposit_encoding: Option, } #[derive(Clone, Debug, Default)] pub struct UnregCertEncoding { pub len_encoding: LenEncoding, pub tag_encoding: Option, - pub coin_encoding: Option, + pub deposit_encoding: Option, } #[derive(Clone, Debug, Default)] pub struct UnregDrepCertEncoding { pub len_encoding: LenEncoding, pub index_0_encoding: Option, - pub coin_encoding: Option, + pub deposit_encoding: Option, } #[derive(Clone, Debug, Default)] @@ -171,5 +171,5 @@ pub struct VoteDelegCertEncoding { pub struct VoteRegDelegCertEncoding { pub len_encoding: LenEncoding, pub tag_encoding: Option, - pub coin_encoding: Option, + pub deposit_encoding: Option, } diff --git a/chain/rust/src/certs/mod.rs b/chain/rust/src/certs/mod.rs index e81693e5..28fc69c9 100644 --- a/chain/rust/src/certs/mod.rs +++ b/chain/rust/src/certs/mod.rs @@ -10,7 +10,7 @@ use crate::address::RewardAccount; use crate::crypto::{Ed25519KeyHash, PoolMetadataHash, ScriptHash, VRFKeyHash}; use crate::governance::Anchor; use cbor_encodings::{ - AuthCommitteeHotCertEncoding, DnsNameEncoding, Ipv4Encoding, Ipv6Encoding, + AuthCommitteeHotCertEncoding, DNSNameEncoding, Ipv4Encoding, Ipv6Encoding, MultiHostNameEncoding, PoolMetadataEncoding, PoolParamsEncoding, PoolRegistrationEncoding, PoolRetirementEncoding, RegCertEncoding, RegDrepCertEncoding, ResignCommitteeColdCertEncoding, SingleHostAddrEncoding, SingleHostNameEncoding, StakeDelegationEncoding, @@ -50,17 +50,27 @@ impl AuthCommitteeHotCert { #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub enum Certificate { + /// Will be deprecated in the next era. Use RegCert instead which takes an explicit deposit amount, as that can change. StakeRegistration(StakeRegistration), + /// Will be deprecated in the next era. Use UnregCert instead which takes an explicit deposit amount, as that can change. StakeDeregistration(StakeDeregistration), + /// Delegate to a take pool only StakeDelegation(StakeDelegation), PoolRegistration(PoolRegistration), PoolRetirement(PoolRetirement), + /// Registers a stake credential. RegCert(RegCert), + /// Unregisters a stake credential. UnregCert(UnregCert), + /// Delegate to a DRep for voting only VoteDelegCert(VoteDelegCert), + /// Delegate to a stake pool and a DRep StakeVoteDelegCert(StakeVoteDelegCert), + /// Register a stake credential and delegate to a pool in a single cert StakeRegDelegCert(StakeRegDelegCert), + /// Register a stake credential and delegate to a DRep in a single cert VoteRegDelegCert(VoteRegDelegCert), + /// Register a stake credential and delegate to a pool and a DRep in a single cert StakeVoteRegDelegCert(StakeVoteRegDelegCert), AuthCommitteeHotCert(AuthCommitteeHotCert), ResignCommitteeColdCert(ResignCommitteeColdCert), @@ -70,14 +80,17 @@ pub enum Certificate { } impl Certificate { + /// Will be deprecated in the next era. Use RegCert instead which takes an explicit deposit amount, as that can change. pub fn new_stake_registration(stake_credential: StakeCredential) -> Self { Self::StakeRegistration(StakeRegistration::new(stake_credential)) } + /// Will be deprecated in the next era. Use UnregCert instead which takes an explicit deposit amount, as that can change. pub fn new_stake_deregistration(stake_credential: StakeCredential) -> Self { Self::StakeDeregistration(StakeDeregistration::new(stake_credential)) } + /// Delegate to a take pool only pub fn new_stake_delegation(stake_credential: StakeCredential, pool: Ed25519KeyHash) -> Self { Self::StakeDelegation(StakeDelegation::new(stake_credential, pool)) } @@ -90,18 +103,22 @@ impl Certificate { Self::PoolRetirement(PoolRetirement::new(pool, epoch)) } - pub fn new_reg_cert(stake_credential: StakeCredential, coin: Coin) -> Self { - Self::RegCert(RegCert::new(stake_credential, coin)) + /// Registers a stake credential. + pub fn new_reg_cert(stake_credential: StakeCredential, deposit: Coin) -> Self { + Self::RegCert(RegCert::new(stake_credential, deposit)) } - pub fn new_unreg_cert(stake_credential: StakeCredential, coin: Coin) -> Self { - Self::UnregCert(UnregCert::new(stake_credential, coin)) + /// Unregisters a stake credential. + pub fn new_unreg_cert(stake_credential: StakeCredential, deposit: Coin) -> Self { + Self::UnregCert(UnregCert::new(stake_credential, deposit)) } + /// Delegate to a DRep for voting only pub fn new_vote_deleg_cert(stake_credential: StakeCredential, d_rep: DRep) -> Self { Self::VoteDelegCert(VoteDelegCert::new(stake_credential, d_rep)) } + /// Delegate to a stake pool and a DRep pub fn new_stake_vote_deleg_cert( stake_credential: StakeCredential, pool: Ed25519KeyHash, @@ -110,33 +127,36 @@ impl Certificate { Self::StakeVoteDelegCert(StakeVoteDelegCert::new(stake_credential, pool, d_rep)) } + /// Register a stake credential and delegate to a pool in a single cert pub fn new_stake_reg_deleg_cert( stake_credential: StakeCredential, pool: Ed25519KeyHash, - coin: Coin, + deposit: Coin, ) -> Self { - Self::StakeRegDelegCert(StakeRegDelegCert::new(stake_credential, pool, coin)) + Self::StakeRegDelegCert(StakeRegDelegCert::new(stake_credential, pool, deposit)) } + /// Register a stake credential and delegate to a DRep in a single cert pub fn new_vote_reg_deleg_cert( stake_credential: StakeCredential, d_rep: DRep, - coin: Coin, + deposit: Coin, ) -> Self { - Self::VoteRegDelegCert(VoteRegDelegCert::new(stake_credential, d_rep, coin)) + Self::VoteRegDelegCert(VoteRegDelegCert::new(stake_credential, d_rep, deposit)) } + /// Register a stake credential and delegate to a pool and a DRep in a single cert pub fn new_stake_vote_reg_deleg_cert( stake_credential: StakeCredential, pool: Ed25519KeyHash, d_rep: DRep, - coin: Coin, + deposit: Coin, ) -> Self { Self::StakeVoteRegDelegCert(StakeVoteRegDelegCert::new( stake_credential, pool, d_rep, - coin, + deposit, )) } @@ -162,14 +182,14 @@ impl Certificate { pub fn new_reg_drep_cert( drep_credential: DrepCredential, - coin: Coin, + deposit: Coin, anchor: Option, ) -> Self { - Self::RegDrepCert(RegDrepCert::new(drep_credential, coin, anchor)) + Self::RegDrepCert(RegDrepCert::new(drep_credential, deposit, anchor)) } - pub fn new_unreg_drep_cert(drep_credential: DrepCredential, coin: Coin) -> Self { - Self::UnregDrepCert(UnregDrepCert::new(drep_credential, coin)) + pub fn new_unreg_drep_cert(drep_credential: DrepCredential, deposit: Coin) -> Self { + Self::UnregDrepCert(UnregDrepCert::new(drep_credential, deposit)) } pub fn new_update_drep_cert(drep_credential: DrepCredential, anchor: Option) -> Self { @@ -268,6 +288,78 @@ impl Credential { } } +#[derive(Clone, Debug)] +pub struct DNSName { + pub inner: String, + pub encodings: Option, +} + +impl DNSName { + pub fn get(&self) -> &String { + &self.inner + } + + pub fn new(inner: String) -> Result { + if inner.len() > 128 { + return Err(DeserializeError::new( + "DNSName", + DeserializeFailure::RangeCheck { + found: inner.len() as isize, + min: Some(0), + max: Some(128), + }, + )); + } + Ok(Self { + inner, + encodings: None, + }) + } +} + +impl TryFrom for DNSName { + type Error = DeserializeError; + + fn try_from(inner: String) -> Result { + DNSName::new(inner) + } +} + +impl serde::Serialize for DNSName { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.inner.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for DNSName { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let inner = ::deserialize(deserializer)?; + Self::new(inner.clone()).map_err(|_e| { + serde::de::Error::invalid_value(serde::de::Unexpected::Str(&inner), &"invalid DNSName") + }) + } +} + +impl schemars::JsonSchema for DNSName { + fn schema_name() -> String { + String::from("DNSName") + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + + fn is_referenceable() -> bool { + String::is_referenceable() + } +} + #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub enum DRep { Key { @@ -336,78 +428,6 @@ impl DRep { } } -#[derive(Clone, Debug)] -pub struct DnsName { - pub inner: String, - pub encodings: Option, -} - -impl DnsName { - pub fn get(&self) -> &String { - &self.inner - } - - pub fn new(inner: String) -> Result { - if inner.len() > 128 { - return Err(DeserializeError::new( - "DnsName", - DeserializeFailure::RangeCheck { - found: inner.len() as isize, - min: Some(0), - max: Some(128), - }, - )); - } - Ok(Self { - inner, - encodings: None, - }) - } -} - -impl TryFrom for DnsName { - type Error = DeserializeError; - - fn try_from(inner: String) -> Result { - DnsName::new(inner) - } -} - -impl serde::Serialize for DnsName { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.inner.serialize(serializer) - } -} - -impl<'de> serde::de::Deserialize<'de> for DnsName { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let inner = ::deserialize(deserializer)?; - Self::new(inner.clone()).map_err(|_e| { - serde::de::Error::invalid_value(serde::de::Unexpected::Str(&inner), &"invalid DnsName") - }) - } -} - -impl schemars::JsonSchema for DnsName { - fn schema_name() -> String { - String::from("DnsName") - } - - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - pub type DrepCredential = Credential; #[derive(Clone, Debug)] @@ -498,13 +518,15 @@ impl From for Vec { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct MultiHostName { - pub dns_name: DnsName, + /// A SRV DNS record + pub dns_name: DNSName, #[serde(skip)] pub encodings: Option, } impl MultiHostName { - pub fn new(dns_name: DnsName) -> Self { + /// * `dns_name` - A SRV DNS record + pub fn new(dns_name: DNSName) -> Self { Self { dns_name, encodings: None, @@ -610,16 +632,16 @@ impl PoolRetirement { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct RegCert { pub stake_credential: StakeCredential, - pub coin: Coin, + pub deposit: Coin, #[serde(skip)] pub encodings: Option, } impl RegCert { - pub fn new(stake_credential: StakeCredential, coin: Coin) -> Self { + pub fn new(stake_credential: StakeCredential, deposit: Coin) -> Self { Self { stake_credential, - coin, + deposit, encodings: None, } } @@ -628,17 +650,17 @@ impl RegCert { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct RegDrepCert { pub drep_credential: DrepCredential, - pub coin: Coin, + pub deposit: Coin, pub anchor: Option, #[serde(skip)] pub encodings: Option, } impl RegDrepCert { - pub fn new(drep_credential: DrepCredential, coin: Coin, anchor: Option) -> Self { + pub fn new(drep_credential: DrepCredential, deposit: Coin, anchor: Option) -> Self { Self { drep_credential, - coin, + deposit, anchor, encodings: None, } @@ -661,11 +683,11 @@ impl Relay { Self::SingleHostAddr(SingleHostAddr::new(port, ipv4, ipv6)) } - pub fn new_single_host_name(port: Option, dns_name: DnsName) -> Self { + pub fn new_single_host_name(port: Option, dns_name: DNSName) -> Self { Self::SingleHostName(SingleHostName::new(port, dns_name)) } - pub fn new_multi_host_name(dns_name: DnsName) -> Self { + pub fn new_multi_host_name(dns_name: DNSName) -> Self { Self::MultiHostName(MultiHostName::new(dns_name)) } } @@ -711,13 +733,15 @@ impl SingleHostAddr { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct SingleHostName { pub port: Option, - pub dns_name: DnsName, + /// An A or AAAA DNS record + pub dns_name: DNSName, #[serde(skip)] pub encodings: Option, } impl SingleHostName { - pub fn new(port: Option, dns_name: DnsName) -> Self { + /// * `dns_name` - An A or AAAA DNS record + pub fn new(port: Option, dns_name: DNSName) -> Self { Self { port, dns_name, @@ -766,17 +790,17 @@ impl StakeDeregistration { pub struct StakeRegDelegCert { pub stake_credential: StakeCredential, pub pool: Ed25519KeyHash, - pub coin: Coin, + pub deposit: Coin, #[serde(skip)] pub encodings: Option, } impl StakeRegDelegCert { - pub fn new(stake_credential: StakeCredential, pool: Ed25519KeyHash, coin: Coin) -> Self { + pub fn new(stake_credential: StakeCredential, pool: Ed25519KeyHash, deposit: Coin) -> Self { Self { stake_credential, pool, - coin, + deposit, encodings: None, } } @@ -823,7 +847,7 @@ pub struct StakeVoteRegDelegCert { pub stake_credential: StakeCredential, pub pool: Ed25519KeyHash, pub d_rep: DRep, - pub coin: Coin, + pub deposit: Coin, #[serde(skip)] pub encodings: Option, } @@ -833,20 +857,20 @@ impl StakeVoteRegDelegCert { stake_credential: StakeCredential, pool: Ed25519KeyHash, d_rep: DRep, - coin: Coin, + deposit: Coin, ) -> Self { Self { stake_credential, pool, d_rep, - coin, + deposit, encodings: None, } } } -impl From for String { - fn from(wrapper: DnsName) -> Self { +impl From for String { + fn from(wrapper: DNSName) -> Self { wrapper.inner } } @@ -860,16 +884,16 @@ impl From for String { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct UnregCert { pub stake_credential: StakeCredential, - pub coin: Coin, + pub deposit: Coin, #[serde(skip)] pub encodings: Option, } impl UnregCert { - pub fn new(stake_credential: StakeCredential, coin: Coin) -> Self { + pub fn new(stake_credential: StakeCredential, deposit: Coin) -> Self { Self { stake_credential, - coin, + deposit, encodings: None, } } @@ -878,16 +902,16 @@ impl UnregCert { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct UnregDrepCert { pub drep_credential: DrepCredential, - pub coin: Coin, + pub deposit: Coin, #[serde(skip)] pub encodings: Option, } impl UnregDrepCert { - pub fn new(drep_credential: DrepCredential, coin: Coin) -> Self { + pub fn new(drep_credential: DrepCredential, deposit: Coin) -> Self { Self { drep_credential, - coin, + deposit, encodings: None, } } @@ -1005,17 +1029,17 @@ impl VoteDelegCert { pub struct VoteRegDelegCert { pub stake_credential: StakeCredential, pub d_rep: DRep, - pub coin: Coin, + pub deposit: Coin, #[serde(skip)] pub encodings: Option, } impl VoteRegDelegCert { - pub fn new(stake_credential: StakeCredential, d_rep: DRep, coin: Coin) -> Self { + pub fn new(stake_credential: StakeCredential, d_rep: DRep, deposit: Coin) -> Self { Self { stake_credential, d_rep, - coin, + deposit, encodings: None, } } diff --git a/chain/rust/src/certs/serialization.rs b/chain/rust/src/certs/serialization.rs index 17cb8f13..754b1f58 100644 --- a/chain/rust/src/certs/serialization.rs +++ b/chain/rust/src/certs/serialization.rs @@ -752,6 +752,45 @@ impl Deserialize for Credential { } } +impl Serialize for DNSName { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + force_canonical: bool, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_text_sz( + &self.inner, + self.encodings + .as_ref() + .map(|encs| encs.inner_encoding.clone()) + .unwrap_or_default() + .to_str_len_sz(self.inner.len() as u64, force_canonical), + ) + } +} + +impl Deserialize for DNSName { + fn deserialize(raw: &mut Deserializer) -> Result { + let (inner, inner_encoding) = raw + .text_sz() + .map(|(s, enc)| (s, StringEncoding::from(enc)))?; + if inner.len() > 128 { + return Err(DeserializeError::new( + "DNSName", + DeserializeFailure::RangeCheck { + found: inner.len() as isize, + min: Some(0), + max: Some(128), + }, + )); + } + Ok(Self { + inner, + encodings: Some(DNSNameEncoding { inner_encoding }), + }) + } +} + impl Serialize for DRep { fn serialize<'se, W: Write>( &self, @@ -1009,45 +1048,6 @@ impl Deserialize for DRep { } } -impl Serialize for DnsName { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - force_canonical: bool, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_text_sz( - &self.inner, - self.encodings - .as_ref() - .map(|encs| encs.inner_encoding.clone()) - .unwrap_or_default() - .to_str_len_sz(self.inner.len() as u64, force_canonical), - ) - } -} - -impl Deserialize for DnsName { - fn deserialize(raw: &mut Deserializer) -> Result { - let (inner, inner_encoding) = raw - .text_sz() - .map(|(s, enc)| (s, StringEncoding::from(enc)))?; - if inner.len() > 128 { - return Err(DeserializeError::new( - "DnsName", - DeserializeFailure::RangeCheck { - found: inner.len() as isize, - min: Some(0), - max: Some(128), - }, - )); - } - Ok(Self { - inner, - encodings: Some(DnsNameEncoding { inner_encoding }), - }) - } -} - impl Serialize for Ipv4 { fn serialize<'se, W: Write>( &self, @@ -1208,7 +1208,7 @@ impl DeserializeEmbeddedGroup for MultiHostName { })() .map_err(|e| e.annotate("tag"))?; let dns_name = - DnsName::deserialize(raw).map_err(|e: DeserializeError| e.annotate("dns_name"))?; + DNSName::deserialize(raw).map_err(|e: DeserializeError| e.annotate("dns_name"))?; Ok(MultiHostName { dns_name, encodings: Some(MultiHostNameEncoding { @@ -1760,12 +1760,12 @@ impl SerializeEmbeddedGroup for RegCert { self.stake_credential .serialize(serializer, force_canonical)?; serializer.write_unsigned_integer_sz( - self.coin, + self.deposit, fit_sz( - self.coin, + self.deposit, self.encodings .as_ref() - .map(|encs| encs.coin_encoding) + .map(|encs| encs.deposit_encoding) .unwrap_or_default(), force_canonical, ), @@ -1818,18 +1818,18 @@ impl DeserializeEmbeddedGroup for RegCert { .map_err(|e| e.annotate("tag"))?; let stake_credential = Credential::deserialize(raw) .map_err(|e: DeserializeError| e.annotate("stake_credential"))?; - let (coin, coin_encoding) = raw + let (deposit, deposit_encoding) = raw .unsigned_integer_sz() .map_err(Into::::into) .map(|(x, enc)| (x, Some(enc))) - .map_err(|e: DeserializeError| e.annotate("coin"))?; + .map_err(|e: DeserializeError| e.annotate("deposit"))?; Ok(RegCert { stake_credential, - coin, + deposit, encodings: Some(RegCertEncoding { len_encoding, tag_encoding, - coin_encoding, + deposit_encoding, }), }) })() @@ -1874,12 +1874,12 @@ impl SerializeEmbeddedGroup for RegDrepCert { self.drep_credential .serialize(serializer, force_canonical)?; serializer.write_unsigned_integer_sz( - self.coin, + self.deposit, fit_sz( - self.coin, + self.deposit, self.encodings .as_ref() - .map(|encs| encs.coin_encoding) + .map(|encs| encs.deposit_encoding) .unwrap_or_default(), force_canonical, ), @@ -1936,11 +1936,11 @@ impl DeserializeEmbeddedGroup for RegDrepCert { .map_err(|e| e.annotate("index_0"))?; let drep_credential = Credential::deserialize(raw) .map_err(|e: DeserializeError| e.annotate("drep_credential"))?; - let (coin, coin_encoding) = raw + let (deposit, deposit_encoding) = raw .unsigned_integer_sz() .map_err(Into::::into) .map(|(x, enc)| (x, Some(enc))) - .map_err(|e: DeserializeError| e.annotate("coin"))?; + .map_err(|e: DeserializeError| e.annotate("deposit"))?; let anchor = (|| -> Result<_, DeserializeError> { Ok(match raw.cbor_type()? != cbor_event::Type::Special { true => Some(Anchor::deserialize(raw)?), @@ -1955,12 +1955,12 @@ impl DeserializeEmbeddedGroup for RegDrepCert { .map_err(|e| e.annotate("anchor"))?; Ok(RegDrepCert { drep_credential, - coin, + deposit, anchor, encodings: Some(RegDrepCertEncoding { len_encoding, index_0_encoding, - coin_encoding, + deposit_encoding, }), }) })() @@ -2454,7 +2454,7 @@ impl DeserializeEmbeddedGroup for SingleHostName { })() .map_err(|e| e.annotate("port"))?; let dns_name = - DnsName::deserialize(raw).map_err(|e: DeserializeError| e.annotate("dns_name"))?; + DNSName::deserialize(raw).map_err(|e: DeserializeError| e.annotate("dns_name"))?; Ok(SingleHostName { port, dns_name, @@ -2725,12 +2725,12 @@ impl SerializeEmbeddedGroup for StakeRegDelegCert { .to_str_len_sz(self.pool.to_raw_bytes().len() as u64, force_canonical), )?; serializer.write_unsigned_integer_sz( - self.coin, + self.deposit, fit_sz( - self.coin, + self.deposit, self.encodings .as_ref() - .map(|encs| encs.coin_encoding) + .map(|encs| encs.deposit_encoding) .unwrap_or_default(), force_canonical, ), @@ -2792,20 +2792,20 @@ impl DeserializeEmbeddedGroup for StakeRegDelegCert { .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) }) .map_err(|e: DeserializeError| e.annotate("pool"))?; - let (coin, coin_encoding) = raw + let (deposit, deposit_encoding) = raw .unsigned_integer_sz() .map_err(Into::::into) .map(|(x, enc)| (x, Some(enc))) - .map_err(|e: DeserializeError| e.annotate("coin"))?; + .map_err(|e: DeserializeError| e.annotate("deposit"))?; Ok(StakeRegDelegCert { stake_credential, pool, - coin, + deposit, encodings: Some(StakeRegDelegCertEncoding { len_encoding, tag_encoding, pool_encoding, - coin_encoding, + deposit_encoding, }), }) })() @@ -3074,12 +3074,12 @@ impl SerializeEmbeddedGroup for StakeVoteRegDelegCert { )?; self.d_rep.serialize(serializer, force_canonical)?; serializer.write_unsigned_integer_sz( - self.coin, + self.deposit, fit_sz( - self.coin, + self.deposit, self.encodings .as_ref() - .map(|encs| encs.coin_encoding) + .map(|encs| encs.deposit_encoding) .unwrap_or_default(), force_canonical, ), @@ -3143,21 +3143,21 @@ impl DeserializeEmbeddedGroup for StakeVoteRegDelegCert { .map_err(|e: DeserializeError| e.annotate("pool"))?; let d_rep = DRep::deserialize(raw).map_err(|e: DeserializeError| e.annotate("d_rep"))?; - let (coin, coin_encoding) = raw + let (deposit, deposit_encoding) = raw .unsigned_integer_sz() .map_err(Into::::into) .map(|(x, enc)| (x, Some(enc))) - .map_err(|e: DeserializeError| e.annotate("coin"))?; + .map_err(|e: DeserializeError| e.annotate("deposit"))?; Ok(StakeVoteRegDelegCert { stake_credential, pool, d_rep, - coin, + deposit, encodings: Some(StakeVoteRegDelegCertEncoding { len_encoding, tag_encoding, pool_encoding, - coin_encoding, + deposit_encoding, }), }) })() @@ -3202,12 +3202,12 @@ impl SerializeEmbeddedGroup for UnregCert { self.stake_credential .serialize(serializer, force_canonical)?; serializer.write_unsigned_integer_sz( - self.coin, + self.deposit, fit_sz( - self.coin, + self.deposit, self.encodings .as_ref() - .map(|encs| encs.coin_encoding) + .map(|encs| encs.deposit_encoding) .unwrap_or_default(), force_canonical, ), @@ -3260,18 +3260,18 @@ impl DeserializeEmbeddedGroup for UnregCert { .map_err(|e| e.annotate("tag"))?; let stake_credential = Credential::deserialize(raw) .map_err(|e: DeserializeError| e.annotate("stake_credential"))?; - let (coin, coin_encoding) = raw + let (deposit, deposit_encoding) = raw .unsigned_integer_sz() .map_err(Into::::into) .map(|(x, enc)| (x, Some(enc))) - .map_err(|e: DeserializeError| e.annotate("coin"))?; + .map_err(|e: DeserializeError| e.annotate("deposit"))?; Ok(UnregCert { stake_credential, - coin, + deposit, encodings: Some(UnregCertEncoding { len_encoding, tag_encoding, - coin_encoding, + deposit_encoding, }), }) })() @@ -3316,12 +3316,12 @@ impl SerializeEmbeddedGroup for UnregDrepCert { self.drep_credential .serialize(serializer, force_canonical)?; serializer.write_unsigned_integer_sz( - self.coin, + self.deposit, fit_sz( - self.coin, + self.deposit, self.encodings .as_ref() - .map(|encs| encs.coin_encoding) + .map(|encs| encs.deposit_encoding) .unwrap_or_default(), force_canonical, ), @@ -3374,18 +3374,18 @@ impl DeserializeEmbeddedGroup for UnregDrepCert { .map_err(|e| e.annotate("index_0"))?; let drep_credential = Credential::deserialize(raw) .map_err(|e: DeserializeError| e.annotate("drep_credential"))?; - let (coin, coin_encoding) = raw + let (deposit, deposit_encoding) = raw .unsigned_integer_sz() .map_err(Into::::into) .map(|(x, enc)| (x, Some(enc))) - .map_err(|e: DeserializeError| e.annotate("coin"))?; + .map_err(|e: DeserializeError| e.annotate("deposit"))?; Ok(UnregDrepCert { drep_credential, - coin, + deposit, encodings: Some(UnregDrepCertEncoding { len_encoding, index_0_encoding, - coin_encoding, + deposit_encoding, }), }) })() @@ -3683,12 +3683,12 @@ impl SerializeEmbeddedGroup for VoteRegDelegCert { .serialize(serializer, force_canonical)?; self.d_rep.serialize(serializer, force_canonical)?; serializer.write_unsigned_integer_sz( - self.coin, + self.deposit, fit_sz( - self.coin, + self.deposit, self.encodings .as_ref() - .map(|encs| encs.coin_encoding) + .map(|encs| encs.deposit_encoding) .unwrap_or_default(), force_canonical, ), @@ -3743,19 +3743,19 @@ impl DeserializeEmbeddedGroup for VoteRegDelegCert { .map_err(|e: DeserializeError| e.annotate("stake_credential"))?; let d_rep = DRep::deserialize(raw).map_err(|e: DeserializeError| e.annotate("d_rep"))?; - let (coin, coin_encoding) = raw + let (deposit, deposit_encoding) = raw .unsigned_integer_sz() .map_err(Into::::into) .map(|(x, enc)| (x, Some(enc))) - .map_err(|e: DeserializeError| e.annotate("coin"))?; + .map_err(|e: DeserializeError| e.annotate("deposit"))?; Ok(VoteRegDelegCert { stake_credential, d_rep, - coin, + deposit, encodings: Some(VoteRegDelegCertEncoding { len_encoding, tag_encoding, - coin_encoding, + deposit_encoding, }), }) })() diff --git a/chain/rust/src/crypto/hash.rs b/chain/rust/src/crypto/hash.rs index d07f6e03..c8fbe5f0 100644 --- a/chain/rust/src/crypto/hash.rs +++ b/chain/rust/src/crypto/hash.rs @@ -88,37 +88,16 @@ 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/deposit.rs b/chain/rust/src/deposit.rs index 3e40b394..a498e203 100644 --- a/chain/rust/src/deposit.rs +++ b/chain/rust/src/deposit.rs @@ -1,6 +1,9 @@ use cml_core::ArithmeticError; -use crate::{certs::Certificate, transaction::TransactionBody, Coin, Value, Withdrawals}; +use crate::{ + certs::Certificate, governance::ProposalProcedure, transaction::TransactionBody, Coin, Value, + Withdrawals, +}; pub fn internal_get_implicit_input( withdrawals: Option<&Withdrawals>, @@ -22,7 +25,10 @@ pub fn internal_get_implicit_input( .try_fold(0u64, |acc, cert| match cert { Certificate::PoolRetirement(_cert) => acc.checked_add(pool_deposit), Certificate::StakeDeregistration(_cert) => acc.checked_add(key_deposit), - Certificate::UnregCert(_cert) => acc.checked_add(key_deposit), + Certificate::UnregCert(cert) => acc.checked_add(cert.deposit), + Certificate::UnregDrepCert(cert) => acc.checked_add(cert.deposit), + // TODO: is this the case? + Certificate::ResignCommitteeColdCert(_cert) => acc.checked_add(key_deposit), _ => Some(acc), }) .ok_or(ArithmeticError::IntegerOverflow)?, @@ -36,6 +42,7 @@ pub fn internal_get_implicit_input( pub fn internal_get_deposit( certs: Option<&[Certificate]>, + proposals: Option<&[ProposalProcedure]>, pool_deposit: Coin, // // protocol parameter key_deposit: Coin, // protocol parameter ) -> Result { @@ -46,13 +53,25 @@ pub fn internal_get_deposit( .try_fold(0u64, |acc, cert| match cert { Certificate::PoolRegistration(_cert) => acc.checked_add(pool_deposit), Certificate::StakeRegistration(_cert) => acc.checked_add(key_deposit), - Certificate::RegCert(_cert) => acc.checked_add(key_deposit), - Certificate::StakeRegDelegCert(_cert) => acc.checked_add(key_deposit), + Certificate::RegCert(cert) => acc.checked_add(cert.deposit), + Certificate::StakeRegDelegCert(cert) => acc.checked_add(cert.deposit), + Certificate::RegDrepCert(cert) => acc.checked_add(cert.deposit), + Certificate::VoteRegDelegCert(cert) => acc.checked_add(cert.deposit), + Certificate::StakeVoteRegDelegCert(cert) => acc.checked_add(cert.deposit), _ => Some(acc), }) .ok_or(ArithmeticError::IntegerOverflow)?, }; - Ok(certificate_refund) + let proposal_refund = match proposals { + None => 0, + Some(proposals) => proposals + .iter() + .try_fold(0u64, |acc, proposal| acc.checked_add(proposal.deposit)) + .ok_or(ArithmeticError::IntegerOverflow)?, + }; + certificate_refund + .checked_add(proposal_refund) + .ok_or(ArithmeticError::IntegerOverflow) } pub fn get_implicit_input( @@ -75,6 +94,10 @@ pub fn get_deposit( ) -> Result { internal_get_deposit( txbody.certs.as_ref().map(|certs| certs.as_ref()), + txbody + .proposal_procedures + .as_ref() + .map(|proposals| proposals.as_ref()), pool_deposit, key_deposit, ) diff --git a/chain/rust/src/genesis/byron/parse.rs b/chain/rust/src/genesis/byron/parse.rs index 1ace4de7..ddbedc88 100644 --- a/chain/rust/src/genesis/byron/parse.rs +++ b/chain/rust/src/genesis/byron/parse.rs @@ -3,6 +3,7 @@ use base64::{ Engine, }; use cbor_event::cbor; +use cml_core::DeserializeError; use cml_crypto::{CryptoError, RawBytesEncoding}; use serde_json; use std::collections::BTreeMap; @@ -29,6 +30,8 @@ pub enum GenesisJSONError { Serde(#[from] serde_json::Error), #[error("Crypto: {0:?}")] CryptoError(#[from] CryptoError), + #[error("Deserialize: {0:?}")] + DeserializeError(#[from] DeserializeError), #[error("Base64: {0:?}")] Base64(#[from] base64::DecodeError), #[error("ParseInt: {0:?}")] diff --git a/chain/rust/src/genesis/mod.rs b/chain/rust/src/genesis/mod.rs index f5f3e848..e485153e 100644 --- a/chain/rust/src/genesis/mod.rs +++ b/chain/rust/src/genesis/mod.rs @@ -1,2 +1,3 @@ pub mod byron; pub mod network_info; +pub mod shelley; diff --git a/chain/rust/src/genesis/network_info.rs b/chain/rust/src/genesis/network_info.rs index eb9aef0d..7893ae67 100644 --- a/chain/rust/src/genesis/network_info.rs +++ b/chain/rust/src/genesis/network_info.rs @@ -1,13 +1,15 @@ -use crate::{byron::ProtocolMagic, plutus::CostModels}; -use cml_core::{ - network::{ - BYRON_MAINNET_NETWORK_MAGIC, BYRON_TESTNET_NETWORK_MAGIC, PREPROD_NETWORK_MAGIC, - PREVIEW_NETWORK_MAGIC, SANCHO_TESTNET_NETWORK_MAGIC, - }, - Int, +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)] +#[derive( + Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, +)] pub struct NetworkInfo { network_id: u8, protocol_magic: ProtocolMagic, @@ -67,7 +69,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 +83,7 @@ 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/genesis/shelley/config.rs b/chain/rust/src/genesis/shelley/config.rs new file mode 100644 index 00000000..45eade5a --- /dev/null +++ b/chain/rust/src/genesis/shelley/config.rs @@ -0,0 +1,65 @@ +use cml_crypto::{Ed25519KeyHash, VRFKeyHash}; +use fraction::Fraction; +use std::collections::BTreeMap; + +use crate::{address::Address, block::ProtocolVersion, Coin}; + +/// A subset of the Shelley genesis data. The genesis data is a JSON file +/// is something completely different from a epoch genesis block and the Byron genesis block +#[derive(Debug, Clone)] +pub struct ShelleyGenesisData { + pub active_slots_coeff: Fraction, + pub epoch_length: u64, + pub gen_delegs: BTreeMap, + pub initial_funds: BTreeMap, + pub max_kes_evolutions: u64, + pub max_lovelace_supply: Coin, + pub network_id: u64, + pub network_magic: u64, + pub protocol_params: ShelleyGenesisProtocolParameters, + pub security_param: u64, + pub slot_length: u64, + pub slots_per_kes_period: u64, + pub staking: Option, + pub system_start: chrono::DateTime, + pub update_quorum: u64, +} + +#[derive(Debug, Clone)] +pub struct ShelleyGenesisDelegations { + pub delegate: Ed25519KeyHash, + pub vrf: VRFKeyHash, +} + +#[derive(Debug, Clone)] +pub struct ShelleyGenesisStaking { + pub pools: BTreeMap, + // initial delegations of staking key -> pool id + pub stake: BTreeMap, +} + +#[derive(Debug, Clone)] +pub struct ShelleyGenesisProtocolParameters { + pub a0: Fraction, + pub decentralisation_param: Fraction, + pub e_max: u64, + pub extra_entropy: ShelleyGenesisExtraEntropy, + pub key_deposit: Coin, + pub max_block_body_size: u64, + pub max_block_header_size: u64, + pub max_tx_size: u64, + pub min_fee_a: Coin, + pub min_fee_b: Coin, + pub min_pool_cost: Coin, + pub min_utxo_value: Coin, + pub n_opt: u64, + pub pool_deposit: Coin, + pub protocol_version: ProtocolVersion, + pub rho: Fraction, + pub tau: Fraction, +} + +#[derive(Debug, Clone)] +pub struct ShelleyGenesisExtraEntropy { + pub tag: String, +} diff --git a/chain/rust/src/genesis/shelley/mod.rs b/chain/rust/src/genesis/shelley/mod.rs new file mode 100644 index 00000000..9fdecf58 --- /dev/null +++ b/chain/rust/src/genesis/shelley/mod.rs @@ -0,0 +1,3 @@ +pub mod config; +pub mod parse; +pub mod raw; diff --git a/chain/rust/src/genesis/shelley/parse.rs b/chain/rust/src/genesis/shelley/parse.rs new file mode 100644 index 00000000..0dfa4415 --- /dev/null +++ b/chain/rust/src/genesis/shelley/parse.rs @@ -0,0 +1,226 @@ +use cml_core::DeserializeError; +use cml_crypto::{ + chain_crypto::Blake2b256, Ed25519KeyHash, PoolMetadataHash, TransactionHash, VRFKeyHash, +}; +use serde_json; +use std::collections::BTreeMap; +use std::io::Read; +use std::str::FromStr; + +use crate::{ + address::{Address, RewardAccount}, + block::ProtocolVersion, + certs::{Ipv4, Ipv6, PoolMetadata, PoolParams, Relay, StakeCredential, Url}, + UnitInterval, +}; + +use super::{ + config, + raw::{self}, +}; + +#[derive(Debug, thiserror::Error)] +pub enum GenesisJSONError { + #[error("JSON: {0:?}")] + Serde(#[from] serde_json::Error), + #[error("Deserialize: {0:?}")] + Deserialize(#[from] DeserializeError), + #[error("ParseInt: {0:?}")] + ParseInt(#[from] std::num::ParseIntError), + #[error("ParseIP: {0:?}")] + ParseIP(#[from] crate::certs::utils::IPStringParsingError), + #[error("Unexpected network type: {0:?}")] + ParseNetwork(String), +} + +pub fn parse_genesis_data( + json: R, +) -> Result { + let data_value: serde_json::Value = serde_json::from_reader(json)?; + let data: raw::ShelleyGenesisData = serde_json::from_value(data_value)?; + + let mut initial_funds = BTreeMap::new(); + for (addr_hex, balance) in &data.initialFunds { + initial_funds.insert(Address::from_hex(addr_hex)?, *balance); + } + + let network_id = match data.networkId.as_str() { + "Mainnet" => crate::NetworkId::mainnet().network, + "Testnet" => crate::NetworkId::testnet().network, + val => return Err(GenesisJSONError::ParseNetwork(val.to_string())), + }; + + let staking = match data.staking.as_ref() { + Some(raw) => { + // 1) Get stake pools + let mut pools: BTreeMap = BTreeMap::new(); + for (pool_id, params) in &raw.pools { + let ration = fraction::Fraction::from_str(¶ms.margin).unwrap(); + let mut owners = Vec::::new(); + for owner in ¶ms.owners { + owners.push(Ed25519KeyHash::from_hex(owner)?); + } + let mut relays = Vec::::new(); + for relay in ¶ms.relays { + if let Some((key, value)) = relay.iter().next() { + match key.as_str() { + "single host address" => { + let ipv4 = match value.IPv4.as_ref() { + Some(s) => Some(Ipv4::from_str(s)?), + _ => None + }; + let ipv6 = match value.IPv6.as_ref() { + Some(s) => Some(Ipv6::from_str(s)?), + _ => None + }; + relays.push(Relay::new_single_host_addr( + value.port, + ipv4, + ipv6 + )); + }, + _ => panic!("Only single host address relays are supported in cardano-node Relay JSON parsing") + } + } + } + let pool_metadata = match params.metadata.as_ref() { + Some(metadata) => Some(PoolMetadata::new( + Url::new(metadata.url.clone()).unwrap(), + PoolMetadataHash::from_hex(&metadata.hash)?, + )), + _ => None, + }; + let parsed_params = PoolParams::new( + Ed25519KeyHash::from_hex(¶ms.publicKey)?, + VRFKeyHash::from_hex(¶ms.vrf)?, + params.pledge, + params.cost, + UnitInterval::new(*ration.numer().unwrap(), *ration.denom().unwrap()), + RewardAccount::new( + match data.networkId.as_str() { + "Mainnet" => crate::NetworkId::mainnet().network as u8, + "Testnet" => crate::NetworkId::testnet().network as u8, + val => return Err(GenesisJSONError::ParseNetwork(val.to_string())), + }, + StakeCredential::new_pub_key(Ed25519KeyHash::from_hex( + ¶ms.rewardAccount.credential.keyHash, + )?), + ), + owners.into(), + relays, + pool_metadata, + ); + pools.insert(Ed25519KeyHash::from_hex(pool_id)?, parsed_params); + } + // 2) Get initial delegations + let mut stake: BTreeMap = BTreeMap::new(); + for (staking_key, pool_id) in &raw.stake { + stake.insert( + Ed25519KeyHash::from_hex(staking_key)?, + Ed25519KeyHash::from_hex(pool_id)?, + ); + } + Some(config::ShelleyGenesisStaking { stake, pools }) + } + _ => None, + }; + + let mut gen_delegs = BTreeMap::new(); + for (key, val) in data.genDelegs.iter() { + gen_delegs.insert( + Ed25519KeyHash::from_hex(key)?, + config::ShelleyGenesisDelegations { + delegate: Ed25519KeyHash::from_hex(&val.delegate)?, + vrf: VRFKeyHash::from_hex(&val.vrf)?, + }, + ); + } + Ok(config::ShelleyGenesisData { + active_slots_coeff: fraction::Fraction::from_str(&data.activeSlotsCoeff).unwrap(), + epoch_length: data.epochLength, + gen_delegs, + initial_funds, + max_kes_evolutions: data.maxKESEvolutions, + max_lovelace_supply: data.maxLovelaceSupply, + network_id, + network_magic: data.networkMagic, + protocol_params: config::ShelleyGenesisProtocolParameters { + a0: fraction::Fraction::from_str(&data.protocolParams.a0).unwrap(), + decentralisation_param: fraction::Fraction::from_str( + &data.protocolParams.decentralisationParam, + ) + .unwrap(), + e_max: data.protocolParams.eMax, + extra_entropy: config::ShelleyGenesisExtraEntropy { + tag: data.protocolParams.extraEntropy.tag, + }, + key_deposit: data.protocolParams.keyDeposit, + max_block_body_size: data.protocolParams.maxBlockBodySize, + max_block_header_size: data.protocolParams.maxBlockHeaderSize, + max_tx_size: data.protocolParams.maxTxSize, + min_fee_a: data.protocolParams.minFeeA, + min_fee_b: data.protocolParams.minFeeB, + min_pool_cost: data.protocolParams.minPoolCost, + min_utxo_value: data.protocolParams.minUTxOValue, + n_opt: data.protocolParams.nOpt, + pool_deposit: data.protocolParams.poolDeposit, + protocol_version: ProtocolVersion::new( + data.protocolParams.protocolVersion.major, + data.protocolParams.protocolVersion.minor, + ), + rho: fraction::Fraction::from_str(&data.protocolParams.rho).unwrap(), + tau: fraction::Fraction::from_str(&data.protocolParams.tau).unwrap(), + }, + security_param: data.securityParam, + slot_length: data.slotLength, + slots_per_kes_period: data.slotsPerKESPeriod, + staking, + system_start: data.systemStart.parse().expect("Failed to parse date"), + update_quorum: data.updateQuorum, + }) +} + +pub fn redeem_address_to_txid(pubkey: &Address) -> TransactionHash { + let txid = Blake2b256::new(&pubkey.to_raw_bytes()); + TransactionHash::from(*txid.as_hash_bytes()) +} + +#[cfg(test)] +mod test { + use super::*; + + fn get_test_genesis_data() -> &'static str { + include_str!("./test_data/test.json") + } + + #[test] + fn calc_address_txid() { + let hash = redeem_address_to_txid( + &Address::from_bech32("addr_test1qpefp65049pncyz95nyyww2e44sgumqr5kx8mcemm0fuumeftwv8zdtpqct0836wz8y56aakem2uejf604cee7cn2p3qp9p8te").unwrap(), + ); + assert_eq!( + hash.to_hex(), + "66dc6b2e628bf1fb6204797f1a07f8e949d9520a70e859ecbf3ea3076029871e" + ); + } + + #[test] + fn parse_test_genesis_files() { + let genesis_data = super::parse_genesis_data(get_test_genesis_data().as_bytes()).unwrap(); + + assert_eq!(genesis_data.epoch_length, 432000u64); + assert_eq!(genesis_data.network_id, 0); + assert_eq!(genesis_data.network_magic, 764824073u64); + + assert_eq!( + *genesis_data + .initial_funds + .iter() + .find(|(n, _)| n.to_hex() + == "605276322ac7882434173dcc6441905f6737689bd309b68ad8b3614fd8") + .unwrap() + .1, + 3000000000000000u64 + ); + } +} diff --git a/chain/rust/src/genesis/shelley/raw.rs b/chain/rust/src/genesis/shelley/raw.rs new file mode 100644 index 00000000..c89fd6d4 --- /dev/null +++ b/chain/rust/src/genesis/shelley/raw.rs @@ -0,0 +1,139 @@ +use crate::assets::Coin; +use serde::{Deserialize, Serialize}; +use serde_aux::prelude::*; +use std::collections::HashMap; + +/// Parsing of the JSON representation of the Shelley genesis block +/// Note: for a lot of these fields, I didn't check what the max valid size is in the Haskell code +/// so I just used u64 everywhere + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisData { + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub activeSlotsCoeff: String, + pub epochLength: u64, + pub genDelegs: HashMap, + pub initialFunds: HashMap, + pub maxKESEvolutions: u64, + pub maxLovelaceSupply: Coin, + pub networkId: String, + pub networkMagic: u64, + pub protocolParams: ShelleyGenesisProtocolParameters, + pub securityParam: u64, + pub slotLength: u64, + pub slotsPerKESPeriod: u64, + pub staking: Option, + pub systemStart: String, + pub updateQuorum: u64, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisDelegations { + pub delegate: String, + pub vrf: String, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisProtocolParameters { + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub a0: String, + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub decentralisationParam: String, + pub eMax: u64, + pub extraEntropy: ShelleyGenesisExtraEntropy, + pub keyDeposit: Coin, + pub maxBlockBodySize: u64, + pub maxBlockHeaderSize: u64, + pub maxTxSize: u64, + pub minFeeA: Coin, + pub minFeeB: Coin, + pub minPoolCost: Coin, + pub minUTxOValue: Coin, + pub nOpt: u64, + pub poolDeposit: Coin, + pub protocolVersion: ShelleyGenesisProtocolVersion, + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub rho: String, + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub tau: String, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisExtraEntropy { + pub tag: String, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisProtocolVersion { + pub major: u64, + pub minor: u64, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisStaking { + pub pools: HashMap, + pub stake: HashMap, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisPool { + pub cost: Coin, + // convert lossless JSON floats to string to avoid lossy Rust f64 + #[serde(deserialize_with = "deserialize_string_from_number")] + pub margin: String, + pub metadata: Option, + pub owners: Vec, + pub pledge: Coin, + pub publicKey: String, + pub relays: Vec, + pub rewardAccount: ShelleyGenesisRewardAccount, + pub vrf: String, +} + +// TODO: there are other relay types, but I can't find the JSON type for them +// and I can't find any usage of them in the wild anyway +// The key here defines the relay type +// ex: +// - single host address +type RelayTypeMap = HashMap; + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisPoolSingleHotsRelay { + pub IPv6: Option, + pub port: Option, + pub IPv4: Option, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisPoolMetadata { + pub hash: String, + pub url: String, +} +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisRewardAccount { + pub network: String, + pub credential: ShelleyGenesisCredential, +} +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +pub struct ShelleyGenesisCredential { + // for some reason there actually is a space in the JSON key emitted by the Haskell node + // both key hash and keyHash are accepted + #[serde(alias = "key hash")] + pub keyHash: String, +} diff --git a/chain/rust/src/genesis/shelley/test_data/test.json b/chain/rust/src/genesis/shelley/test_data/test.json new file mode 100644 index 00000000..09f234ef --- /dev/null +++ b/chain/rust/src/genesis/shelley/test_data/test.json @@ -0,0 +1,135 @@ +{ + "activeSlotsCoeff": 0.05, + "epochLength": 432000, + "genDelegs": { + "337bc5ef0f1abf205624555c13a37258c42b46b1259a6b1a6d82574e": { + "delegate": "41fd6bb31f34469320aa47cf6ccc3918e58a06d60ea1f2361efe2458", + "vrf": "7053e3ecd2b19db13e5338aa75fb518fc08b6c218f56ad65760d3eb074be95d4" + }, + "b5c3fed76c54bfdcda7993e4171eff2fb853f8ab920ab80047278a91": { + "delegate": "fcb677a90948d1d5b358912f54f6baaf762ecf5cd6579c93bcb49cef", + "vrf": "c7715f726e8e4f7745ccc646f4350758460de71de5694b244a47761fb106ec6e" + }, + "e34a75849b978bb20c366f531f978b3111b91aae0e469108dca8e433": { + "delegate": "81babf3c139646f0f0268abed36d2a54757492a3a68cda2438a37f7e", + "vrf": "ca336185cd781a6543b6c1e62ee1eee53e237d5d1fb065f08412d40d61b6ca06" + } + }, + "initialFunds": { + "007290ea8fa9433c1045a4c8473959ad608e6c03a58c7de33bdbd3ce6f295b987135610616f3c74e11c94d77b6ced5ccc93a7d719cfb135062": 300000000000, + "605276322ac7882434173dcc6441905f6737689bd309b68ad8b3614fd8": 3000000000000000, + "60a0f1aa7dca95017c11e7e373aebcf0c4568cf47ec12b94f8eb5bba8b": 3000000000000000, + "60ba957a0fff6816021b2afa7900beea68fd10f2d78fb5b64de0d2379c": 3000000000000000, + "00c8c47610a36034aac6fc58848bdae5c278d994ff502c05455e3b3ee8f8ed3a0eea0ef835ffa7bbfcde55f7fe9d2cc5d55ea62cecb42bab3c": 10000000000, + "004048ff89ca4f88e66598e620aa0c7128c2145d9a181ae9a4a81ca8e3e849af38840c5562dd382be37c9e76545c8191f9d8f6df1d20cfcee0": 10000000000, + "00ca6e1b1f320d543a24adeabc0aa4627635c7349b639f86f74bdfdd78d31b28c9619a58b3792a7394ab85deb36889c4d7b0632c8167b855d2": 10000000000, + "0007d781fe8e33883e371f9550c2f1087321fc32e06e80b65e349ccb027702d6880e86e77a0520efa37ede45002a1de43b68692e175b742e67": 10000000000, + "00627b2598dd71129167825160c564067d1d245e79cc237094815c5cb2b125e30ec2f4ce4059a069e08c3cd82cdfc9451bfb22487f8a25ceef": 10000000000, + "00c6cf7bd50f37f7e4cc161fc00f07e9b2226ba5552ccaf30d315fa0135bbc8cbd9ab5379f368fc8d3500c37a9d14074cc6ddad89e3686f0e0": 10000000000, + "005164ab186715c86378020956d892cf72f67636b78967d67cfe7360479130dc89cf7a9bc89109f939956b66f93293ade4c3920b72fd40beea": 10000000000, + "003dd38742e9848c6f12c13ddb1f9464fc0ce0bb92102768087975317e5a9f869fcd913562c9b0e0f01f77e5359ea780d37f9355f9702eff8b": 10000000000, + "0088e7e670b45cab2322b518ef7b6f66d30aec0d923dc463e467091a790f67796b9fa71224f2846cebbcf4950c11e040ee124d30f6e164bcd5": 10000000000, + "00c70b8421617802d3f23956cab1957e1d306cd4808589b41760e97927ebfd6053ba12b38288b2b6d5d4c4618d6a8ce59d50580e9c6f704af5": 10000000000, + "00c0933b8238f6f3332e48c34cf1a8e0555943b33cd4abc53aefb7d6124b7ce40dd496bdc02b34602f3a773ff7cccee873991e4c8866f3a70b": 10000000000, + "0069f7d7289de2f01cd1e0265ac5be943b41775abae0ce6b3eac0edee0ce9cadb7cdec2bded3ef8a7bbe3352869bfc1387754c9ee6b1782d9c": 10000000000, + "00709a7070005c652c27df73dbbde3319a90b127bea96aded1c5fb87a59c51dbcf90fa890174497f3f66a0dad06eb7f131e06567995e9c50a5": 10000000000, + "00fc576df3a279885a7a4d0fc500372daa1d96f26c6763542ecd2ad8551753024adea37c134edebb68dc0cfaed5a7009e8305fe1fed8d0ccd1": 10000000000, + "003346a630e6972bf38cce87219db1d63061e7cd324cad88c18e504f2990cac68e973f51256ca938683fa4ea12173d7d047d940fbb883bd0e8": 10000000000, + "0028b862d001e6a64a02b3560cbc532eab4557593477c39cc523e0b9fc527100898c11e731194171b908aad463770d6cbf7ec8871c4cb1e518": 10000000000, + "005e0e57040b06e9d71e0f28f126262838a68db0b52b4fd1b3877dda2203d5d7d4f19c5ee3a1ed51bb670779de19d40aaff2e5e9468cc05c5e": 10000000000, + "00367f65ab69b1e6715c8d5a14964214c9505ed17032266b3209a2c40dcbae9a2a881e603ff39d36e987bacfb87ee98051f222c5fe3efd350c": 10000000000, + "00c5c4ca287f3b53948b5468e5e23b1c87fe61ce52c0d9afd65d070795038751a619d463e91eaed0a774ebdb2f8e12a01a378a153bc3627323": 10000000000, + "00ef198fb7c35e1968308a0b75cfee54a46e13e86dd3354283300831d624165c357b5a0413906a0bfea8ba57587331f0836a186d632ed041b8": 10000000000 + }, + "maxKESEvolutions": 60, + "maxLovelaceSupply": 45000000000000000, + "networkId": "Testnet", + "networkMagic": 764824073, + "protocolParams": { + "a0": 0, + "decentralisationParam": 0, + "eMax": 18, + "extraEntropy": { + "tag": "NeutralNonce" + }, + "keyDeposit": 2000000, + "maxBlockBodySize": 65536, + "maxBlockHeaderSize": 1100, + "maxTxSize": 16384, + "minFeeA": 44, + "minFeeB": 155381, + "minPoolCost": 340000000, + "minUTxOValue": 1000000, + "nOpt": 100, + "poolDeposit": 500000000, + "protocolVersion": { + "major": 8, + "minor": 0 + }, + "rho": 0.003, + "tau": 0.20 + }, + "securityParam": 12, + "slotLength": 1, + "slotsPerKESPeriod": 129600, + "staking": { + "pools": { + "7301761068762f5900bde9eb7c1c15b09840285130f5b0f53606cc57": { + "cost": 340000000, + "margin": 3.0e-2, + "metadata": { + "hash": "b150b12a1301c4b1510ac8b9f53f7571cabb43455f6fd244cd8fd97504b1c869", + "url": "https://adalite.io/ADLT4-metadata.json" + }, + "owners": [ + "463a9695c9222183ee6e1523478722bebcb332fa3769f1d8ef40c7d0", + "5049c1dac0e597ee902f27a74a167cf135ae7c1717b0d3a417cd6c67" + ], + "pledge": 0, + "publicKey": "7301761068762f5900bde9eb7c1c15b09840285130f5b0f53606cc57", + "relays": [ + { + "single host address": { + "IPv6": null, + "port": 3003, + "IPv4": "54.228.75.154" + } + }, + { + "single host address": { + "IPv6": null, + "port": 3001, + "IPv4": "54.228.75.154" + } + }, + { + "single host address": { + "IPv6": null, + "port": 3003, + "IPv4": "34.249.11.89" + } + }, + { + "single host address": { + "IPv6": null, + "port": 3001, + "IPv4": "34.249.11.89" + } + } + ], + "rewardAccount": { + "credential": { + "key hash": "11a14edf73b08a0a27cb98b2c57eb37c780df18fcfcf6785ed5df84a" + }, + "network": "Testnet" + }, + "vrf": "c2b62ffa92ad18ffc117ea3abeb161a68885000a466f9c71db5e4731d6630061" + } + }, + "stake": { + "295b987135610616f3c74e11c94d77b6ced5ccc93a7d719cfb135062": "7301761068762f5900bde9eb7c1c15b09840285130f5b0f53606cc57" + } + }, + "systemStart": "2022-09-15T04:09:11.577484Z", + "updateQuorum": 2 +} \ No newline at end of file diff --git a/chain/rust/src/governance/mod.rs b/chain/rust/src/governance/mod.rs index 62417925..e0e6b2af 100644 --- a/chain/rust/src/governance/mod.rs +++ b/chain/rust/src/governance/mod.rs @@ -3,6 +3,12 @@ pub mod cbor_encodings; pub mod serialization; +pub mod utils; + +#[cfg(not(feature = "used_from_wasm"))] +use noop_proc_macro::wasm_bindgen; +#[cfg(feature = "used_from_wasm")] +use wasm_bindgen::prelude::wasm_bindgen; use crate::address::RewardAccount; use crate::assets::Coin; @@ -76,15 +82,11 @@ pub enum GovAction { impl GovAction { pub fn new_parameter_change_action( - gov_action_id: Option, - protocol_param_update: ProtocolParamUpdate, + action_id: Option, + update: ProtocolParamUpdate, policy_hash: Option, ) -> Self { - Self::ParameterChangeAction(ParameterChangeAction::new( - gov_action_id, - protocol_param_update, - policy_hash, - )) + Self::ParameterChangeAction(ParameterChangeAction::new(action_id, update, policy_hash)) } pub fn new_hard_fork_initiation_action( @@ -215,8 +217,8 @@ impl NoConfidence { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct ParameterChangeAction { - pub gov_action_id: Option, - pub protocol_param_update: ProtocolParamUpdate, + pub action_id: Option, + pub update: ProtocolParamUpdate, pub policy_hash: Option, #[serde(skip)] pub encodings: Option, @@ -224,13 +226,13 @@ pub struct ParameterChangeAction { impl ParameterChangeAction { pub fn new( - gov_action_id: Option, - protocol_param_update: ProtocolParamUpdate, + action_id: Option, + update: ProtocolParamUpdate, policy_hash: Option, ) -> Self { Self { - gov_action_id, - protocol_param_update, + action_id, + update, policy_hash, encodings: None, } @@ -324,7 +326,7 @@ impl UpdateCommittee { serde::Serialize, schemars::JsonSchema, )] -#[wasm_bindgen::prelude::wasm_bindgen] +#[wasm_bindgen] pub enum Vote { No, Yes, diff --git a/chain/rust/src/governance/serialization.rs b/chain/rust/src/governance/serialization.rs index dddc9f01..806a1d98 100644 --- a/chain/rust/src/governance/serialization.rs +++ b/chain/rust/src/governance/serialization.rs @@ -869,12 +869,11 @@ impl SerializeEmbeddedGroup for ParameterChangeAction { force_canonical, ), )?; - match &self.gov_action_id { + match &self.action_id { Some(x) => x.serialize(serializer, force_canonical), None => serializer.write_special(cbor_event::Special::Null), }?; - self.protocol_param_update - .serialize(serializer, force_canonical)?; + self.update.serialize(serializer, force_canonical)?; match &self.policy_hash { Some(x) => serializer.write_bytes_sz( x.to_raw_bytes(), @@ -932,7 +931,7 @@ impl DeserializeEmbeddedGroup for ParameterChangeAction { Ok(Some(tag_encoding)) })() .map_err(|e| e.annotate("tag"))?; - let gov_action_id = (|| -> Result<_, DeserializeError> { + let action_id = (|| -> Result<_, DeserializeError> { Ok(match raw.cbor_type()? != cbor_event::Type::Special { true => Some(GovActionId::deserialize(raw)?), false => { @@ -943,9 +942,9 @@ impl DeserializeEmbeddedGroup for ParameterChangeAction { } }) })() - .map_err(|e| e.annotate("gov_action_id"))?; - let protocol_param_update = ProtocolParamUpdate::deserialize(raw) - .map_err(|e: DeserializeError| e.annotate("protocol_param_update"))?; + .map_err(|e| e.annotate("action_id"))?; + let update = ProtocolParamUpdate::deserialize(raw) + .map_err(|e: DeserializeError| e.annotate("update"))?; let (policy_hash, policy_hash_encoding) = (|| -> Result<_, DeserializeError> { Ok(match raw.cbor_type()? != cbor_event::Type::Special { true => Result::<_, DeserializeError>::Ok( @@ -970,8 +969,8 @@ impl DeserializeEmbeddedGroup for ParameterChangeAction { })() .map_err(|e| e.annotate("policy_hash"))?; Ok(ParameterChangeAction { - gov_action_id, - protocol_param_update, + action_id, + update, policy_hash, encodings: Some(ParameterChangeActionEncoding { len_encoding, diff --git a/chain/rust/src/governance/utils.rs b/chain/rust/src/governance/utils.rs new file mode 100644 index 00000000..12391008 --- /dev/null +++ b/chain/rust/src/governance/utils.rs @@ -0,0 +1,48 @@ +use cml_crypto::{Ed25519KeyHash, ScriptHash}; + +use super::{GovAction, Voter}; + +impl GovAction { + pub fn script_hash(&self) -> Option<&ScriptHash> { + match self { + Self::ParameterChangeAction(action) => action.policy_hash.as_ref(), + Self::HardForkInitiationAction(_action) => None, + Self::TreasuryWithdrawalsAction(action) => action.policy_hash.as_ref(), + Self::NoConfidence(_action) => None, + // TODO: unsure if these count? they can be credentials but maybe it's not needed to sign + Self::UpdateCommittee(_action) => None, + // TODO: unsure if this counts? + //Self::NewConstitution(action) => action.constitution.script_hash, + Self::NewConstitution(_action) => None, + Self::InfoAction { .. } => None, + } + } +} + +impl Voter { + pub fn key_hash(&self) -> Option<&Ed25519KeyHash> { + match self { + Self::ConstitutionalCommitteeHotKeyHash { + ed25519_key_hash, .. + } => Some(ed25519_key_hash), + Self::ConstitutionalCommitteeHotScriptHash { .. } => None, + Self::DRepKeyHash { + ed25519_key_hash, .. + } => Some(ed25519_key_hash), + Self::DRepScriptHash { .. } => None, + Self::StakingPoolKeyHash { + ed25519_key_hash, .. + } => Some(ed25519_key_hash), + } + } + + pub fn script_hash(&self) -> Option<&ScriptHash> { + match self { + Self::ConstitutionalCommitteeHotKeyHash { .. } => None, + Self::ConstitutionalCommitteeHotScriptHash { script_hash, .. } => Some(script_hash), + Self::DRepKeyHash { .. } => None, + Self::DRepScriptHash { script_hash, .. } => Some(script_hash), + Self::StakingPoolKeyHash { .. } => None, + } + } +} diff --git a/chain/rust/src/json/metadatums.rs b/chain/rust/src/json/metadatums.rs index 06cadcf6..6f5b1980 100644 --- a/chain/rust/src/json/metadatums.rs +++ b/chain/rust/src/json/metadatums.rs @@ -1,3 +1,6 @@ +#[cfg(not(feature = "used_from_wasm"))] +use noop_proc_macro::wasm_bindgen; +#[cfg(feature = "used_from_wasm")] use wasm_bindgen::prelude::wasm_bindgen; use crate::{ diff --git a/chain/rust/src/json/plutus_datums.rs b/chain/rust/src/json/plutus_datums.rs index eb222229..af2f8ba9 100644 --- a/chain/rust/src/json/plutus_datums.rs +++ b/chain/rust/src/json/plutus_datums.rs @@ -6,6 +6,9 @@ use crate::{ use std::collections::BTreeMap; use std::str::FromStr; +#[cfg(not(feature = "used_from_wasm"))] +use noop_proc_macro::wasm_bindgen; +#[cfg(feature = "used_from_wasm")] use wasm_bindgen::prelude::wasm_bindgen; /// JSON <-> PlutusData conversion schemas. 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..ae4850f2 100644 --- a/chain/rust/src/plutus/mod.rs +++ b/chain/rust/src/plutus/mod.rs @@ -5,6 +5,11 @@ pub mod cbor_encodings; pub mod serialization; pub mod utils; +#[cfg(not(feature = "used_from_wasm"))] +use noop_proc_macro::wasm_bindgen; +#[cfg(feature = "used_from_wasm")] +use wasm_bindgen::prelude::wasm_bindgen; + use self::cbor_encodings::{ LegacyRedeemerEncoding, PlutusV3ScriptEncoding, RedeemerKeyEncoding, RedeemerValEncoding, }; @@ -18,34 +23,68 @@ 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, Default)] 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()) + } +} + +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() } } @@ -101,7 +140,7 @@ impl ExUnits { serde::Serialize, schemars::JsonSchema, )] -#[wasm_bindgen::prelude::wasm_bindgen] +#[wasm_bindgen] pub enum Language { PlutusV1, PlutusV2, @@ -210,10 +249,6 @@ pub struct PlutusV1Script { } impl PlutusV1Script { - pub fn get(&self) -> &Vec { - &self.inner - } - pub fn new(inner: Vec) -> Self { Self { inner, @@ -283,10 +318,6 @@ pub struct PlutusV2Script { } impl PlutusV2Script { - pub fn get(&self) -> &Vec { - &self.inner - } - pub fn new(inner: Vec) -> Self { Self { inner, @@ -356,10 +387,6 @@ pub struct PlutusV3Script { } impl PlutusV3Script { - pub fn get(&self) -> &Vec { - &self.inner - } - pub fn new(inner: Vec) -> Self { Self { inner, @@ -455,7 +482,7 @@ impl RedeemerKey { serde::Serialize, schemars::JsonSchema, )] -#[wasm_bindgen::prelude::wasm_bindgen] +#[wasm_bindgen] pub enum RedeemerTag { Spend, Mint, diff --git a/chain/rust/src/plutus/serialization.rs b/chain/rust/src/plutus/serialization.rs index 8e187e26..c7fecb5c 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).unsigned_abs(), + 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()); + } + 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..300d4e63 100644 --- a/chain/rust/src/plutus/utils.rs +++ b/chain/rust/src/plutus/utils.rs @@ -7,12 +7,12 @@ 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::*, Int}; 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,69 @@ 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 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 { @@ -446,19 +452,49 @@ impl From for PlutusScript { impl PlutusV1Script { pub fn hash(&self) -> ScriptHash { - hash_script(ScriptHashNamespace::PlutusV1, self.get()) + hash_script(ScriptHashNamespace::PlutusV1, self.to_raw_bytes()) } } impl PlutusV2Script { pub fn hash(&self) -> ScriptHash { - hash_script(ScriptHashNamespace::PlutusV2, self.get()) + hash_script(ScriptHashNamespace::PlutusV2, self.to_raw_bytes()) } } impl PlutusV3Script { pub fn hash(&self) -> ScriptHash { - hash_script(ScriptHashNamespace::PlutusV3, self.get()) + hash_script(ScriptHashNamespace::PlutusV3, self.to_raw_bytes()) + } +} + +impl RawBytesEncoding for PlutusV1Script { + fn to_raw_bytes(&self) -> &[u8] { + self.inner.as_ref() + } + + fn from_raw_bytes(bytes: &[u8]) -> Result { + Ok(Self::new(bytes.to_vec())) + } +} + +impl RawBytesEncoding for PlutusV2Script { + fn to_raw_bytes(&self) -> &[u8] { + self.inner.as_ref() + } + + fn from_raw_bytes(bytes: &[u8]) -> Result { + Ok(Self::new(bytes.to_vec())) + } +} + +impl RawBytesEncoding for PlutusV3Script { + fn to_raw_bytes(&self) -> &[u8] { + self.inner.as_ref() + } + + fn from_raw_bytes(bytes: &[u8]) -> Result { + Ok(Self::new(bytes.to_vec())) } } @@ -489,6 +525,34 @@ 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 { @@ -679,12 +743,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, @@ -697,8 +760,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/chain/rust/src/transaction/mod.rs b/chain/rust/src/transaction/mod.rs index 7590dce4..9994be7d 100644 --- a/chain/rust/src/transaction/mod.rs +++ b/chain/rust/src/transaction/mod.rs @@ -143,7 +143,9 @@ pub enum NativeScript { ScriptAll(ScriptAll), ScriptAny(ScriptAny), ScriptNOfK(ScriptNOfK), + /// Timelock validity intervals are half-open intervals [a, b). This field specifies the left (included) endpoint a. ScriptInvalidBefore(ScriptInvalidBefore), + /// Timelock validity intervals are half-open intervals [a, b). This field specifies the right (excluded) endpoint b. ScriptInvalidHereafter(ScriptInvalidHereafter), } @@ -164,10 +166,12 @@ impl NativeScript { Self::ScriptNOfK(ScriptNOfK::new(n, native_scripts)) } + /// Timelock validity intervals are half-open intervals [a, b). This field specifies the left (included) endpoint a. pub fn new_script_invalid_before(before: Slot) -> Self { Self::ScriptInvalidBefore(ScriptInvalidBefore::new(before)) } + /// Timelock validity intervals are half-open intervals [a, b). This field specifies the right (excluded) endpoint b. pub fn new_script_invalid_hereafter(after: Slot) -> Self { Self::ScriptInvalidHereafter(ScriptInvalidHereafter::new(after)) } diff --git a/chain/rust/src/utils.rs b/chain/rust/src/utils.rs index ab3b184c..15750f60 100644 --- a/chain/rust/src/utils.rs +++ b/chain/rust/src/utils.rs @@ -2,9 +2,9 @@ use cbor_event::{de::Deserializer, se::Serializer, Sz}; use cml_core::{ error::{DeserializeError, DeserializeFailure}, serialization::{fit_sz, sz_max, Deserialize, LenEncoding, Serialize}, - Int, + Int, Slot, }; -use cml_crypto::{RawBytesEncoding, ScriptHash}; +use cml_crypto::{Ed25519KeyHash, RawBytesEncoding, ScriptHash}; use derivative::Derivative; use std::io::{BufRead, Seek, Write}; use std::iter::IntoIterator; @@ -12,7 +12,7 @@ use std::iter::IntoIterator; use crate::{ crypto::hash::{hash_script, ScriptHashNamespace}, plutus::{Language, PlutusScript, PlutusV1Script, PlutusV2Script}, - NativeScript, Script, + NativeScript, Script, SubCoin, }; impl Script { @@ -41,6 +41,57 @@ impl NativeScript { pub fn hash(&self) -> ScriptHash { hash_script(ScriptHashNamespace::NativeScript, &self.to_cbor_bytes()) } + + pub fn verify( + &self, + lower_bound: Option, + upper_bound: Option, + key_hashes: &Vec, + ) -> bool { + fn verify_helper( + script: &NativeScript, + lower_bound: Option, + upper_bound: Option, + key_hashes: &Vec, + ) -> bool { + match &script { + NativeScript::ScriptPubkey(pub_key) => { + key_hashes.contains(&pub_key.ed25519_key_hash) + } + NativeScript::ScriptAll(script_all) => { + script_all.native_scripts.iter().all(|sub_script| { + verify_helper(sub_script, lower_bound, upper_bound, key_hashes) + }) + } + NativeScript::ScriptAny(script_any) => { + script_any.native_scripts.iter().any(|sub_script| { + verify_helper(sub_script, lower_bound, upper_bound, key_hashes) + }) + } + NativeScript::ScriptNOfK(script_atleast) => { + script_atleast + .native_scripts + .iter() + .map(|sub_script| { + verify_helper(sub_script, lower_bound, upper_bound, key_hashes) + }) + .filter(|r| *r) + .count() + >= script_atleast.n as usize + } + NativeScript::ScriptInvalidBefore(timelock_start) => match lower_bound { + Some(tx_slot) => tx_slot >= timelock_start.before, + _ => false, + }, + NativeScript::ScriptInvalidHereafter(timelock_expiry) => match upper_bound { + Some(tx_slot) => tx_slot < timelock_expiry.after, + _ => false, + }, + } + } + + verify_helper(self, lower_bound, upper_bound, key_hashes) + } } impl From for Script { @@ -920,6 +971,20 @@ impl Deserialize for NonemptySetRawBytes { } } +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/Cargo.toml b/chain/wasm/Cargo.toml index 3183820f..3dce0a78 100644 --- a/chain/wasm/Cargo.toml +++ b/chain/wasm/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["cardano"] crate-type = ["cdylib", "rlib"] [dependencies] -cml-chain = { path = "../rust", version = "5.3.1" } +cml-chain = { path = "../rust", version = "5.3.1", features = ["used_from_wasm"] } cml-core = { path = "../../core/rust", version = "5.3.1" } cml-core-wasm = { path = "../../core/wasm", version = "5.3.1" } # TODO: remove this dependency if possible to reduce confusion? maybe pub export necessary things in crypto-wasm? @@ -22,7 +22,7 @@ cml-crypto = { path = "../../crypto/rust", version = "5.3.1" } cml-crypto-wasm = { path = "../../crypto/wasm", version = "5.3.1" } cbor_event = "2.4.0" hex = "0.4.0" -wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.87" } linked-hash-map = "0.5.3" serde_json = "1.0.57" serde-wasm-bindgen = "0.4.5" diff --git a/chain/wasm/json-gen/src/lib.rs b/chain/wasm/json-gen/src/lib.rs index 8062b4c6..0fe20163 100644 --- a/chain/wasm/json-gen/src/lib.rs +++ b/chain/wasm/json-gen/src/lib.rs @@ -70,8 +70,8 @@ pub fn export_schemas() { gen_json_schema!(cml_chain::certs::AuthCommitteeHotCert); gen_json_schema!(cml_chain::certs::Certificate); gen_json_schema!(cml_chain::certs::Credential); + gen_json_schema!(cml_chain::certs::DNSName); gen_json_schema!(cml_chain::certs::DRep); - gen_json_schema!(cml_chain::certs::DnsName); gen_json_schema!(cml_chain::certs::Ipv4); gen_json_schema!(cml_chain::certs::Ipv6); gen_json_schema!(cml_chain::certs::MultiHostName); diff --git a/chain/wasm/src/assets/mod.rs b/chain/wasm/src/assets/mod.rs index 62966512..b946a230 100644 --- a/chain/wasm/src/assets/mod.rs +++ b/chain/wasm/src/assets/mod.rs @@ -18,10 +18,3 @@ pub struct AssetName(cml_chain::assets::AssetName); impl_wasm_cbor_json_api!(AssetName); impl_wasm_conversions!(cml_chain::assets::AssetName, AssetName); - -#[wasm_bindgen] -impl AssetName { - pub fn get(&self) -> Vec { - self.0.get().clone() - } -} diff --git a/chain/wasm/src/assets/utils.rs b/chain/wasm/src/assets/utils.rs index b5393091..8fb40e6b 100644 --- a/chain/wasm/src/assets/utils.rs +++ b/chain/wasm/src/assets/utils.rs @@ -6,7 +6,9 @@ use std::{ use crate::{assets::AssetName, AssetNameList, MapAssetNameToNonZeroInt64, PolicyId, PolicyIdList}; use wasm_bindgen::{prelude::wasm_bindgen, JsError, JsValue}; -use cml_core_wasm::{impl_wasm_cbor_json_api, impl_wasm_conversions, impl_wasm_map}; +use cml_core_wasm::{ + impl_raw_bytes_api, impl_wasm_cbor_json_api, impl_wasm_conversions, impl_wasm_map, +}; use super::Coin; @@ -25,15 +27,6 @@ impl_wasm_map!( #[wasm_bindgen] impl AssetName { - /** - * Create an AssetName from raw bytes. 64 byte maximum. - */ - pub fn from_bytes(bytes: Vec) -> Result { - cml_chain::assets::AssetName::try_from(bytes) - .map(Into::into) - .map_err(Into::into) - } - /** * Create an AssetName from utf8 string. 64 byte (not char!) maximum. */ @@ -55,6 +48,8 @@ impl AssetName { } } +impl_raw_bytes_api!(cml_chain::assets::AssetName, AssetName); + #[derive(Clone, Debug)] #[wasm_bindgen] pub struct MultiAsset(cml_chain::assets::MultiAsset); diff --git a/chain/wasm/src/auxdata/mod.rs b/chain/wasm/src/auxdata/mod.rs index 1f3a6e33..83d19ab2 100644 --- a/chain/wasm/src/auxdata/mod.rs +++ b/chain/wasm/src/auxdata/mod.rs @@ -26,9 +26,9 @@ impl AuxiliaryData { )) } - pub fn new_shelley_m_a(shelley_m_a: &ShelleyMaFormatAuxData) -> Self { - Self(cml_chain::auxdata::AuxiliaryData::new_shelley_m_a( - shelley_m_a.clone().into(), + pub fn new_shelley_ma(shelley_ma: &ShelleyMAFormatAuxData) -> Self { + Self(cml_chain::auxdata::AuxiliaryData::new_shelley_ma( + shelley_ma.clone().into(), )) } @@ -53,10 +53,10 @@ impl AuxiliaryData { } } - pub fn as_shelley_m_a(&self) -> Option { + pub fn as_shelley_ma(&self) -> Option { match &self.0 { - cml_chain::auxdata::AuxiliaryData::ShelleyMA(shelley_m_a) => { - Some(shelley_m_a.clone().into()) + cml_chain::auxdata::AuxiliaryData::ShelleyMA(shelley_ma) => { + Some(shelley_ma.clone().into()) } _ => None, } @@ -145,17 +145,17 @@ pub type ShelleyFormatAuxData = Metadata; #[derive(Clone, Debug)] #[wasm_bindgen] -pub struct ShelleyMaFormatAuxData(cml_chain::auxdata::ShelleyMaFormatAuxData); +pub struct ShelleyMAFormatAuxData(cml_chain::auxdata::ShelleyMAFormatAuxData); -impl_wasm_cbor_json_api!(ShelleyMaFormatAuxData); +impl_wasm_cbor_json_api!(ShelleyMAFormatAuxData); impl_wasm_conversions!( - cml_chain::auxdata::ShelleyMaFormatAuxData, - ShelleyMaFormatAuxData + cml_chain::auxdata::ShelleyMAFormatAuxData, + ShelleyMAFormatAuxData ); #[wasm_bindgen] -impl ShelleyMaFormatAuxData { +impl ShelleyMAFormatAuxData { pub fn transaction_metadata(&self) -> Metadata { self.0.transaction_metadata.clone().into() } @@ -165,7 +165,7 @@ impl ShelleyMaFormatAuxData { } pub fn new(transaction_metadata: &Metadata, auxiliary_scripts: &NativeScriptList) -> Self { - Self(cml_chain::auxdata::ShelleyMaFormatAuxData::new( + Self(cml_chain::auxdata::ShelleyMAFormatAuxData::new( transaction_metadata.clone().into(), auxiliary_scripts.clone().into(), )) diff --git a/chain/wasm/src/builders/mod.rs b/chain/wasm/src/builders/mod.rs index 30911f48..960df211 100644 --- a/chain/wasm/src/builders/mod.rs +++ b/chain/wasm/src/builders/mod.rs @@ -2,7 +2,9 @@ pub mod certificate_builder; pub mod input_builder; pub mod mint_builder; pub mod output_builder; +pub mod proposal_builder; pub mod redeemer_builder; pub mod tx_builder; +pub mod vote_builder; pub mod withdrawal_builder; pub mod witness_builder; diff --git a/chain/wasm/src/builders/proposal_builder.rs b/chain/wasm/src/builders/proposal_builder.rs new file mode 100644 index 00000000..abf6d23d --- /dev/null +++ b/chain/wasm/src/builders/proposal_builder.rs @@ -0,0 +1,99 @@ +use wasm_bindgen::{prelude::wasm_bindgen, JsError}; + +use cml_core_wasm::impl_wasm_conversions; + +use crate::{ + governance::ProposalProcedure, plutus::PlutusData, transaction::NativeScript, RequiredSigners, +}; + +use super::witness_builder::{NativeScriptWitnessInfo, PartialPlutusWitness}; + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct ProposalBuilderResult(cml_chain::builders::proposal_builder::ProposalBuilderResult); + +impl_wasm_conversions!( + cml_chain::builders::proposal_builder::ProposalBuilderResult, + ProposalBuilderResult +); + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct ProposalBuilder(cml_chain::builders::proposal_builder::ProposalBuilder); + +impl_wasm_conversions!( + cml_chain::builders::proposal_builder::ProposalBuilder, + ProposalBuilder +); + +#[wasm_bindgen] +impl ProposalBuilder { + pub fn new() -> Self { + Self(cml_chain::builders::proposal_builder::ProposalBuilder::new()) + } + + pub fn with_proposal(&self, proposal: ProposalProcedure) -> Result { + self.0 + .clone() + .with_proposal(proposal.clone().into()) + .map(Into::into) + .map_err(Into::into) + } + + pub fn with_native_script_proposal( + &self, + proposal: ProposalProcedure, + native_script: NativeScript, + witness_info: NativeScriptWitnessInfo, + ) -> Result { + self.0 + .clone() + .with_native_script_proposal( + proposal.clone().into(), + native_script.clone().into(), + witness_info.clone().into(), + ) + .map(Into::into) + .map_err(Into::into) + } + + pub fn with_plutus_proposal( + &self, + proposal: &ProposalProcedure, + partial_witness: &PartialPlutusWitness, + required_signers: &RequiredSigners, + datum: &PlutusData, + ) -> Result { + self.0 + .clone() + .with_plutus_proposal( + proposal.clone().into(), + partial_witness.clone().into(), + required_signers.clone().into(), + datum.clone().into(), + ) + .map(Into::into) + .map_err(Into::into) + } + + pub fn with_plutus_proposal_inline_datum( + &self, + proposal: ProposalProcedure, + partial_witness: PartialPlutusWitness, + required_signers: RequiredSigners, + ) -> Result { + self.0 + .clone() + .with_plutus_proposal_inline_datum( + proposal.clone().into(), + partial_witness.clone().into(), + required_signers.clone().into(), + ) + .map(Into::into) + .map_err(Into::into) + } + + pub fn build(&self) -> ProposalBuilderResult { + self.0.clone().build().into() + } +} diff --git a/chain/wasm/src/builders/redeemer_builder.rs b/chain/wasm/src/builders/redeemer_builder.rs index 7c6be9b1..26e673a4 100644 --- a/chain/wasm/src/builders/redeemer_builder.rs +++ b/chain/wasm/src/builders/redeemer_builder.rs @@ -1,6 +1,7 @@ use super::{ certificate_builder::CertificateBuilderResult, input_builder::InputBuilderResult, - mint_builder::MintBuilderResult, withdrawal_builder::WithdrawalBuilderResult, + mint_builder::MintBuilderResult, proposal_builder::ProposalBuilderResult, + vote_builder::VoteBuilderResult, withdrawal_builder::WithdrawalBuilderResult, }; use crate::plutus::{ExUnits, LegacyRedeemer, PlutusData, RedeemerTag, Redeemers}; use cml_core_wasm::impl_wasm_conversions; @@ -92,6 +93,14 @@ impl RedeemerSetBuilder { self.0.add_cert(result.as_ref()); } + pub fn add_proposal(&mut self, result: &ProposalBuilderResult) { + self.0.add_proposal(result.as_ref()); + } + + pub fn add_vote(&mut self, result: &VoteBuilderResult) { + self.0.add_vote(result.as_ref()); + } + pub fn build(&self, default_to_dummy_exunits: bool) -> Result { self.0 .build(default_to_dummy_exunits) diff --git a/chain/wasm/src/builders/tx_builder.rs b/chain/wasm/src/builders/tx_builder.rs index 1d084053..b6091e33 100644 --- a/chain/wasm/src/builders/tx_builder.rs +++ b/chain/wasm/src/builders/tx_builder.rs @@ -10,7 +10,8 @@ use crate::{ builders::{ certificate_builder::CertificateBuilderResult, input_builder::InputBuilderResult, mint_builder::MintBuilderResult, output_builder::SingleOutputBuilderResult, - redeemer_builder::RedeemerWitnessKey, withdrawal_builder::WithdrawalBuilderResult, + proposal_builder::ProposalBuilderResult, redeemer_builder::RedeemerWitnessKey, + vote_builder::VoteBuilderResult, withdrawal_builder::WithdrawalBuilderResult, witness_builder::TransactionWitnessSetBuilder, }, crypto::{BootstrapWitness, Vkeywitness}, @@ -40,6 +41,14 @@ impl TransactionUnspentOutput { ) .into() } + + pub fn input(&self) -> TransactionInput { + self.0.input.clone().into() + } + + pub fn output(&self) -> TransactionOutput { + self.0.output.clone().into() + } } #[wasm_bindgen] @@ -203,6 +212,14 @@ impl TransactionBuilder { self.0.add_cert(result.clone().into()) } + pub fn add_proposal(&mut self, result: ProposalBuilderResult) { + self.0.add_proposal(result.clone().into()) + } + + pub fn add_vote(&mut self, result: VoteBuilderResult) { + self.0.add_vote(result.clone().into()) + } + pub fn get_withdrawals(&self) -> Option { self.0.get_withdrawals().map(|wd| wd.into()) } diff --git a/chain/wasm/src/builders/vote_builder.rs b/chain/wasm/src/builders/vote_builder.rs new file mode 100644 index 00000000..88774f97 --- /dev/null +++ b/chain/wasm/src/builders/vote_builder.rs @@ -0,0 +1,120 @@ +use wasm_bindgen::{prelude::wasm_bindgen, JsError}; + +use cml_core_wasm::impl_wasm_conversions; + +use crate::{ + governance::{GovActionId, Voter, VotingProcedure}, + plutus::PlutusData, + transaction::NativeScript, + RequiredSigners, +}; + +use super::witness_builder::{NativeScriptWitnessInfo, PartialPlutusWitness}; + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct VoteBuilderResult(cml_chain::builders::vote_builder::VoteBuilderResult); + +impl_wasm_conversions!( + cml_chain::builders::vote_builder::VoteBuilderResult, + VoteBuilderResult +); + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct VoteBuilder(cml_chain::builders::vote_builder::VoteBuilder); + +impl_wasm_conversions!(cml_chain::builders::vote_builder::VoteBuilder, VoteBuilder); + +#[wasm_bindgen] +impl VoteBuilder { + pub fn new() -> Self { + Self(cml_chain::builders::vote_builder::VoteBuilder::new()) + } + + pub fn with_vote( + &self, + voter: &Voter, + gov_action_id: &GovActionId, + procedure: &VotingProcedure, + ) -> Result { + self.0 + .clone() + .with_vote( + voter.clone().into(), + gov_action_id.clone().into(), + procedure.clone().into(), + ) + .map(Into::into) + .map_err(Into::into) + } + + pub fn with_native_script_vote( + &self, + voter: &Voter, + gov_action_id: &GovActionId, + procedure: &VotingProcedure, + native_script: NativeScript, + witness_info: NativeScriptWitnessInfo, + ) -> Result { + self.0 + .clone() + .with_native_script_vote( + voter.clone().into(), + gov_action_id.clone().into(), + procedure.clone().into(), + native_script.clone().into(), + witness_info.clone().into(), + ) + .map(Into::into) + .map_err(Into::into) + } + + pub fn with_plutus_vote( + &self, + voter: &Voter, + gov_action_id: &GovActionId, + procedure: &VotingProcedure, + partial_witness: &PartialPlutusWitness, + required_signers: &RequiredSigners, + datum: &PlutusData, + ) -> Result { + self.0 + .clone() + .with_plutus_vote( + voter.clone().into(), + gov_action_id.clone().into(), + procedure.clone().into(), + partial_witness.clone().into(), + required_signers.clone().into(), + datum.clone().into(), + ) + .map(Into::into) + .map_err(Into::into) + } + + pub fn with_plutus_vote_inline_datum( + &self, + voter: &Voter, + gov_action_id: &GovActionId, + procedure: &VotingProcedure, + partial_witness: PartialPlutusWitness, + required_signers: RequiredSigners, + ) -> Result { + self.0 + .clone() + .with_plutus_vote_inline_datum( + voter.clone().into(), + gov_action_id.clone().into(), + procedure.clone().into(), + partial_witness.clone().into(), + required_signers.clone().into(), + ) + .map(Into::into) + .map_err(Into::into) + } + + pub fn build(&self) -> VoteBuilderResult { + self.0.clone().build().into() + } +} diff --git a/chain/wasm/src/certs/mod.rs b/chain/wasm/src/certs/mod.rs index 16fb9470..cc0eb2bd 100644 --- a/chain/wasm/src/certs/mod.rs +++ b/chain/wasm/src/certs/mod.rs @@ -48,18 +48,21 @@ impl_wasm_conversions!(cml_chain::certs::Certificate, Certificate); #[wasm_bindgen] impl Certificate { + /// Will be deprecated in the next era. Use RegCert instead which takes an explicit deposit amount, as that can change. pub fn new_stake_registration(stake_credential: &StakeCredential) -> Self { Self(cml_chain::certs::Certificate::new_stake_registration( stake_credential.clone().into(), )) } + /// Will be deprecated in the next era. Use UnregCert instead which takes an explicit deposit amount, as that can change. pub fn new_stake_deregistration(stake_credential: &StakeCredential) -> Self { Self(cml_chain::certs::Certificate::new_stake_deregistration( stake_credential.clone().into(), )) } + /// Delegate to a take pool only pub fn new_stake_delegation(stake_credential: &StakeCredential, pool: &Ed25519KeyHash) -> Self { Self(cml_chain::certs::Certificate::new_stake_delegation( stake_credential.clone().into(), @@ -80,20 +83,23 @@ impl Certificate { )) } - pub fn new_reg_cert(stake_credential: &StakeCredential, coin: Coin) -> Self { + /// Registers a stake credential. + pub fn new_reg_cert(stake_credential: &StakeCredential, deposit: Coin) -> Self { Self(cml_chain::certs::Certificate::new_reg_cert( stake_credential.clone().into(), - coin, + deposit, )) } - pub fn new_unreg_cert(stake_credential: &StakeCredential, coin: Coin) -> Self { + /// Unregisters a stake credential. + pub fn new_unreg_cert(stake_credential: &StakeCredential, deposit: Coin) -> Self { Self(cml_chain::certs::Certificate::new_unreg_cert( stake_credential.clone().into(), - coin, + deposit, )) } + /// Delegate to a DRep for voting only pub fn new_vote_deleg_cert(stake_credential: &StakeCredential, d_rep: &DRep) -> Self { Self(cml_chain::certs::Certificate::new_vote_deleg_cert( stake_credential.clone().into(), @@ -101,6 +107,7 @@ impl Certificate { )) } + /// Delegate to a stake pool and a DRep pub fn new_stake_vote_deleg_cert( stake_credential: &StakeCredential, pool: &Ed25519KeyHash, @@ -113,42 +120,45 @@ impl Certificate { )) } + /// Register a stake credential and delegate to a pool in a single cert pub fn new_stake_reg_deleg_cert( stake_credential: &StakeCredential, pool: &Ed25519KeyHash, - coin: Coin, + deposit: Coin, ) -> Self { Self(cml_chain::certs::Certificate::new_stake_reg_deleg_cert( stake_credential.clone().into(), pool.clone().into(), - coin, + deposit, )) } + /// Register a stake credential and delegate to a DRep in a single cert pub fn new_vote_reg_deleg_cert( stake_credential: &StakeCredential, d_rep: &DRep, - coin: Coin, + deposit: Coin, ) -> Self { Self(cml_chain::certs::Certificate::new_vote_reg_deleg_cert( stake_credential.clone().into(), d_rep.clone().into(), - coin, + deposit, )) } + /// Register a stake credential and delegate to a pool and a DRep in a single cert pub fn new_stake_vote_reg_deleg_cert( stake_credential: &StakeCredential, pool: &Ed25519KeyHash, d_rep: &DRep, - coin: Coin, + deposit: Coin, ) -> Self { Self( cml_chain::certs::Certificate::new_stake_vote_reg_deleg_cert( stake_credential.clone().into(), pool.clone().into(), d_rep.clone().into(), - coin, + deposit, ), ) } @@ -177,20 +187,20 @@ impl Certificate { pub fn new_reg_drep_cert( drep_credential: &DrepCredential, - coin: Coin, + deposit: Coin, anchor: Option, ) -> Self { Self(cml_chain::certs::Certificate::new_reg_drep_cert( drep_credential.clone().into(), - coin, + deposit, anchor.map(Into::into), )) } - pub fn new_unreg_drep_cert(drep_credential: &DrepCredential, coin: Coin) -> Self { + pub fn new_unreg_drep_cert(drep_credential: &DrepCredential, deposit: Coin) -> Self { Self(cml_chain::certs::Certificate::new_unreg_drep_cert( drep_credential.clone().into(), - coin, + deposit, )) } @@ -462,6 +472,21 @@ pub enum CredentialKind { Script, } +#[derive(Clone, Debug)] +#[wasm_bindgen] +pub struct DNSName(cml_chain::certs::DNSName); + +impl_wasm_cbor_json_api!(DNSName); + +impl_wasm_conversions!(cml_chain::certs::DNSName, DNSName); + +#[wasm_bindgen] +impl DNSName { + pub fn get(&self) -> String { + self.0.get().clone() + } +} + #[derive(Clone, Debug)] #[wasm_bindgen] pub struct DRep(cml_chain::certs::DRep); @@ -522,21 +547,6 @@ pub enum DRepKind { AlwaysNoConfidence, } -#[derive(Clone, Debug)] -#[wasm_bindgen] -pub struct DnsName(cml_chain::certs::DnsName); - -impl_wasm_cbor_json_api!(DnsName); - -impl_wasm_conversions!(cml_chain::certs::DnsName, DnsName); - -#[wasm_bindgen] -impl DnsName { - pub fn get(&self) -> String { - self.0.get().clone() - } -} - pub type DrepCredential = Credential; #[derive(Clone, Debug)] @@ -579,11 +589,12 @@ impl_wasm_conversions!(cml_chain::certs::MultiHostName, MultiHostName); #[wasm_bindgen] impl MultiHostName { - pub fn dns_name(&self) -> DnsName { + pub fn dns_name(&self) -> DNSName { self.0.dns_name.clone().into() } - pub fn new(dns_name: &DnsName) -> Self { + /// * `dns_name` - A SRV DNS record + pub fn new(dns_name: &DNSName) -> Self { Self(cml_chain::certs::MultiHostName::new( dns_name.clone().into(), )) @@ -748,14 +759,14 @@ impl RegCert { self.0.stake_credential.clone().into() } - pub fn coin(&self) -> Coin { - self.0.coin + pub fn deposit(&self) -> Coin { + self.0.deposit } - pub fn new(stake_credential: &StakeCredential, coin: Coin) -> Self { + pub fn new(stake_credential: &StakeCredential, deposit: Coin) -> Self { Self(cml_chain::certs::RegCert::new( stake_credential.clone().into(), - coin, + deposit, )) } } @@ -774,18 +785,18 @@ impl RegDrepCert { self.0.drep_credential.clone().into() } - pub fn coin(&self) -> Coin { - self.0.coin + pub fn deposit(&self) -> Coin { + self.0.deposit } pub fn anchor(&self) -> Option { self.0.anchor.clone().map(std::convert::Into::into) } - pub fn new(drep_credential: &DrepCredential, coin: Coin, anchor: Option) -> Self { + pub fn new(drep_credential: &DrepCredential, deposit: Coin, anchor: Option) -> Self { Self(cml_chain::certs::RegDrepCert::new( drep_credential.clone().into(), - coin, + deposit, anchor.map(Into::into), )) } @@ -813,14 +824,14 @@ impl Relay { )) } - pub fn new_single_host_name(port: Option, dns_name: &DnsName) -> Self { + pub fn new_single_host_name(port: Option, dns_name: &DNSName) -> Self { Self(cml_chain::certs::Relay::new_single_host_name( port, dns_name.clone().into(), )) } - pub fn new_multi_host_name(dns_name: &DnsName) -> Self { + pub fn new_multi_host_name(dns_name: &DNSName) -> Self { Self(cml_chain::certs::Relay::new_multi_host_name( dns_name.clone().into(), )) @@ -946,11 +957,12 @@ impl SingleHostName { self.0.port } - pub fn dns_name(&self) -> DnsName { + pub fn dns_name(&self) -> DNSName { self.0.dns_name.clone().into() } - pub fn new(port: Option, dns_name: &DnsName) -> Self { + /// * `dns_name` - An A or AAAA DNS record + pub fn new(port: Option, dns_name: &DNSName) -> Self { Self(cml_chain::certs::SingleHostName::new( port, dns_name.clone().into(), @@ -1025,15 +1037,15 @@ impl StakeRegDelegCert { self.0.pool.into() } - pub fn coin(&self) -> Coin { - self.0.coin + pub fn deposit(&self) -> Coin { + self.0.deposit } - pub fn new(stake_credential: &StakeCredential, pool: &Ed25519KeyHash, coin: Coin) -> Self { + pub fn new(stake_credential: &StakeCredential, pool: &Ed25519KeyHash, deposit: Coin) -> Self { Self(cml_chain::certs::StakeRegDelegCert::new( stake_credential.clone().into(), pool.clone().into(), - coin, + deposit, )) } } @@ -1115,21 +1127,21 @@ impl StakeVoteRegDelegCert { self.0.d_rep.clone().into() } - pub fn coin(&self) -> Coin { - self.0.coin + pub fn deposit(&self) -> Coin { + self.0.deposit } pub fn new( stake_credential: &StakeCredential, pool: &Ed25519KeyHash, d_rep: &DRep, - coin: Coin, + deposit: Coin, ) -> Self { Self(cml_chain::certs::StakeVoteRegDelegCert::new( stake_credential.clone().into(), pool.clone().into(), d_rep.clone().into(), - coin, + deposit, )) } } @@ -1148,14 +1160,14 @@ impl UnregCert { self.0.stake_credential.clone().into() } - pub fn coin(&self) -> Coin { - self.0.coin + pub fn deposit(&self) -> Coin { + self.0.deposit } - pub fn new(stake_credential: &StakeCredential, coin: Coin) -> Self { + pub fn new(stake_credential: &StakeCredential, deposit: Coin) -> Self { Self(cml_chain::certs::UnregCert::new( stake_credential.clone().into(), - coin, + deposit, )) } } @@ -1174,14 +1186,14 @@ impl UnregDrepCert { self.0.drep_credential.clone().into() } - pub fn coin(&self) -> Coin { - self.0.coin + pub fn deposit(&self) -> Coin { + self.0.deposit } - pub fn new(drep_credential: &DrepCredential, coin: Coin) -> Self { + pub fn new(drep_credential: &DrepCredential, deposit: Coin) -> Self { Self(cml_chain::certs::UnregDrepCert::new( drep_credential.clone().into(), - coin, + deposit, )) } } @@ -1271,15 +1283,15 @@ impl VoteRegDelegCert { self.0.d_rep.clone().into() } - pub fn coin(&self) -> Coin { - self.0.coin + pub fn deposit(&self) -> Coin { + self.0.deposit } - pub fn new(stake_credential: &StakeCredential, d_rep: &DRep, coin: Coin) -> Self { + pub fn new(stake_credential: &StakeCredential, d_rep: &DRep, deposit: Coin) -> Self { Self(cml_chain::certs::VoteRegDelegCert::new( stake_credential.clone().into(), d_rep.clone().into(), - coin, + deposit, )) } } diff --git a/chain/wasm/src/governance/mod.rs b/chain/wasm/src/governance/mod.rs index a184942c..99595b4e 100644 --- a/chain/wasm/src/governance/mod.rs +++ b/chain/wasm/src/governance/mod.rs @@ -15,6 +15,8 @@ use cml_core::ordered_hash_map::OrderedHashMap; use cml_core_wasm::{impl_wasm_cbor_json_api, impl_wasm_conversions}; use wasm_bindgen::prelude::{wasm_bindgen, JsError, JsValue}; +pub mod utils; + #[derive(Clone, Debug)] #[wasm_bindgen] pub struct Anchor(cml_chain::governance::Anchor); @@ -78,14 +80,14 @@ impl_wasm_conversions!(cml_chain::governance::GovAction, GovAction); #[wasm_bindgen] impl GovAction { pub fn new_parameter_change_action( - gov_action_id: Option, - protocol_param_update: &ProtocolParamUpdate, + action_id: Option, + update: &ProtocolParamUpdate, policy_hash: Option, ) -> Self { Self( cml_chain::governance::GovAction::new_parameter_change_action( - gov_action_id.map(Into::into), - protocol_param_update.clone().into(), + action_id.map(Into::into), + update.clone().into(), policy_hash.map(Into::into), ), ) @@ -348,12 +350,12 @@ impl_wasm_conversions!( #[wasm_bindgen] impl ParameterChangeAction { - pub fn gov_action_id(&self) -> Option { - self.0.gov_action_id.clone().map(std::convert::Into::into) + pub fn action_id(&self) -> Option { + self.0.action_id.clone().map(std::convert::Into::into) } - pub fn protocol_param_update(&self) -> ProtocolParamUpdate { - self.0.protocol_param_update.clone().into() + pub fn update(&self) -> ProtocolParamUpdate { + self.0.update.clone().into() } pub fn policy_hash(&self) -> Option { @@ -361,13 +363,13 @@ impl ParameterChangeAction { } pub fn new( - gov_action_id: Option, - protocol_param_update: &ProtocolParamUpdate, + action_id: Option, + update: &ProtocolParamUpdate, policy_hash: Option, ) -> Self { Self(cml_chain::governance::ParameterChangeAction::new( - gov_action_id.map(Into::into), - protocol_param_update.clone().into(), + action_id.map(Into::into), + update.clone().into(), policy_hash.map(Into::into), )) } diff --git a/chain/wasm/src/governance/utils.rs b/chain/wasm/src/governance/utils.rs new file mode 100644 index 00000000..ec905d7e --- /dev/null +++ b/chain/wasm/src/governance/utils.rs @@ -0,0 +1,23 @@ +use wasm_bindgen::prelude::wasm_bindgen; + +use cml_crypto_wasm::{Ed25519KeyHash, ScriptHash}; + +use super::{GovAction, Voter}; + +#[wasm_bindgen] +impl GovAction { + pub fn script_hash(&self) -> Option { + self.0.script_hash().map(|hash| (*hash).into()) + } +} + +#[wasm_bindgen] +impl Voter { + pub fn key_hash(&self) -> Option { + self.0.key_hash().map(|hash| (*hash).into()) + } + + pub fn script_hash(&self) -> Option { + self.0.script_hash().map(|hash| (*hash).into()) + } +} diff --git a/chain/wasm/src/plutus/mod.rs b/chain/wasm/src/plutus/mod.rs index ccf194fd..58d3db19 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() } } @@ -235,13 +224,6 @@ impl_wasm_cbor_json_api!(PlutusV1Script); impl_wasm_conversions!(cml_chain::plutus::PlutusV1Script, PlutusV1Script); -#[wasm_bindgen] -impl PlutusV1Script { - pub fn get(&self) -> Vec { - self.0.get().clone() - } -} - #[derive(Clone, Debug)] #[wasm_bindgen] pub struct PlutusV2Script(cml_chain::plutus::PlutusV2Script); @@ -250,13 +232,6 @@ impl_wasm_cbor_json_api!(PlutusV2Script); impl_wasm_conversions!(cml_chain::plutus::PlutusV2Script, PlutusV2Script); -#[wasm_bindgen] -impl PlutusV2Script { - pub fn get(&self) -> Vec { - self.0.get().clone() - } -} - #[derive(Clone, Debug)] #[wasm_bindgen] pub struct PlutusV3Script(cml_chain::plutus::PlutusV3Script); @@ -265,13 +240,6 @@ impl_wasm_cbor_json_api!(PlutusV3Script); impl_wasm_conversions!(cml_chain::plutus::PlutusV3Script, PlutusV3Script); -#[wasm_bindgen] -impl PlutusV3Script { - pub fn get(&self) -> Vec { - self.0.get().clone() - } -} - #[derive(Clone, Debug)] #[wasm_bindgen] pub struct RedeemerKey(cml_chain::plutus::RedeemerKey); diff --git a/chain/wasm/src/plutus/utils.rs b/chain/wasm/src/plutus/utils.rs index e2ca0103..2b458235 100644 --- a/chain/wasm/src/plutus/utils.rs +++ b/chain/wasm/src/plutus/utils.rs @@ -3,7 +3,9 @@ use crate::{ LegacyRedeemerList, PlutusDataList, }; use cml_chain::plutus::Language; -use cml_core_wasm::{impl_wasm_cbor_api, impl_wasm_cbor_json_api, impl_wasm_conversions}; +use cml_core_wasm::{ + impl_raw_bytes_api, impl_wasm_cbor_api, impl_wasm_cbor_json_api, impl_wasm_conversions, +}; use cml_crypto_wasm::ScriptHash; use wasm_bindgen::prelude::{wasm_bindgen, JsError, JsValue}; @@ -152,6 +154,19 @@ impl PlutusV2Script { } } +#[wasm_bindgen] +impl PlutusV3Script { + pub fn hash(&self) -> ScriptHash { + self.0.hash().into() + } +} + +impl_raw_bytes_api!(cml_chain::plutus::PlutusV1Script, PlutusV1Script); + +impl_raw_bytes_api!(cml_chain::plutus::PlutusV2Script, PlutusV2Script); + +impl_raw_bytes_api!(cml_chain::plutus::PlutusV3Script, PlutusV3Script); + #[wasm_bindgen] impl Redeemers { pub fn to_flat_format(&self) -> LegacyRedeemerList { diff --git a/chain/wasm/src/transaction/mod.rs b/chain/wasm/src/transaction/mod.rs index e4fb2a94..26ba128c 100644 --- a/chain/wasm/src/transaction/mod.rs +++ b/chain/wasm/src/transaction/mod.rs @@ -18,7 +18,6 @@ use cml_crypto_wasm::{ AuxiliaryDataHash, DatumHash, Ed25519KeyHash, ScriptDataHash, TransactionHash, }; use wasm_bindgen::prelude::{wasm_bindgen, JsError, JsValue}; - pub mod utils; #[derive(Clone, Debug)] @@ -187,10 +186,12 @@ impl NativeScript { )) } + /// Timelock validity intervals are half-open intervals [a, b). This field specifies the left (included) endpoint a. pub fn new_script_invalid_before(before: Slot) -> Self { Self(cml_chain::transaction::NativeScript::new_script_invalid_before(before)) } + /// Timelock validity intervals are half-open intervals [a, b). This field specifies the right (excluded) endpoint b. pub fn new_script_invalid_hereafter(after: Slot) -> Self { Self(cml_chain::transaction::NativeScript::new_script_invalid_hereafter(after)) } diff --git a/chain/wasm/src/transaction/utils.rs b/chain/wasm/src/transaction/utils.rs index 67932bf5..5d2ddffc 100644 --- a/chain/wasm/src/transaction/utils.rs +++ b/chain/wasm/src/transaction/utils.rs @@ -4,6 +4,7 @@ use crate::{ utils::LanguageList, Ed25519KeyHashList, NativeScript, Value, }; +use cml_core::Slot; use cml_crypto_wasm::{DatumHash, ScriptHash}; use wasm_bindgen::prelude::wasm_bindgen; @@ -68,6 +69,15 @@ impl NativeScript { pub fn hash(&self) -> ScriptHash { self.0.hash().into() } + + pub fn verify( + &self, + lower_bound: Option, + upper_bound: Option, + key_hashes: &Ed25519KeyHashList, + ) -> bool { + self.0.verify(lower_bound, upper_bound, key_hashes.as_ref()) + } } #[wasm_bindgen] diff --git a/chain/wasm/src/utils.rs b/chain/wasm/src/utils.rs index ce9c6751..512c90cd 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}; @@ -86,6 +86,17 @@ impl NetworkId { } } +#[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() + } +} + // we provide direct From/Into conversions between NonemptySet and TList // to allow the auto-generated code to work directly without changes macro_rules! impl_wasm_conversions_into { diff --git a/cip25/rust/Cargo.toml b/cip25/rust/Cargo.toml index 6277b682..99b7e507 100644 --- a/cip25/rust/Cargo.toml +++ b/cip25/rust/Cargo.toml @@ -24,4 +24,4 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.57" thiserror = "1.0.37" # for enums -wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] } \ No newline at end of file +wasm-bindgen = { version = "0.2.87" } diff --git a/cip25/rust/src/utils.rs b/cip25/rust/src/utils.rs index 2b61f379..1df70073 100644 --- a/cip25/rust/src/utils.rs +++ b/cip25/rust/src/utils.rs @@ -7,7 +7,6 @@ pub use cml_chain::{ PolicyId, }; pub use cml_core::{error::*, serialization::*}; -use cml_crypto::RawBytesEncoding; use std::io::{BufRead, Seek, SeekFrom, Write}; use crate::{CIP25ChunkableString, CIP25Metadata, CIP25MetadataDetails, CIP25String64}; @@ -251,7 +250,7 @@ impl CIP25LabelMetadata { details: CIP25MetadataDetails, ) -> Result, CIP25Error> { if self.version == CIP25Version::V1 { - if let Err(e) = String::from_utf8(asset_name.get().clone()) { + if let Err(e) = String::from_utf8(asset_name.to_raw_bytes().to_vec()) { return Err(CIP25Error::Version1NonStringAsset(asset_name, e)); } } @@ -296,7 +295,8 @@ impl cbor_event::se::Serialize for CIP25LabelMetadata { for (asset_name, details) in assets.iter() { // hand-edit: write as string // note: this invariant is checked during setting and data is private - let asset_name_str = String::from_utf8(asset_name.get().clone()).unwrap(); + let asset_name_str = + String::from_utf8(asset_name.to_raw_bytes().to_vec()).unwrap(); serializer.write_text(asset_name_str)?; details.serialize(serializer)?; } @@ -313,7 +313,7 @@ impl cbor_event::se::Serialize for CIP25LabelMetadata { serializer.write_map(cbor_event::Len::Len(assets.len() as u64))?; for (asset_name, details) in assets.iter() { // hand-edit: write bytes - serializer.write_bytes(asset_name.get())?; + serializer.write_bytes(asset_name.to_raw_bytes())?; details.serialize(serializer)?; } diff --git a/cip25/wasm/Cargo.toml b/cip25/wasm/Cargo.toml index db03c318..31b0a22f 100644 --- a/cip25/wasm/Cargo.toml +++ b/cip25/wasm/Cargo.toml @@ -15,7 +15,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] cbor_event = "2.2.0" -cml-chain = { path = "../../chain/rust", version = "5.3.1" } +cml-chain = { path = "../../chain/rust", version = "5.3.1", features = ["used_from_wasm"] } cml-chain-wasm = { path = "../../chain/wasm", version = "5.3.1" } cml-core = { path = "../../core/rust", version = "5.3.1" } cml-core-wasm = { path = "../../core/wasm", version = "5.3.1" } @@ -24,4 +24,4 @@ hex = "0.4.0" linked-hash-map = "0.5.3" serde_json = "1.0.57" serde-wasm-bindgen = "0.4.5" -wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] } \ No newline at end of file +wasm-bindgen = { version = "0.2.87" } diff --git a/cip36/wasm/Cargo.toml b/cip36/wasm/Cargo.toml index 6de5fcea..0a83fc26 100644 --- a/cip36/wasm/Cargo.toml +++ b/cip36/wasm/Cargo.toml @@ -23,7 +23,7 @@ cml-core = { path = "../../core/rust", version = "5.3.1" } cml-core-wasm = { path = "../../core/wasm", version = "5.3.1" } cbor_event = "2.2.0" hex = "0.4.0" -wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.87" } linked-hash-map = "0.5.3" serde_json = "1.0.57" serde-wasm-bindgen = "0.4.5" diff --git a/cml/wasm/Cargo.toml b/cml/wasm/Cargo.toml index 15d595ad..42ae2dc2 100644 --- a/cml/wasm/Cargo.toml +++ b/cml/wasm/Cargo.toml @@ -23,4 +23,4 @@ hex = "0.4.0" linked-hash-map = "0.5.3" serde_json = "1.0.57" serde-wasm-bindgen = "0.4.5" -wasm-bindgen = { version = "0.2", features=["serde-serialize"] } \ No newline at end of file +wasm-bindgen = { version = "0.2.87" } diff --git a/core/rust/Cargo.toml b/core/rust/Cargo.toml index d50ea7ac..6b30f23a 100644 --- a/core/rust/Cargo.toml +++ b/core/rust/Cargo.toml @@ -34,26 +34,6 @@ num-integer = "0.1.45" thiserror = "1.0.37" cfg-if = "1" -# These can be removed if we make wasm bindings for ALL functionality here. -# This was not done right now as there is a lot of existing legacy code e.g. -# for Byron that might need to be used from WASM and might not. -# We can remove this dependency when that is decided. -# -# The other use-case here is enums. Without this two enums would need to be defined -# despite wasm_bindgen supporting C-style enums (with non-negative values) 100% -# This could possibly be resolved with macros but maybe not. - -# non-wasm -#[target.'cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))'.dependencies] -#rand_os = "0.1" -#noop_proc_macro = "0.3.0" - -# wasm -#[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] -wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] } -#rand_os = { version = "0.1", features = ["wasm-bindgen"] } -#js-sys = "=0.3.59" - [dev-dependencies] quickcheck = "0.9.2" diff --git a/core/rust/src/serialization.rs b/core/rust/src/serialization.rs index 18f92a2c..0249c440 100644 --- a/core/rust/src/serialization.rs +++ b/core/rust/src/serialization.rs @@ -288,3 +288,24 @@ impl FromBytes for T { Self::deserialize(&mut raw).map_err(Into::into) } } +pub trait RawBytesEncoding { + fn to_raw_bytes(&self) -> &[u8]; + + fn from_raw_bytes(bytes: &[u8]) -> Result + where + Self: Sized; + + fn to_raw_hex(&self) -> String { + hex::encode(self.to_raw_bytes()) + } + + fn from_raw_hex(hex_str: &str) -> Result + where + Self: Sized, + { + let bytes = hex::decode(hex_str).map_err(|e| { + DeserializeError::from(DeserializeFailure::InvalidStructure(Box::new(e))) + })?; + Self::from_raw_bytes(bytes.as_ref()) + } +} diff --git a/core/wasm/Cargo.toml b/core/wasm/Cargo.toml index 605216ed..58eac743 100644 --- a/core/wasm/Cargo.toml +++ b/core/wasm/Cargo.toml @@ -16,7 +16,7 @@ crate-type = ["cdylib", "rlib"] cml-core = { path = "../rust", version = "5.3.1" } cbor_event = "2.2.0" hex = "0.4.0" -wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.87" } linked-hash-map = "0.5.3" serde_json = "1.0.57" -serde-wasm-bindgen = "0.4.5" \ No newline at end of file +serde-wasm-bindgen = "0.4.5" diff --git a/core/wasm/src/lib.rs b/core/wasm/src/lib.rs index c3295315..aaabee57 100644 --- a/core/wasm/src/lib.rs +++ b/core/wasm/src/lib.rs @@ -2,6 +2,9 @@ use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; use cml_core::serialization::{Deserialize, Serialize}; +// re-export to make macros easier to use +pub use cml_core::serialization::RawBytesEncoding; + #[macro_use] pub mod wasm_wrappers; diff --git a/core/wasm/src/wasm_wrappers.rs b/core/wasm/src/wasm_wrappers.rs index aa0ae9f1..2e1abad5 100644 --- a/core/wasm/src/wasm_wrappers.rs +++ b/core/wasm/src/wasm_wrappers.rs @@ -417,6 +417,13 @@ macro_rules! impl_wasm_cbor_api { cml_core::serialization::Serialize::to_cbor_bytes(&self.0) } + /** + * Serialize this type to CBOR bytes using canonical CBOR encodings + */ + pub fn to_canonical_cbor_bytes(&self) -> Vec { + cml_core::serialization::Serialize::to_canonical_cbor_bytes(&self.0) + } + /** * Create this type from CBOR bytes */ @@ -440,6 +447,13 @@ macro_rules! impl_wasm_cbor_api { hex::encode(self.to_cbor_bytes()) } + /** + * Serialize this type to CBOR bytes using canonical CBOR encodings as hex bytes + */ + pub fn to_canonical_cbor_hex(&self) -> String { + hex::encode(self.to_canonical_cbor_bytes()) + } + /** * Create this type from the CBOR bytes encoded as a hex string. * This is useful for interfacing with CIP30 @@ -550,3 +564,46 @@ macro_rules! impl_wasm_json_api { } }; } + +#[macro_export] +macro_rules! impl_raw_bytes_api { + ($rust:ty, $wasm:ident) => { + #[wasm_bindgen] + impl $wasm { + /** + * Direct raw bytes without any CBOR structure + */ + pub fn to_raw_bytes(&self) -> Vec { + use cml_core_wasm::RawBytesEncoding; + self.0.to_raw_bytes().to_vec() + } + + /** + * Parse from the direct raw bytes, without any CBOR structure + */ + pub fn from_raw_bytes(bytes: &[u8]) -> Result<$wasm, wasm_bindgen::JsError> { + use cml_core_wasm::RawBytesEncoding; + <$rust>::from_raw_bytes(bytes).map(Self).map_err(Into::into) + } + + /** + * Direct raw bytes without any CBOR structure, as a hex-encoded string + */ + pub fn to_hex(&self) -> String { + use cml_core_wasm::RawBytesEncoding; + self.0.to_raw_hex() + } + + /** + * Parse from a hex string of the direct raw bytes, without any CBOR structure + */ + pub fn from_hex(input: &str) -> Result<$wasm, wasm_bindgen::JsError> { + use cml_core_wasm::RawBytesEncoding; + <$rust>::from_raw_hex(input) + .map(Into::into) + .map(Self) + .map_err(Into::into) + } + } + }; +} diff --git a/crypto/rust/src/lib.rs b/crypto/rust/src/lib.rs index cea53404..bda9bfd7 100644 --- a/crypto/rust/src/lib.rs +++ b/crypto/rust/src/lib.rs @@ -2,7 +2,7 @@ use crate::chain_crypto::bech32::Bech32; pub use cml_core::{ error::{DeserializeError, DeserializeFailure}, - serialization::{Deserialize, Serialize, StringEncoding}, + serialization::{Deserialize, RawBytesEncoding, Serialize, StringEncoding}, }; use cryptoxide::blake2b::Blake2b; pub use derivative::Derivative; @@ -22,26 +22,6 @@ pub mod typed_bytes; #[macro_use] extern crate cfg_if; -pub trait RawBytesEncoding { - fn to_raw_bytes(&self) -> &[u8]; - - fn from_raw_bytes(bytes: &[u8]) -> Result - where - Self: Sized; - - fn to_raw_hex(&self) -> String { - hex::encode(self.to_raw_bytes()) - } - - fn from_raw_hex(hex_str: &str) -> Result - where - Self: Sized, - { - let bytes = hex::decode(hex_str)?; - Self::from_raw_bytes(bytes.as_ref()) - } -} - #[derive(Debug, thiserror::Error)] pub enum CryptoError { #[error("Bech32: {0}")] @@ -124,7 +104,7 @@ impl Bip32PrivateKey { buf[0..64].clone_from_slice(&bytes[0..64]); buf[64..96].clone_from_slice(&bytes[96..128]); - Bip32PrivateKey::from_raw_bytes(&buf) + Bip32PrivateKey::from_raw_bytes(&buf).map_err(Into::into) } /// see from_128_xprv pub fn to_128_xprv(&self) -> Vec { @@ -181,9 +161,9 @@ impl RawBytesEncoding for Bip32PrivateKey { self.0.as_ref() } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_raw_bytes(bytes: &[u8]) -> Result { chain_crypto::SecretKey::::from_binary(bytes) - .map_err(Into::into) + .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) .map(Bip32PrivateKey) } } @@ -248,9 +228,9 @@ impl RawBytesEncoding for Bip32PublicKey { self.0.as_ref() } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_raw_bytes(bytes: &[u8]) -> Result { chain_crypto::PublicKey::::from_binary(bytes) - .map_err(Into::into) + .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) .map(Bip32PublicKey) } } @@ -339,8 +319,10 @@ impl RawBytesEncoding for PrivateKey { } } - fn from_raw_bytes(bytes: &[u8]) -> Result { - Self::from_normal_bytes(bytes).or_else(|_| Self::from_extended_bytes(bytes)) + fn from_raw_bytes(bytes: &[u8]) -> Result { + Self::from_normal_bytes(bytes) + .or_else(|_| Self::from_extended_bytes(bytes)) + .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) } } @@ -387,9 +369,9 @@ impl RawBytesEncoding for PublicKey { self.0.as_ref() } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_raw_bytes(bytes: &[u8]) -> Result { chain_crypto::PublicKey::from_binary(bytes) - .map_err(Into::into) + .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) .map(PublicKey) } } @@ -429,10 +411,10 @@ macro_rules! impl_signature { self.0.as_ref() } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_raw_bytes(bytes: &[u8]) -> Result { chain_crypto::Signature::from_binary(bytes.as_ref()) .map(Self) - .map_err(Into::into) + .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) } } @@ -528,8 +510,10 @@ macro_rules! impl_hash_type { hex::encode(&self.0.as_ref()) } - pub fn from_hex(input: &str) -> Result { - let hex_bytes = hex::decode(input)?; + pub fn from_hex(input: &str) -> Result { + let hex_bytes = hex::decode(input).map_err(|e| { + DeserializeError::from(DeserializeFailure::InvalidStructure(Box::new(e))) + })?; Self::from_raw_bytes(&hex_bytes) } } @@ -557,7 +541,7 @@ macro_rules! impl_hash_type { self.0.as_ref() } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_raw_bytes(bytes: &[u8]) -> Result { use std::convert::TryInto; match bytes.len() { $byte_count => Ok($name(bytes[..$byte_count].try_into().unwrap())), @@ -570,8 +554,7 @@ macro_rules! impl_hash_type { Err(DeserializeError::new( stringify!($name), DeserializeFailure::CBOR(cbor_error), - ) - .into()) + )) } } } @@ -652,10 +635,10 @@ impl RawBytesEncoding for LegacyDaedalusPrivateKey { self.0.as_ref() } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_raw_bytes(bytes: &[u8]) -> Result { chain_crypto::SecretKey::::from_binary(bytes) .map(LegacyDaedalusPrivateKey) - .map_err(|e| e.into()) + .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) } } diff --git a/crypto/wasm/Cargo.toml b/crypto/wasm/Cargo.toml index 39eb864c..f6b14588 100644 --- a/crypto/wasm/Cargo.toml +++ b/crypto/wasm/Cargo.toml @@ -14,8 +14,9 @@ keywords = ["cardano"] crate-type = ["cdylib", "rlib"] [dependencies] +cml-core-wasm = { path = "../../core/wasm", version = "5.3.1" } cml-crypto = { path = "../rust", version = "5.3.1" } cbor_event = "2.2.0" -wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.87" } linked-hash-map = "0.5.3" serde_json = "1.0.57" diff --git a/crypto/wasm/src/lib.rs b/crypto/wasm/src/lib.rs index 358c7848..4f017bba 100644 --- a/crypto/wasm/src/lib.rs +++ b/crypto/wasm/src/lib.rs @@ -333,39 +333,20 @@ macro_rules! impl_signature { #[wasm_bindgen] impl $name { - pub fn to_raw_bytes(&self) -> Vec { - self.0.to_raw_bytes().to_vec() - } - pub fn to_bech32(&self) -> String { self.0.to_bech32() } - pub fn to_hex(&self) -> String { - self.0.to_raw_hex() - } - - pub fn from_bech32(bech32_str: &str) -> Result<$name, JsError> { + pub fn from_bech32(bech32_str: &str) -> Result<$name, wasm_bindgen::JsError> { cml_crypto::$name::from_bech32(bech32_str) .map(Into::into) .map(Self) .map_err(Into::into) } - - pub fn from_hex(input: &str) -> Result<$name, JsError> { - cml_crypto::$name::from_raw_hex(input) - .map(Into::into) - .map(Self) - .map_err(Into::into) - } - - pub fn from_raw_bytes(bytes: &[u8]) -> Result<$name, JsError> { - cml_crypto::$name::from_raw_bytes(bytes) - .map(Self) - .map_err(Into::into) - } } + cml_core_wasm::impl_raw_bytes_api!(cml_crypto::$name, $name); + impl From for $name { fn from(inner: cml_crypto::$name) -> Self { Self(inner) @@ -397,11 +378,6 @@ macro_rules! impl_hash_type_ext { #[wasm_bindgen::prelude::wasm_bindgen] impl $wasm_name { - pub fn to_raw_bytes(&self) -> Vec { - use cml_crypto::RawBytesEncoding; - self.0.to_raw_bytes().to_vec() - } - pub fn to_bech32( &self, prefix: &str, @@ -409,11 +385,6 @@ macro_rules! impl_hash_type_ext { self.0.to_bech32(prefix).map_err(Into::into) } - pub fn to_hex(&self) -> String { - use cml_crypto::RawBytesEncoding; - self.0.to_raw_hex() - } - pub fn from_bech32( bech32_str: &str, ) -> Result<$wasm_name, wasm_bindgen::prelude::JsError> { @@ -422,22 +393,6 @@ macro_rules! impl_hash_type_ext { .map(Self) .map_err(Into::into) } - - pub fn from_hex(input: &str) -> Result<$wasm_name, wasm_bindgen::prelude::JsError> { - use cml_crypto::RawBytesEncoding; - <$rust_name>::from_raw_hex(input) - .map(Self) - .map_err(Into::into) - } - - pub fn from_raw_bytes( - bytes: &[u8], - ) -> Result<$wasm_name, wasm_bindgen::prelude::JsError> { - use cml_crypto::RawBytesEncoding; - <$rust_name>::from_raw_bytes(bytes) - .map(Self) - .map_err(Into::into) - } } impl From<$rust_name> for $wasm_name { @@ -457,6 +412,8 @@ macro_rules! impl_hash_type_ext { &self.0 } } + + cml_core_wasm::impl_raw_bytes_api!($rust_name, $wasm_name); }; } diff --git a/docs/docs/index.md b/docs/docs/index.md index abc65997..51d3d29b 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -13,16 +13,26 @@ This is a library, written in Rust, that can be deployed to multiple platforms ( ##### NPM packages -TODO: update these once we publish new versions: - browser: [link](https://www.npmjs.com/package/@dcspark/cardano-multiplatform-lib-browser) - nodejs: [link](https://www.npmjs.com/package/@dcspark/cardano-multiplatform-lib-nodejs) + +There is also an outdated asm.js . It is strongly discouraged from using this as it is out of date and asm.js results in incredibly slow cryptographic operations. - asm.js (strongly discouraged): [link](https://www.npmjs.com/package/@dcspark/cardano-multiplatform-lib-asmjs) Note: If you are using WebPack, you must use version 5 or later for CML to work. ##### Rust crates -- crates: [link](https://crates.io/crates/cardano-multiplatform-lib) +The rust crates are split up by functionality. + +- core: [link](https://crates.io/crates/cml-core) +- crypto: [link](https://crates.io/crates/cml-crypto) +- chain: [link](https://crates.io/crates/cml-chain) +- multi-era: [link](https://crates.io/crates/cml-multi-era) +- cip25: [link](https://crates.io/crates/cml-cip25) +- cip36: [link](https://crates.io/crates/cml-cip36) + +Most users will likely be using primarily `cml-chain` for general uses, `cml-multi-era` if they need historical (pre-babbage eras) chain-parsing and `cip25` or `cip36` if they need those specific metadata standards. ##### Mobile bindings diff --git a/docs/docs/modules/CIP25.mdx b/docs/docs/modules/CIP25.mdx index 09e54de9..725236ac 100644 --- a/docs/docs/modules/CIP25.mdx +++ b/docs/docs/modules/CIP25.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 5 +sidebar_position: 3 --- # CIP25 diff --git a/docs/docs/modules/builders/generating_transactions.mdx b/docs/docs/modules/builders/generating_transactions.mdx index f0555970..c9b88f58 100644 --- a/docs/docs/modules/builders/generating_transactions.mdx +++ b/docs/docs/modules/builders/generating_transactions.mdx @@ -2,21 +2,6 @@ sidebar_position: 4 --- -# TransactionBuilder - -In order to simplify transaction creation, we provide a `TransactionBuilder` struct that manages witnesses, fee calculation, change addresses and such. Assume we have instantiated an instance under the variable `builder` for this explanation. The `TransactionBuilder` requires several protocol parameters governing Cardano to be created which is shown in the following section. These are specified initially in the genesis file for Cardano nodes. - -The minimum required for a valid transaction is to add inputs, outputs, time-to-live and either set the fee explicitly with `builder.set_fee(fee)`, or calculate it implicitly using `builder.add_change_if_needed(address)`. -Optionally a transaction can also have certificates, reward withdrawals, and metadata added to it. -Any change made to the builder can impact the size and thus the fee so the fee should be the last thing set. -If implicitly setting the fee any extra ADA (`inputs + withdrawals - outputs + refund - deposit - min fee`) is sent to the provided change address. -Fees must be sufficient, i.e. `inputs + withdrawals + refund >= outputs + deposit + fee` which must be manually ensured if you explicitly set the fee. Any extra fee is not necessary and the extra ADA beyond that will be burned. -Once the transaction is ready, `const body = builder.build()` can be called to return a ready `TransactionBody`. - -Withdrawals are ADA withdrawn as part of the rewards generated by staking and deposits are refundable ADA locked while resources such as stake certificates or pool registrations exist on the blockchain. They are returned as refunds when these resources are deregistered/retired. - -To get to a transaction ready to post on the blockchain, we must create a `Transaction` from that, which consists of the `TransactionBody`, a matching `TransactionWitnessSet` and optionally a `TransactionMetadata`. -The witnesses and optional metadata must match those provided to the builder. The witnesses must sign the hash of the transaction body returned by `hash_transaction(body)`. In addition to the witnesses for inputs, withdrawals and some certificates require witnesses as well. For example, staking address registration does not require a witness while stake address de-registration requires one. For any questions or doubts about the rules governing fees, deposits, rewards, certificates or which witness types are required refer to the [shelley specs](https://github.com/input-output-hk/cardano-ledger-specs#cardano-ledger), specifically the Shelley design specification for general design. The formal specification could be useful for specific details as well. The design spec contains details about which certificates require which type of witnesses in the Certificates and Registrations section. ## Example code @@ -27,95 +12,50 @@ Fees are automatically calculated and sent to a change address in the example. ```javascript // instantiate the tx builder with the Cardano protocol parameters - these may change later on -const linearFee = CardanoWasm.LinearFee.new( - CardanoWasm.BigNum.from_str('44'), - CardanoWasm.BigNum.from_str('155381') -); -const txBuilderCfg = CardanoWasm.TransactionBuilderConfigBuilder.new() - .fee_algo(linearFee) - .pool_deposit(CardanoWasm.BigNum.from_str('500000000')) - .key_deposit(CardanoWasm.BigNum.from_str('2000000')) - .max_value_size(4000) - .max_tx_size(8000) - .coins_per_utxo_word(CardanoWasm.BigNum.from_str('34482')) - .build(); -const txBuilder = CardanoWasm.TransactionBuilder.new(txBuilderCfg); - +const txBuilder = makeTxBuilder(); +const testnetId = 0; // add a keyhash input - for ADA held in a Shelley-era normal address (Base, Enterprise, Pointer) -const prvKey = CardanoWasm.PrivateKey.from_bech32("ed25519e_sk16rl5fqqf4mg27syjzjrq8h3vq44jnnv52mvyzdttldszjj7a64xtmjwgjtfy25lu0xmv40306lj9pcqpa6slry9eh3mtlqvfjz93vuq0grl80"); -txBuilder.add_key_input( - prvKey.to_public().hash(), - CardanoWasm.TransactionInput.new( - CardanoWasm.TransactionHash.from_bytes( - Buffer.from("8561258e210352fba2ac0488afed67b3427a27ccf1d41ec030c98a8199bc22ec", "hex") - ), // tx hash +const prvKey = CML.PrivateKey.from_bech32("ed25519e_sk16rl5fqqf4mg27syjzjrq8h3vq44jnnv52mvyzdttldszjj7a64xtmjwgjtfy25lu0xmv40306lj9pcqpa6slry9eh3mtlqvfjz93vuq0grl80"); +const inputAddr = CML.EnterpriseAddress.new(testnetId, CML.StakeCredential.new_key(prvKey.to_public().hash())).to_address(); +txBuilder.add_input(CML.SingleInputBuilder.new( + CML.TransactionInput.new( + CML.TransactionHash.from_hex("8561258e210352fba2ac0488afed67b3427a27ccf1d41ec030c98a8199bc22ec"), // tx hash 0, // index ), - CardanoWasm.Value.new(CardanoWasm.BigNum.from_str('3000000')) -); - -// add a bootstrap input - for ADA held in a Byron-era address -const byronAddress = CardanoWasm.ByronAddress.from_base58("Ae2tdPwUPEZLs4HtbuNey7tK4hTKrwNwYtGqp7bDfCy2WdR3P6735W5Yfpe"); -txBuilder.add_bootstrap_input( - byronAddress, - CardanoWasm.TransactionInput.new( - CardanoWasm.TransactionHash.from_bytes( - Buffer.from("488afed67b342d41ec08561258e210352fba2ac030c98a8199bc22ec7a27ccf1", "hex"), - ), // tx hash - 0, // index - ), - CardanoWasm.Value.new(CardanoWasm.BigNum.from_str('3000000')) + CML.TransactionOutput.new( + inputAddr, + CML.Value.from_coin(BigInt(6000000)), + ) ); // base address -const shelleyOutputAddress = CardanoWasm.Address.from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w"); +const outputAddress = CML.Address.from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w"); // pointer address -const shelleyChangeAddress = CardanoWasm.Address.from_bech32("addr_test1gz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerspqgpsqe70et"); +const changeAddress = CML.Address.from_bech32("addr_test1gz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerspqgpsqe70et"); // add output to the tx txBuilder.add_output( - CardanoWasm.TransactionOutput.new( - shelleyOutputAddress, - CardanoWasm.Value.new(CardanoWasm.BigNum.from_str('1000000')) - ), + CML.TransactionOutputBuilder() + .with_address(outputAddress) + .next() + .with_value(CML.Value.from_coin(BigInt(1000000))) + .build() ); -// set the time to live - the absolute slot value before the tx becomes invalid -txBuilder.set_ttl(410021); - // calculate the min fee required and send any change to an address -txBuilder.add_change_if_needed(shelleyChangeAddress); - -// once the transaction is ready, we build it to get the tx body without witnesses -const txBody = txBuilder.build(); -const txHash = CardanoWasm.hash_transaction(txBody); -const witnesses = CardanoWasm.TransactionWitnessSet.new(); - -// add keyhash witnesses -const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new(); -const vkeyWitness = CardanoWasm.make_vkey_witness(txHash, prvKey); -vkeyWitnesses.add(vkeyWitness); -witnesses.set_vkeys(vkeyWitnesses); - -// add bootstrap (Byron-era) witnesses -const cip1852Account = CardanoWasm.Bip32PrivateKey.from_bech32('xprv1hretan5mml3tq2p0twkhq4tz4jvka7m2l94kfr6yghkyfar6m9wppc7h9unw6p65y23kakzct3695rs32z7vaw3r2lg9scmfj8ec5du3ufydu5yuquxcz24jlkjhsc9vsa4ufzge9s00fn398svhacse5su2awrw'); -const bootstrapWitnesses = CardanoWasm.BootstrapWitnesses.new(); -const bootstrapWitness = CardanoWasm.make_icarus_bootstrap_witness( - txHash, - byronAddress, - cip1852Account, +// this moves onto the next step of building the transaction: providing witnesses +const signedTxBuilder = tx_builder.build( + changeAddress, + CML.ChangeSelectionAlgo.Default ); -bootstrapWitnesses.add(bootstrapWitness); -witnesses.set_bootstraps(bootstrapWitnesses); -// create the finalized transaction with witnesses -const transaction = CardanoWasm.Transaction.new( - txBody, - witnesses, - undefined, // transaction metadata -); +// sign with the key that owns the input used +signedTxBuilder.add_vkey(CML.make_vkey_witness(txHash, prvKey)); + +const tx = signedTxBuilder.build_checked(); +// ready to submit, can be converted to CBOR via tx.to_cbor_bytes() or to_cbor_hex() for hex ``` ## A note on fees -Fees is Cardano Shelley are based directly on the size of the final encoded transaction. It is important to note that a transaction created by this library potentially can vary in size compared to one built with other tools. This is because transactions, as well as other Cardano Shelley structures, are encoded using [CBOR](https://cbor.io/) a binary JSON-like encoding. Due to arrays and maps allowing both definite or indefinite length encoding in the encoded transaction created by the library, the size can vary. This is because definite encoding consists of a tag containing the size of the array/map which can be 1 or more bytes long depending on the number of elements the size of the encoded structure, while indefinite length encoding consists of a 1 byte starting tag and after all elements are listed, a 1 byte ending tag. These variances should should only be a couple bytes and cardano-multiplatform-lib uses definite encoding which is the same length or smaller for any reasonable sized transaction. \ No newline at end of file +Fees in Cardano are based directly on the size of the final encoded transaction. It is important to note that a transaction created by this library potentially can vary in size compared to one built with other tools. This is because transactions, as well as other Cardano structures, are encoded using [CBOR](https://cbor.io/) a binary JSON-like encoding. Due to arrays and maps allowing both definite or indefinite length encoding in the encoded transaction created by the library, the size can vary. This is because definite encoding consists of a tag containing the size of the array/map which can be 1 or more bytes long depending on the number of elements the size of the encoded structure, while indefinite length encoding consists of a 1 byte starting tag and after all elements are listed, a 1 byte ending tag. These variances should should only be a couple bytes and cardano-multiplatform-lib uses definite encoding by default which is the same length or smaller for any reasonable sized transaction. \ No newline at end of file diff --git a/docs/docs/modules/builders/index.mdx b/docs/docs/modules/builders/index.mdx new file mode 100644 index 00000000..851258bf --- /dev/null +++ b/docs/docs/modules/builders/index.mdx @@ -0,0 +1,79 @@ +# TransactionBuilder + +In order to simplify transaction creation, we provide a `TransactionBuilder` struct that manages witnesses, fee calculation, change addresses and such. Assume we have instantiated an instance under the variable `builder` for this explanation. The `TransactionBuilder` requires several protocol parameters governing Cardano to be created which is shown in the following section. These are specified initially in the genesis file for Cardano nodes. + +The minimum required for a valid transaction is to add inputs, outputs, and either set the fee explicitly with `builder.set_fee(fee)`, or calculate it implicitly using `builder.add_change_if_needed(address)`. +Optionally a transaction can also have certificates, reward withdrawals, metadata, and minting added to it. +Any change made to the builder can impact the size and thus the fee so the fee should be the last thing set. +If implicitly setting the fee any extra ADA (`inputs + withdrawals - outputs + refund - deposit - min fee`) is sent to the provided change address. +Fees must be sufficient, i.e. `inputs + withdrawals + refund >= outputs + deposit + fee` which must be manually ensured if you explicitly set the fee. Any extra fee is not necessary and the extra ADA beyond that will be burned. +Once the transaction is ready, `const body = builder.build()` can be called to return a ready `TransactionBody`. + +Withdrawals are ADA withdrawn as part of the rewards generated by staking and deposits are refundable ADA locked while resources such as stake certificates or pool registrations exist on the blockchain. They are returned as refunds when these resources are deregistered/retired. + +To get to a transaction ready to post on the blockchain, we must create a `Transaction` from that, which consists of the `TransactionBody`, a matching `TransactionWitnessSet` and optionally an `AuxiliaryData`. +The witnesses and optional metadata must match those provided to the builder. The witnesses must sign the hash of the transaction body returned by `hash_transaction(body)`. In addition to the witnesses for inputs, withdrawals and some certificates require witnesses as well. For example, staking address registration does not require a witness while stake address de-registration requires one. For any questions or doubts about the rules governing fees, deposits, rewards, certificates or which witness types are required refer to the [specs for the relevant era](https://github.com/input-output-hk/cardano-ledger-specs#cardano-ledger), specifically the Shelley design specification for general design for non-governance certificates. Refer to the Conway specs for those. The formal specification could be useful for specific details as well. The design spec contains details about which certificates require which type of witnesses in the Certificates and Registrations section. + +# TransactionBuilderConfig + +To correctly make transactions the builder must know some on-chain parameters such as the current fee costs, key deposits, etc. These can all potentially change, even if some have largely been static for large periods of time. We pass these into the builder via the `TransactionBuilderConfigBuilder`. For test things out hard-coding them might suffice, but these parameters should ideally be fetched from the current blockchain head or your transactions could fail to be accepted by the network or will end up paying higher fees. The cost models parameter is optional if you are not building a transaction that utilizes Plutus smart contracts. + +Code examples for the builders will assume you have a `make_tx_builder()` function that creates a `TransactionBuilder` with the appropriate config. + +# Blockfrost + +One way of getting this information is via the `epochs/latest/parameters` endpoint of blockfrost. This can be automated from rust using the `cml-blockfrost` crate's `make_tx_builder_cfg()`. Blockfrost is by no means necessary but it can be convenient. It is possible to get this information by other means as well e.g. having a synced cardano node. + +Using `cml-blockfrost` (rust): + +```rust +let cfg = cml_blockfrost::make_tx_builder_cfg(&api).await.unwrap(); +let mut tx_builder = TransactionBuilder::new(cfg); +``` + +This could also be done manually similar to below (or reference `cml-blockfrost`'s code) + +Manually using WASM: + +```javascript +let params = await blockfrost.epochsLatestParameters(); + +// cost order is based on lex ordering of keys +let costModels = CML.CostModels.new(); +let v1Costs = params.cost_models['PlutusV1']; +if (v1Costs != null) { + let v1CMLCosts = CML.IntList.new(); + for (key in Object.keys(v1Costs).toSorted()) { + v1CMLCosts.add(CML.Int.new(v1Costs[key])); + } + costModels.set_plutus_v1(v1CMLCosts); +} +// cost order is based on lex ordering of keys +let v2Costs = params.cost_models['PlutusV2']; +if (v2Costs != null) { + let v2CMLCosts = CML.IntList.new(); + for (key in Object.keys(v2Costs).toSorted()) { + v2CMLCosts.add(CML.Int.new(v2Costs[key])); + } + costModels.set_plutus_v2(v2CMLCosts); +} +// note: as of writing this the sancho testnet format is different for v3 +// compared to v1/v2. this may remain true once mainnet switches over so +// please inspect the object you are getting for cost models from blockfrost + +let configBuilder = CML.TransactionBuilderConfigBuilder.new() + .fee_algo(CML.LinearFee.new(params.min_fee_a, params.min_fee_b)) + .coins_per_utxo_byte(BigNum(params.coins_per_utxo_size)) + .pool_deposit(BigNum(params.pool_deposit)) + .key_deposit(BigNum(params.key_deposit)) + .max_value_size(Number(params.max_val_size)) + .max_tx_size(params.max_tx_size) + .ex_unit_prices(CML.ExUnitPrices.new( + CML.SubCoin.from_base10_f32(params.price_mem), + CML.SubCoin.from_base10_f32(params.price_step) + )) + .cost_models(costModels) + .collateral_percentage(params.collateral_percent) + max_collateral_inputs(params.max_collateral_inputs); +let mut txBuilder = CML.TransactionBuilder.new(configBuilder.build()); +``` \ No newline at end of file diff --git a/docs/docs/modules/builders/plutus_contracts.mdx b/docs/docs/modules/builders/plutus_contracts.mdx new file mode 100644 index 00000000..2084331b --- /dev/null +++ b/docs/docs/modules/builders/plutus_contracts.mdx @@ -0,0 +1,139 @@ +--- +sidebar_position: 2 +--- + +# Aiken's Hello World Example + +Using Aiken's [hello world](https://aiken-lang.org/example--hello-world/basics) example we can see how to use datums and redeemers with the transaction builder. + +To deploy the contract datum we need to create an output to the contract with the datum attached like so: + +```rust +let change_addr: Address = todo!("Add your change address here"); +let sk1: PrivateKey = todo!("Add your own private key here that controls the input"); +let sk2: PrivateKey = todo!("Add your own private key here for the contract's datum"); + +let mut tx_builder = make_tx_builder(); + +// input needed to pay for the tx +tx_builder.add_input(SingleInputBuilder::new( + TransactionInput::new( + TransactionHash::from_hex("1665fc34e312445884d752a557e6b3499e1fc10228de77ca712b6bda9078ced7").unwrap(), + 0 + ), + TransactionOutput::new( + addr.clone(), + Value::from(10000000000), + Some(DatumOption::new_hash(contract_datum.hash())), + None + ), +).payment_key().unwrap())?; + +// contract created from bytes from the Plutus.json generated by Aiken +let contract = PlutusV2Script::from_cbor_bytes(&hex::decode("58f2010000323232323232323222232325333008323232533300b002100114a06644646600200200644a66602200229404c8c94ccc040cdc78010028a511330040040013014002375c60240026eb0c038c03cc03cc03cc03cc03cc03cc03cc03cc020c008c020014dd71801180400399b8f375c6002600e00a91010d48656c6c6f2c20576f726c6421002300d00114984d958c94ccc020cdc3a400000226464a66601a601e0042930b1bae300d00130060041630060033253330073370e900000089919299980618070010a4c2c6eb8c030004c01401058c01400c8c014dd5000918019baa0015734aae7555cf2ab9f5742ae881").unwrap()).unwrap(); +let contract_addr = EnterpriseAddress::new( + change_addr.network_id().unwrap(), + StakeCredential::new_script(contract.hash()), +).to_address(); + +// contract datum +let contract_datum = PlutusData::new_constr_plutus_data( + ConstrPlutusData::new(0, vec![PlutusData::new_bytes(sk2.to_public().hash().to_raw_bytes().to_vec())]) +); + +// send funds to the contract +tx_builder.add_output(SingleOutputBuilderResult::new( + TransactionOutput::new(contract_addr.clone(), Value::from(5000000000), None, None) +)); + +let mut signed_tx_builder = tx_builder.build( + ChangeSelectionAlgo::Default, + &addr +)?; + +let tx_hash = hash_transaction(&signed_tx_builder.body()); +signed_tx_builder.add_vkey(make_vkey_witness(&tx_hash, &sk1)); +let tx = signed_tx_builder.build_checked().unwrap(); +``` + +After we've deployed this contract it is time to redeem it. + +```rust +// contract / tx_builder / contract_datum are same as above + +let redeemer_datum = PlutusData::new_constr_plutus_data( + ConstrPlutusData::new(0, vec![PlutusData::new_bytes("Hello, World!".as_bytes().to_vec())]) +); + +tx_builder.add_input(SingleInputBuilder::new( + TransactionInput::new( + TransactionHash::from_hex("6079e89a1eeba9ef1d6a334f8edbf6029ff5299315a3fd55fce732da6a72fd9b").unwrap(), + 0 + ), + TransactionOutput::new( + addr.clone(), + Value::from(7500000), + None, + None + ), +).plutus_script( + PartialPlutusWitness::new( + PlutusScriptWitness::Script(contract.into()), + redeemer_datum.clone() + ), + vec![sk1.to_public().hash(), sk2.to_public().hash()], + contract_datum.clone(), +).unwrap())?; + +// In order to run plutus contracts we must supply collateral which will only be spent if the contract does +// not provide enough ADA specified via exunits to execute it. +tx_builder.add_collateral(SingleInputBuilder::new( + TransactionInput::new( + TransactionHash::from_hex("5acebd1bc82df3d6c8f50908c1a182d5fb2ff0525066fa5a3ec44fe8df80f005").unwrap(), + 0, + ), + TransactionOutput::new( + addr.clone(), + Value::from(5000000), + None, + None, + ), +).payment_key().unwrap())?; +let mut redeemer_builder = tx_builder.build_for_evaluation( + ChangeSelectionAlgo::Default, + &addr, +)?; + +// We must then send this draft tx for evaluation via ogmios, blockfrost, etc +// in order to figure out how many exunits are necessary to evaluate it +println!("draft tx: {}", hex::encode(redeemer_builder.draft_tx()?.to_cbor_bytes())); + +// once we get the exunits required back we can set it and be ready to finalize the signing of the tx +redeemer_builder.set_exunits( + RedeemerWitnessKey::new(RedeemerTag::Spend, 0), + ExUnits::new(5000000, 2000000000), +); +let redeemers = redeemer_builder.build()?; +tx_builder.set_exunits( + RedeemerWitnessKey::new(RedeemerTag::Spend, 0), + ExUnits::new(5000000, 2000000000), +); + + +let mut signed_tx_builder = tx_builder.build( + ChangeSelectionAlgo::Default, + &addr +)?; +let tx_hash = hash_transaction(&signed_tx_builder.body()); +signed_tx_builder + .add_vkey(make_vkey_witness( + &tx_hash, + &sk1, + )); +signed_tx_builder + .add_vkey(make_vkey_witness( + &tx_hash, + &sk2, + )); +let tx = signed_tx_builder.build_checked().unwrap(); +``` \ No newline at end of file diff --git a/docs/docs/modules/cbor.mdx b/docs/docs/modules/cbor.mdx new file mode 100644 index 00000000..51536c15 --- /dev/null +++ b/docs/docs/modules/cbor.mdx @@ -0,0 +1,88 @@ +--- +sidebar_position: 1 +--- + +# CBOR + +Cardano on-chain types are stored using [CBOR](https://www.rfc-editor.org/rfc/rfc7049), a data format similar to JSON but with many more features and in binary. + +## Tool Interoperability + +Due to CBOR's flexibility it is possible that one piece of CBOR can be represented in multiple ways in the binary encoding. This causes problems when using CBOR taken on-chain or from another tool and using it with another tool. Notably, one small difference in the binary encoding of CBOR could result in hashes being totally different. e.g. metadatum hashes or transaction hashes calculated in a dApp might be different than in the wallet causing the entire transaction to be rejected by the network. + +CML solves this by supporting automatically every single possible CBOR encoding variation. On-chain types created by deserializing from CBOR bytes will remember these details and re-serializing will use them and result in the same CBOR bytes, unlike some other tools. + +## Rust + +On-chan types in rust can (de)serialize to/from CBOR Via the `Serialize`/`Deserialize` and `ToBytes`/`FromBytes` traits located within the `cml_core::serialize` module. + +Most on-chain types implement the `Serialize` and `Deserialize` traits. These traits guarantee that all CBOR encoding details are preserved upon deserialization and upon serialization it is possible to choose between canonical CBOR encoding and arbitrary encodings (the original it was decoded from). + +Byron-era types do not implement `Serialize`/`Deserialize` and instead implement `ToBytes`/`FromBytes`. Byron on-chain types are always in canonical CBOR so this was not necessary. + +The types in the `cip25` module also do not support `Serialize`/`Deserialize` in favor of `ToBytes`/`FromBytes`. The underlying metadata on-chain does and you should use the types in`cml_core::metadata` + +```rust +use cml_core::serialization::{Serialize, Deserialize}; +let canonical_cbor_hex = "825820aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa01"; +// these all represent the following CBOR: +// [ ; array of 2 elements (transaction input struct) +// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, ; bytes (tx hash) +// 1 ; unsigned integer (tx index) +// ] +let non_canonical_cbor = [ + canonical_cbor_hex, + "825820aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1a00000001", + "9f5f48aaaaaaaaaaaaaaaa48aaaaaaaaaaaaaaaa48aaaaaaaaaaaaaaaa48aaaaaaaaaaaaaaaaff01ff", + "9900025820aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa190001", + "9b00000000000000025f41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aaff1b0000000000000001", +]; +for orig_cbor_hex in non_canonical_cbor { + let tx_in = TransactionInput::from_cbor_bytes(&hex::decode(orig_cbor_hex).unwrap()).unwrap(); + // serialize back to cbor bytes using the same cbor encoding details so it will match + // the format where it came from + assert_eq!(hex::encode(tx_in.to_cbor_bytes()), orig_cbor_hex); + // no matter how it was created it will represent the same data and can be encoded to + // canonical cbor bytes which will be the same as all of these are the same transaction input + assert_eq!(hex::encode(tx_in.to_canonical_cbor_bytes()), canonical_cbor_hex); +} +``` + +## WASM + +All on-chain types have the traits directly exposed on each struct as the methods: +* `.to_cbor_bytes()` +* `.to_canonical_cbor_bytes()` +* `.from_cbor_bytes()` +* `.to_cbor_hex()` +* `.to_canonical_cbor_hex()` +* `.from_cbor_hex()` + +The hex ones are useful for working with CIP-30 (dApp connector). + +On post-Byron on-chain types this delegates to `Serialize`/`Deserialize` (see rust section) and preserve round-trip always. CIP25 and Byron types will always serialize to canonical CBOR. All on-chain data during the Byron era has to be canonical CBOR so this is not a big issue but is worth noting. + +```javascript +let canonicalCborHex = "825820aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa01"; +// these all represent the following CBOR: +// [ ; array of 2 elements (transaction input struct) +// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, ; bytes (tx hash) +// 1 ; unsigned integer (tx index) +// ] +let nonCanonicalCbor = [ + canonicalCborHex, + "825820aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1a00000001", + "9f5f48aaaaaaaaaaaaaaaa48aaaaaaaaaaaaaaaa48aaaaaaaaaaaaaaaa48aaaaaaaaaaaaaaaaff01ff", + "9900025820aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa190001", + "9b00000000000000025f41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aa41aaff1b0000000000000001", +]; +for (let origCborHex of nonCanonicalCbor) { + let txIn = CML.TransactionInput.from_cbor_hex(orig_cbor_hex); + // serialize back to cbor bytes using the same cbor encoding details so it will match + // the format where it came from + console.assert(txIn.to_cbor_hex() == origCborHex); + // no matter how it was created it will represent the same data and can be encoded to + // canonical cbor bytes which will be the same as all of these are the same transaction input + console.assert(txIn.to_canonical_cbor_hex() == canonicalCborHex); +} +``` \ No newline at end of file diff --git a/docs/docs/modules/cip36.mdx b/docs/docs/modules/cip36.mdx index 4e7a6cf1..bd146e02 100644 --- a/docs/docs/modules/cip36.mdx +++ b/docs/docs/modules/cip36.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 5 +sidebar_position: 4 --- # CIP36 diff --git a/docs/docs/modules/json.mdx b/docs/docs/modules/json.mdx new file mode 100644 index 00000000..5cc74e66 --- /dev/null +++ b/docs/docs/modules/json.mdx @@ -0,0 +1,63 @@ +--- +sidebar_position: 2 +--- + +# JSON + +## General structs + +All on-chain types have to/from JSON support. The vast majority is auto-generated but some have custom logic e.g. `Url`, `Ipv4`, `BigInteger`, etc. + +### WASM + +In WASM JSON conversions are exposed by `.to_json()` and `.from_json()` methods on all supported wrappers. There is also a `to_js_value()`. + +```javascript +let txInJson = "{\"transaction_id\":\"0fba1404ed9b82b41938ba2e8bda7bec8cce813fb7e7cd7692b43caa76fe891c\",\"index\":3}"; + +let txIn = CML.TransactionInput.from_json(txInJson); + +console.log(`txIn JSON: ${txIn.to_json()}`); +``` + +### Rust + +JSON conversions are exposed in rust via the [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) and [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) traits together with `serde_json`. + +example: +```rust +let tx_in_json = "{\"transaction_id\":\"0fba1404ed9b82b41938ba2e8bda7bec8cce813fb7e7cd7692b43caa76fe891c\",\"index\":3}"; + +// from JSON using serde_json::from_str() - note the type annotations +let tx_in: TransactionInput = serde_json::from_str(tx_in_json).unwrap(); + +// to JSON using serde_json::to_string() - use to_string_pretty() if you want more human-readable formatting +println!("tx_in JSON: {}", serde_json::to_string(&tx_in).unwrap()); +``` + +## Metadata + +Metadata, on top of the generic API mentioned above, has specific JSON functionality for compatability with cardano-node. + +There are three formats on `MetadataJsonSchema`. `NoConversions` is the stricted, stricter than cardano-node and only converts when there are no implicit conversions at all. `BasicConversions` is the node's `TxMetadataJsonNoSchema` and `DetailedSchema` its `TxMetadataJsonDetailedSchema`. See `MetadataJsonSchema` for more info on the schema. + +```javascript +let basic_json = "{\"0x8badf00d\": \"0xdeadbeef\",\"9\": 5,\"obj\": {\"a\":[{\"5\": 2},{}]}}"; +let metadatum = CML.encode_json_str_to_metadatum(basic_json, CML.MetadataJsonSchema.BasicConversions); +console.log(`detailed json: ${CML.decode_metadatum_to_json_str(metadatum, CML.MetadataJsonSchema.DetailedSchema)}`); +// OUTPUT: +// detailed json: {"map":[{"k":{"bytes":"8badf00d"},"v":{"bytes":"deadbeef"}},{"k":{"int":9},"v":{"int":5}},{"k":{"string":"obj"},"v":{"map":[{"k":{"string":"a"},"v":{"list":[{"map":[{"k":{"int":5},"v":{"int":2}}]},{"map":[]}]}}]}}]} +``` + +## Plutus Datums + +Plutus datums also have additional cardano-node JSON support. Remember that Plutus has no String datum so the strings there will be converted to utf8 bytes. See `CardanoNodePlutusDatumSchema` for more info on the schema. + +```javascript +let basic_json = "{ \"100\": [ { \"x\": \"0\", \"y\": 1 } ], \"foo\": \"0x0000baadf00d0000cafed00d0000deadbeef0000\" }"; +let datum = CML.encode_json_str_to_plutus_datum(basic_json, CML.CardanoNodePlutusDatumSchema.BasicConversions); +console.log(`detailed json: ${CML.decode_plutus_datum_to_json_str(datum, CML.CardanoNodePlutusDatumSchema.DetailedSchema, +)}`); +// OUTPUT: +// detailed json: {"map":[{"k":{"int":100},"v":{"list":[{"map":[{"k":{"bytes":"78"},"v":{"bytes":"30"}},{"k":{"bytes":"79"},"v":{"int":1}}]}]}},{"k":{"bytes":"666f6f"},"v":{"bytes":"0000baadf00d0000cafed00d0000deadbeef0000"}}]} +``` \ No newline at end of file diff --git a/docs/docs/modules/multi-era/_category_.json b/docs/docs/modules/multi-era/_category_.json new file mode 100644 index 00000000..35a0d5b7 --- /dev/null +++ b/docs/docs/modules/multi-era/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Multi-Era", + "position": 2, + "link": { + "type": "generated-index" + } +} diff --git a/docs/docs/modules/multi-era/index.md b/docs/docs/modules/multi-era/index.md new file mode 100644 index 00000000..b037f89c --- /dev/null +++ b/docs/docs/modules/multi-era/index.md @@ -0,0 +1,7 @@ +# Multi-Era + +This crate contains all the on-chain types for previous eras (Byron, Shelley, Alonzo, Babbage, etc). There are also wrappers around this era if you need era-agnostic types e.g. parsing all blocks from genesis. The wrappers support the current era as well. + +## Parsing blocks across eras + +`MultiEraBlock` can be used for this. Take care about the format you are giving it. Some tools (e.g. Pallas/Oura) won't give you the block format from the binary spec directly, but will instead have it wrapped in some network wrapper array containing the explicit era tag. If your CBOR looks like `[uint, ]` (likely starting with `82` in hex e.g. `8201`, `8204`, `8207`, etc) then you should use `MultiEraBlock.from_explicit_network_cbor_bytes()` instead of `MultiEraBlock.from_cbor_bytes()`. \ No newline at end of file diff --git a/docs/docs/modules/wasm.mdx b/docs/docs/modules/wasm.mdx new file mode 100644 index 00000000..680a2337 --- /dev/null +++ b/docs/docs/modules/wasm.mdx @@ -0,0 +1,15 @@ +--- +sidebar_position: 6 +--- + +# WASM Usage + +## Memory Management + +If you are using CML from the browser this section is likely irrelevant for you. +Using CML from a javascript environment with weakrefs enabled should have automatic memory cleanup. +If this is not the case (e.g. non-javascript/typescript WASM environment), or you are using CML inside of a very tight loop that is executed hundreds of thousands of times in a short period it might be advisable to explicitly call `.free()` on any CML types after they are used. +This is because while from an environment with weakrefs the types will eventually be freed automatically, +it is still possible to use excessive memory or run out if, for example, large CML types are created in a constant loop that runs many times (e.g. hundreds of thousands of times without a break), as the automatic cleanup will not be run in time. Do not worry about this for normal CML usage. +Do not call `.free()` on a type or use it after `.free()` has been called on it already. +WASM types passed into other CML APIs will be done so by reference and will not have their `.free()` method called just by doing so, but will still eventually be cleaned up if weakrefs are available. \ No newline at end of file diff --git a/multi-era/rust/Cargo.toml b/multi-era/rust/Cargo.toml index bb5c5e66..37d29a25 100644 --- a/multi-era/rust/Cargo.toml +++ b/multi-era/rust/Cargo.toml @@ -13,6 +13,9 @@ keywords = ["cardano"] [lib] crate-type = ["cdylib", "rlib"] +[features] +used_from_wasm = ["wasm-bindgen"] + [dependencies] cml-core = { path = "../../core/rust", version = "5.3.1" } cml-crypto = { path = "../../crypto/rust", version = "5.3.1" } @@ -23,8 +26,13 @@ derivative = "2.2.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.57" schemars = "0.8.8" -wasm-bindgen = { version = "0.2", features=["serde-serialize"] } # only for declaring hash types bech32 = "0.7.2" -hex = "0.4.0" \ No newline at end of file +hex = "0.4.0" + +# non-wasm +noop_proc_macro = { version = "0.3.0" } + +# wasm +wasm-bindgen = { version = "0.2.87", optional = true } \ No newline at end of file diff --git a/multi-era/rust/src/allegra/mod.rs b/multi-era/rust/src/allegra/mod.rs index c9be16f4..38de508b 100644 --- a/multi-era/rust/src/allegra/mod.rs +++ b/multi-era/rust/src/allegra/mod.rs @@ -5,6 +5,11 @@ pub mod cbor_encodings; pub mod serialization; pub mod utils; +#[cfg(not(feature = "used_from_wasm"))] +use noop_proc_macro::wasm_bindgen; +#[cfg(feature = "used_from_wasm")] +use wasm_bindgen::prelude::wasm_bindgen; + use crate::shelley::{ GenesisKeyDelegation, ShelleyHeader, ShelleyPoolParams, ShelleyPoolRegistration, ShelleyTransactionOutput, ShelleyUpdate, @@ -14,7 +19,7 @@ use cbor_encodings::{ AllegraTransactionWitnessSetEncoding, }; use cml_chain::assets::Coin; -use cml_chain::auxdata::{ShelleyFormatAuxData, ShelleyMaFormatAuxData}; +use cml_chain::auxdata::{ShelleyFormatAuxData, ShelleyMAFormatAuxData}; use cml_chain::certs::{ PoolRetirement, StakeCredential, StakeDelegation, StakeDeregistration, StakeRegistration, }; @@ -32,7 +37,7 @@ use self::cbor_encodings::{MoveInstantaneousRewardEncoding, MoveInstantaneousRew #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub enum AllegraAuxiliaryData { Shelley(ShelleyFormatAuxData), - ShelleyMA(ShelleyMaFormatAuxData), + ShelleyMA(ShelleyMAFormatAuxData), } impl AllegraAuxiliaryData { @@ -40,8 +45,8 @@ impl AllegraAuxiliaryData { Self::Shelley(shelley) } - pub fn new_shelley_m_a(shelley_m_a: ShelleyMaFormatAuxData) -> Self { - Self::ShelleyMA(shelley_m_a) + pub fn new_shelley_ma(shelley_ma: ShelleyMAFormatAuxData) -> Self { + Self::ShelleyMA(shelley_ma) } } @@ -111,12 +116,12 @@ impl AllegraCertificate { pub fn new_genesis_key_delegation( genesis_hash: GenesisHash, genesis_delegate_hash: GenesisDelegateHash, - v_r_f_key_hash: VRFKeyHash, + vrf_key_hash: VRFKeyHash, ) -> Self { Self::GenesisKeyDelegation(GenesisKeyDelegation::new( genesis_hash, genesis_delegate_hash, - v_r_f_key_hash, + vrf_key_hash, )) } @@ -259,7 +264,7 @@ impl MIRAction { serde::Serialize, schemars::JsonSchema, )] -#[wasm_bindgen::prelude::wasm_bindgen] +#[wasm_bindgen] pub enum MIRPot { Reserve, Treasury, diff --git a/multi-era/rust/src/allegra/serialization.rs b/multi-era/rust/src/allegra/serialization.rs index 0e997cba..3ffbda9f 100644 --- a/multi-era/rust/src/allegra/serialization.rs +++ b/multi-era/rust/src/allegra/serialization.rs @@ -10,7 +10,7 @@ use cbor_event::de::Deserializer; use cbor_event::se::Serializer; use cml_chain::address::RewardAccount; use cml_chain::auxdata::ShelleyFormatAuxData; -use cml_chain::auxdata::ShelleyMaFormatAuxData; +use cml_chain::auxdata::ShelleyMAFormatAuxData; use cml_core::error::*; use cml_core::serialization::*; use cml_crypto::RawBytesEncoding; @@ -26,8 +26,8 @@ impl Serialize for AllegraAuxiliaryData { AllegraAuxiliaryData::Shelley(shelley) => { shelley.serialize(serializer, force_canonical) } - AllegraAuxiliaryData::ShelleyMA(shelley_m_a) => { - shelley_m_a.serialize(serializer, force_canonical) + AllegraAuxiliaryData::ShelleyMA(shelley_ma) => { + shelley_ma.serialize(serializer, force_canonical) } } } @@ -49,9 +49,9 @@ impl Deserialize for AllegraAuxiliaryData { } }; let deser_variant: Result<_, DeserializeError> = - ShelleyMaFormatAuxData::deserialize(raw); + ShelleyMAFormatAuxData::deserialize(raw); match deser_variant { - Ok(shelley_m_a) => return Ok(Self::ShelleyMA(shelley_m_a)), + Ok(shelley_ma) => return Ok(Self::ShelleyMA(shelley_ma)), Err(e) => { errs.push(e.annotate("ShelleyMA")); raw.as_mut_ref() diff --git a/multi-era/rust/src/allegra/utils.rs b/multi-era/rust/src/allegra/utils.rs index 86eedd25..53ea8922 100644 --- a/multi-era/rust/src/allegra/utils.rs +++ b/multi-era/rust/src/allegra/utils.rs @@ -15,7 +15,7 @@ impl From for AuxiliaryData { fn from(aux: AllegraAuxiliaryData) -> Self { match aux { AllegraAuxiliaryData::Shelley(md) => AuxiliaryData::new_shelley(md), - AllegraAuxiliaryData::ShelleyMA(md) => AuxiliaryData::new_shelley_m_a(md), + AllegraAuxiliaryData::ShelleyMA(md) => AuxiliaryData::new_shelley_ma(md), } } } diff --git a/multi-era/rust/src/alonzo/mod.rs b/multi-era/rust/src/alonzo/mod.rs index cceb8c31..7ba47fb4 100644 --- a/multi-era/rust/src/alonzo/mod.rs +++ b/multi-era/rust/src/alonzo/mod.rs @@ -5,6 +5,11 @@ pub mod cbor_encodings; pub mod serialization; pub mod utils; +#[cfg(not(feature = "used_from_wasm"))] +use noop_proc_macro::wasm_bindgen; +#[cfg(feature = "used_from_wasm")] +use wasm_bindgen::prelude::wasm_bindgen; + use crate::allegra::AllegraCertificate; use crate::shelley::{ProtocolVersionStruct, ShelleyHeader}; use cbor_encodings::{ @@ -13,7 +18,7 @@ use cbor_encodings::{ AlonzoUpdateEncoding, }; use cml_chain::assets::{Coin, Mint}; -use cml_chain::auxdata::{Metadata, ShelleyFormatAuxData, ShelleyMaFormatAuxData}; +use cml_chain::auxdata::{Metadata, ShelleyFormatAuxData, ShelleyMAFormatAuxData}; use cml_chain::crypto::{ AuxiliaryDataHash, BootstrapWitness, GenesisHash, Nonce, ScriptDataHash, Vkeywitness, }; @@ -29,7 +34,7 @@ use self::cbor_encodings::AlonzoRedeemerEncoding; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub enum AlonzoAuxiliaryData { Shelley(ShelleyFormatAuxData), - ShelleyMA(ShelleyMaFormatAuxData), + ShelleyMA(ShelleyMAFormatAuxData), Alonzo(AlonzoFormatAuxData), } @@ -38,8 +43,8 @@ impl AlonzoAuxiliaryData { Self::Shelley(shelley) } - pub fn new_shelley_m_a(shelley_m_a: ShelleyMaFormatAuxData) -> Self { - Self::ShelleyMA(shelley_m_a) + pub fn new_shelley_ma(shelley_ma: ShelleyMAFormatAuxData) -> Self { + Self::ShelleyMA(shelley_ma) } pub fn new_alonzo(alonzo: AlonzoFormatAuxData) -> Self { @@ -210,7 +215,7 @@ impl AlonzoRedeemer { serde::Serialize, schemars::JsonSchema, )] -#[wasm_bindgen::prelude::wasm_bindgen] +#[wasm_bindgen] pub enum AlonzoRedeemerTag { Spend, Mint, diff --git a/multi-era/rust/src/alonzo/serialization.rs b/multi-era/rust/src/alonzo/serialization.rs index 8cbeeae6..03259bc7 100644 --- a/multi-era/rust/src/alonzo/serialization.rs +++ b/multi-era/rust/src/alonzo/serialization.rs @@ -47,7 +47,7 @@ impl Deserialize for AlonzoAuxiliaryData { } }; let deser_variant: Result<_, DeserializeError> = - ShelleyMaFormatAuxData::deserialize(raw); + ShelleyMAFormatAuxData::deserialize(raw); match deser_variant { Ok(shelley_m_a) => return Ok(Self::ShelleyMA(shelley_m_a)), Err(e) => { diff --git a/multi-era/rust/src/alonzo/utils.rs b/multi-era/rust/src/alonzo/utils.rs index 802d8207..e371153a 100644 --- a/multi-era/rust/src/alonzo/utils.rs +++ b/multi-era/rust/src/alonzo/utils.rs @@ -22,12 +22,12 @@ impl From for AuxiliaryData { fn from(aux: AlonzoAuxiliaryData) -> Self { match aux { AlonzoAuxiliaryData::Shelley(md) => AuxiliaryData::new_shelley(md.clone()), - AlonzoAuxiliaryData::ShelleyMA(md) => AuxiliaryData::new_shelley_m_a(md.clone()), + AlonzoAuxiliaryData::ShelleyMA(md) => AuxiliaryData::new_shelley_ma(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/mod.rs b/multi-era/rust/src/babbage/mod.rs index 9a8e0f17..b42abc16 100644 --- a/multi-era/rust/src/babbage/mod.rs +++ b/multi-era/rust/src/babbage/mod.rs @@ -15,7 +15,7 @@ use cbor_encodings::{ }; use cml_chain::address::Address; use cml_chain::assets::{Coin, Value}; -use cml_chain::auxdata::{Metadata, ShelleyFormatAuxData, ShelleyMaFormatAuxData}; +use cml_chain::auxdata::{Metadata, ShelleyFormatAuxData, ShelleyMAFormatAuxData}; use cml_chain::block::Header; use cml_chain::crypto::{ AuxiliaryDataHash, BootstrapWitness, GenesisHash, ScriptDataHash, Vkeywitness, @@ -37,7 +37,7 @@ use self::utils::BabbageMint; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub enum BabbageAuxiliaryData { Shelley(ShelleyFormatAuxData), - ShelleyMA(ShelleyMaFormatAuxData), + ShelleyMA(ShelleyMAFormatAuxData), Babbage(BabbageFormatAuxData), } @@ -46,8 +46,8 @@ impl BabbageAuxiliaryData { Self::Shelley(shelley) } - pub fn new_shelley_m_a(shelley_m_a: ShelleyMaFormatAuxData) -> Self { - Self::ShelleyMA(shelley_m_a) + pub fn new_shelley_ma(shelley_ma: ShelleyMAFormatAuxData) -> Self { + Self::ShelleyMA(shelley_ma) } pub fn new_babbage(babbage: BabbageFormatAuxData) -> Self { diff --git a/multi-era/rust/src/babbage/serialization.rs b/multi-era/rust/src/babbage/serialization.rs index b4f611bb..03dc76cb 100644 --- a/multi-era/rust/src/babbage/serialization.rs +++ b/multi-era/rust/src/babbage/serialization.rs @@ -22,8 +22,8 @@ impl Serialize for BabbageAuxiliaryData { BabbageAuxiliaryData::Shelley(shelley) => { shelley.serialize(serializer, force_canonical) } - BabbageAuxiliaryData::ShelleyMA(shelley_m_a) => { - shelley_m_a.serialize(serializer, force_canonical) + BabbageAuxiliaryData::ShelleyMA(shelley_ma) => { + shelley_ma.serialize(serializer, force_canonical) } BabbageAuxiliaryData::Babbage(babbage) => { babbage.serialize(serializer, force_canonical) @@ -48,7 +48,7 @@ impl Deserialize for BabbageAuxiliaryData { } }; let deser_variant: Result<_, DeserializeError> = - ShelleyMaFormatAuxData::deserialize(raw); + ShelleyMAFormatAuxData::deserialize(raw); match deser_variant { Ok(shelley_m_a) => return Ok(Self::ShelleyMA(shelley_m_a)), Err(e) => { diff --git a/multi-era/rust/src/babbage/utils.rs b/multi-era/rust/src/babbage/utils.rs index 9d060ecd..cd45180c 100644 --- a/multi-era/rust/src/babbage/utils.rs +++ b/multi-era/rust/src/babbage/utils.rs @@ -63,13 +63,13 @@ impl From for AuxiliaryData { fn from(aux: BabbageAuxiliaryData) -> Self { match aux { BabbageAuxiliaryData::Shelley(md) => AuxiliaryData::new_shelley(md.clone()), - BabbageAuxiliaryData::ShelleyMA(md) => AuxiliaryData::new_shelley_m_a(md.clone()), + BabbageAuxiliaryData::ShelleyMA(md) => AuxiliaryData::new_shelley_ma(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 }), } @@ -194,7 +194,8 @@ impl Serialize for BabbageMint { .map(|(i, _)| i) .collect::>(); if force_canonical { - inner_key_order.sort_by(|i, j| assets[*i].0.get().cmp(assets[*j].0.get())); + inner_key_order + .sort_by(|i, j| assets[*i].0.to_raw_bytes().cmp(assets[*j].0.to_raw_bytes())); } for (j, (asset_name, coin)) in inner_key_order.into_iter().zip(assets.iter()) { diff --git a/multi-era/rust/src/shelley/cbor_encodings.rs b/multi-era/rust/src/shelley/cbor_encodings.rs index 247d79b0..dda4684e 100644 --- a/multi-era/rust/src/shelley/cbor_encodings.rs +++ b/multi-era/rust/src/shelley/cbor_encodings.rs @@ -11,7 +11,7 @@ pub struct GenesisKeyDelegationEncoding { pub tag_encoding: Option, pub genesis_hash_encoding: StringEncoding, pub genesis_delegate_hash_encoding: StringEncoding, - pub v_r_f_key_hash_encoding: StringEncoding, + pub vrf_key_hash_encoding: StringEncoding, } #[derive(Clone, Debug, Default)] @@ -58,7 +58,7 @@ pub struct ShelleyBlockEncoding { } #[derive(Clone, Debug, Default)] -pub struct ShelleyDnsNameEncoding { +pub struct ShelleyDNSNameEncoding { pub inner_encoding: StringEncoding, } @@ -69,7 +69,7 @@ pub struct ShelleyHeaderBodyEncoding { pub slot_encoding: Option, pub prev_hash_encoding: StringEncoding, pub issuer_vkey_encoding: StringEncoding, - pub v_r_f_vkey_encoding: StringEncoding, + pub vrf_vkey_encoding: StringEncoding, pub block_body_size_encoding: Option, pub block_body_hash_encoding: StringEncoding, } diff --git a/multi-era/rust/src/shelley/mod.rs b/multi-era/rust/src/shelley/mod.rs index d42a3c90..fdbb4183 100644 --- a/multi-era/rust/src/shelley/mod.rs +++ b/multi-era/rust/src/shelley/mod.rs @@ -36,7 +36,7 @@ use std::convert::TryFrom; use crate::allegra::MIRPot; use self::cbor_encodings::{ - GenesisKeyDelegationEncoding, ProtocolVersionStructEncoding, ShelleyDnsNameEncoding, + GenesisKeyDelegationEncoding, ProtocolVersionStructEncoding, ShelleyDNSNameEncoding, ShelleyMoveInstantaneousRewardEncoding, ShelleyMoveInstantaneousRewardsCertEncoding, ShelleyPoolParamsEncoding, }; @@ -44,7 +44,7 @@ use self::cbor_encodings::{ pub struct GenesisKeyDelegation { pub genesis_hash: GenesisHash, pub genesis_delegate_hash: GenesisDelegateHash, - pub v_r_f_key_hash: VRFKeyHash, + pub vrf_key_hash: VRFKeyHash, #[serde(skip)] pub encodings: Option, } @@ -53,12 +53,12 @@ impl GenesisKeyDelegation { pub fn new( genesis_hash: GenesisHash, genesis_delegate_hash: GenesisDelegateHash, - v_r_f_key_hash: VRFKeyHash, + vrf_key_hash: VRFKeyHash, ) -> Self { Self { genesis_hash, genesis_delegate_hash, - v_r_f_key_hash, + vrf_key_hash, encodings: None, } } @@ -238,12 +238,12 @@ impl ShelleyCertificate { pub fn new_genesis_key_delegation( genesis_hash: GenesisHash, genesis_delegate_hash: GenesisDelegateHash, - v_r_f_key_hash: VRFKeyHash, + vrf_key_hash: VRFKeyHash, ) -> Self { Self::GenesisKeyDelegation(GenesisKeyDelegation::new( genesis_hash, genesis_delegate_hash, - v_r_f_key_hash, + vrf_key_hash, )) } @@ -257,12 +257,12 @@ impl ShelleyCertificate { } #[derive(Clone, Debug)] -pub struct ShelleyDnsName { +pub struct ShelleyDNSName { pub inner: String, - pub encodings: Option, + pub encodings: Option, } -impl ShelleyDnsName { +impl ShelleyDNSName { pub fn get(&self) -> &String { &self.inner } @@ -270,7 +270,7 @@ impl ShelleyDnsName { pub fn new(inner: String) -> Result { if inner.len() > 64 { return Err(DeserializeError::new( - "ShelleyDnsName", + "ShelleyDNSName", DeserializeFailure::RangeCheck { found: inner.len() as isize, min: Some(0), @@ -285,15 +285,15 @@ impl ShelleyDnsName { } } -impl TryFrom for ShelleyDnsName { +impl TryFrom for ShelleyDNSName { type Error = DeserializeError; fn try_from(inner: String) -> Result { - ShelleyDnsName::new(inner) + ShelleyDNSName::new(inner) } } -impl serde::Serialize for ShelleyDnsName { +impl serde::Serialize for ShelleyDNSName { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -302,7 +302,7 @@ impl serde::Serialize for ShelleyDnsName { } } -impl<'de> serde::de::Deserialize<'de> for ShelleyDnsName { +impl<'de> serde::de::Deserialize<'de> for ShelleyDNSName { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, @@ -311,15 +311,15 @@ impl<'de> serde::de::Deserialize<'de> for ShelleyDnsName { Self::new(inner.clone()).map_err(|_e| { serde::de::Error::invalid_value( serde::de::Unexpected::Str(&inner), - &"invalid ShelleyDnsName", + &"invalid ShelleyDNSName", ) }) } } -impl schemars::JsonSchema for ShelleyDnsName { +impl schemars::JsonSchema for ShelleyDNSName { fn schema_name() -> String { - String::from("ShelleyDnsName") + String::from("ShelleyDNSName") } fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { @@ -355,7 +355,7 @@ pub struct ShelleyHeaderBody { pub slot: u64, pub prev_hash: Option, pub issuer_vkey: Vkey, - pub v_r_f_vkey: VRFVkey, + pub vrf_vkey: VRFVkey, pub nonce_vrf: VRFCert, pub leader_vrf: VRFCert, pub block_body_size: u64, @@ -372,7 +372,7 @@ impl ShelleyHeaderBody { slot: u64, prev_hash: Option, issuer_vkey: Vkey, - v_r_f_vkey: VRFVkey, + vrf_vkey: VRFVkey, nonce_vrf: VRFCert, leader_vrf: VRFCert, block_body_size: u64, @@ -385,7 +385,7 @@ impl ShelleyHeaderBody { slot, prev_hash, issuer_vkey, - v_r_f_vkey, + vrf_vkey, nonce_vrf, leader_vrf, block_body_size, @@ -433,13 +433,15 @@ impl ShelleyMoveInstantaneousRewardsCert { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct ShelleyMultiHostName { - pub shelley_dns_name: ShelleyDnsName, + /// A SRV DNS record + pub shelley_dns_name: ShelleyDNSName, #[serde(skip)] pub encodings: Option, } impl ShelleyMultiHostName { - pub fn new(shelley_dns_name: ShelleyDnsName) -> Self { + /// * `shelley_dns_name` - A SRV DNS record + pub fn new(shelley_dns_name: ShelleyDNSName) -> Self { Self { shelley_dns_name, encodings: None, @@ -578,12 +580,12 @@ impl ShelleyRelay { pub fn new_shelley_single_host_name( port: Option, - shelley_dns_name: ShelleyDnsName, + shelley_dns_name: ShelleyDNSName, ) -> Self { Self::ShelleySingleHostName(ShelleySingleHostName::new(port, shelley_dns_name)) } - pub fn new_shelley_multi_host_name(shelley_dns_name: ShelleyDnsName) -> Self { + pub fn new_shelley_multi_host_name(shelley_dns_name: ShelleyDNSName) -> Self { Self::ShelleyMultiHostName(ShelleyMultiHostName::new(shelley_dns_name)) } } @@ -591,13 +593,15 @@ impl ShelleyRelay { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct ShelleySingleHostName { pub port: Option, - pub shelley_dns_name: ShelleyDnsName, + /// An A or AAAA DNS record + pub shelley_dns_name: ShelleyDNSName, #[serde(skip)] pub encodings: Option, } impl ShelleySingleHostName { - pub fn new(port: Option, shelley_dns_name: ShelleyDnsName) -> Self { + /// * `shelley_dns_name` - An A or AAAA DNS record + pub fn new(port: Option, shelley_dns_name: ShelleyDNSName) -> Self { Self { port, shelley_dns_name, @@ -732,8 +736,8 @@ impl ShelleyUpdate { } } -impl From for String { - fn from(wrapper: ShelleyDnsName) -> Self { +impl From for String { + fn from(wrapper: ShelleyDNSName) -> Self { wrapper.inner } } diff --git a/multi-era/rust/src/shelley/serialization.rs b/multi-era/rust/src/shelley/serialization.rs index a69361b7..6fbc30a3 100644 --- a/multi-era/rust/src/shelley/serialization.rs +++ b/multi-era/rust/src/shelley/serialization.rs @@ -72,13 +72,13 @@ impl SerializeEmbeddedGroup for GenesisKeyDelegation { ), )?; serializer.write_bytes_sz( - self.v_r_f_key_hash.to_raw_bytes(), + self.vrf_key_hash.to_raw_bytes(), self.encodings .as_ref() - .map(|encs| encs.v_r_f_key_hash_encoding.clone()) + .map(|encs| encs.vrf_key_hash_encoding.clone()) .unwrap_or_default() .to_str_len_sz( - self.v_r_f_key_hash.to_raw_bytes().len() as u64, + self.vrf_key_hash.to_raw_bytes().len() as u64, force_canonical, ), )?; @@ -146,7 +146,7 @@ impl DeserializeEmbeddedGroup for GenesisKeyDelegation { .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) }) .map_err(|e: DeserializeError| e.annotate("genesis_delegate_hash"))?; - let (v_r_f_key_hash, v_r_f_key_hash_encoding) = raw + let (vrf_key_hash, vrf_key_hash_encoding) = raw .bytes_sz() .map_err(Into::::into) .and_then(|(bytes, enc)| { @@ -154,17 +154,17 @@ impl DeserializeEmbeddedGroup for GenesisKeyDelegation { .map(|bytes| (bytes, StringEncoding::from(enc))) .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) }) - .map_err(|e: DeserializeError| e.annotate("v_r_f_key_hash"))?; + .map_err(|e: DeserializeError| e.annotate("vrf_key_hash"))?; Ok(GenesisKeyDelegation { genesis_hash, genesis_delegate_hash, - v_r_f_key_hash, + vrf_key_hash, encodings: Some(GenesisKeyDelegationEncoding { len_encoding, tag_encoding, genesis_hash_encoding, genesis_delegate_hash_encoding, - v_r_f_key_hash_encoding, + vrf_key_hash_encoding, }), }) })() @@ -1305,7 +1305,7 @@ impl Deserialize for ShelleyCertificate { } } -impl Serialize for ShelleyDnsName { +impl Serialize for ShelleyDNSName { fn serialize<'se, W: Write>( &self, serializer: &'se mut Serializer, @@ -1322,14 +1322,14 @@ impl Serialize for ShelleyDnsName { } } -impl Deserialize for ShelleyDnsName { +impl Deserialize for ShelleyDNSName { fn deserialize(raw: &mut Deserializer) -> Result { let (inner, inner_encoding) = raw .text_sz() .map(|(s, enc)| (s, StringEncoding::from(enc)))?; if inner.len() > 64 { return Err(DeserializeError::new( - "ShelleyDnsName", + "ShelleyDNSName", DeserializeFailure::RangeCheck { found: inner.len() as isize, min: Some(0), @@ -1339,7 +1339,7 @@ impl Deserialize for ShelleyDnsName { } Ok(Self { inner, - encodings: Some(ShelleyDnsNameEncoding { inner_encoding }), + encodings: Some(ShelleyDNSNameEncoding { inner_encoding }), }) } } @@ -1454,12 +1454,12 @@ impl Serialize for ShelleyHeaderBody { ), )?; serializer.write_bytes_sz( - self.v_r_f_vkey.to_raw_bytes(), + self.vrf_vkey.to_raw_bytes(), self.encodings .as_ref() - .map(|encs| encs.v_r_f_vkey_encoding.clone()) + .map(|encs| encs.vrf_vkey_encoding.clone()) .unwrap_or_default() - .to_str_len_sz(self.v_r_f_vkey.to_raw_bytes().len() as u64, force_canonical), + .to_str_len_sz(self.vrf_vkey.to_raw_bytes().len() as u64, force_canonical), )?; self.nonce_vrf.serialize(serializer, force_canonical)?; self.leader_vrf.serialize(serializer, force_canonical)?; @@ -1547,7 +1547,7 @@ impl Deserialize for ShelleyHeaderBody { .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) }) .map_err(|e: DeserializeError| e.annotate("issuer_vkey"))?; - let (v_r_f_vkey, v_r_f_vkey_encoding) = raw + let (vrf_vkey, vrf_vkey_encoding) = raw .bytes_sz() .map_err(Into::::into) .and_then(|(bytes, enc)| { @@ -1555,7 +1555,7 @@ impl Deserialize for ShelleyHeaderBody { .map(|bytes| (bytes, StringEncoding::from(enc))) .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) }) - .map_err(|e: DeserializeError| e.annotate("v_r_f_vkey"))?; + .map_err(|e: DeserializeError| e.annotate("vrf_vkey"))?; let nonce_vrf = VRFCert::deserialize(raw).map_err(|e: DeserializeError| e.annotate("nonce_vrf"))?; let leader_vrf = VRFCert::deserialize(raw) @@ -1592,7 +1592,7 @@ impl Deserialize for ShelleyHeaderBody { slot, prev_hash, issuer_vkey, - v_r_f_vkey, + vrf_vkey, nonce_vrf, leader_vrf, block_body_size, @@ -1605,7 +1605,7 @@ impl Deserialize for ShelleyHeaderBody { slot_encoding, prev_hash_encoding, issuer_vkey_encoding, - v_r_f_vkey_encoding, + vrf_vkey_encoding, block_body_size_encoding, block_body_hash_encoding, }), @@ -1997,7 +1997,7 @@ impl DeserializeEmbeddedGroup for ShelleyMultiHostName { Ok(Some(tag_encoding)) })() .map_err(|e| e.annotate("tag"))?; - let shelley_dns_name = ShelleyDnsName::deserialize(raw) + let shelley_dns_name = ShelleyDNSName::deserialize(raw) .map_err(|e: DeserializeError| e.annotate("shelley_dns_name"))?; Ok(ShelleyMultiHostName { shelley_dns_name, @@ -3468,8 +3468,8 @@ impl DeserializeEmbeddedGroup for ShelleySingleHostName { }) })() .map_err(|e| e.annotate("port"))?; - let shelley_dns_name = ShelleyDnsName::deserialize(raw) - .map_err(|e: DeserializeError| e.annotate("shelley_dns_name"))?; + let shelley_dns_name = ShelleyDNSName::deserialize(raw) + .map_err(|e: DeserializeError| e.annotate("shelley__dns_name"))?; Ok(ShelleySingleHostName { port, shelley_dns_name, diff --git a/multi-era/rust/src/shelley/utils.rs b/multi-era/rust/src/shelley/utils.rs index 13749094..e92ec241 100644 --- a/multi-era/rust/src/shelley/utils.rs +++ b/multi-era/rust/src/shelley/utils.rs @@ -1,5 +1,5 @@ use cml_chain::{ - certs::{DnsName, PoolParams, PoolRegistration, Relay}, + certs::{DNSName, PoolParams, PoolRegistration, Relay}, transaction::{NativeScript, TransactionWitnessSet}, }; @@ -91,10 +91,10 @@ impl From for Relay { } ShelleyRelay::ShelleySingleHostName(host) => Self::new_single_host_name( host.port, - DnsName::new(host.shelley_dns_name.inner).unwrap(), + DNSName::new(host.shelley_dns_name.inner).unwrap(), ), ShelleyRelay::ShelleyMultiHostName(host) => { - Self::new_multi_host_name(DnsName::new(host.shelley_dns_name.inner).unwrap()) + Self::new_multi_host_name(DNSName::new(host.shelley_dns_name.inner).unwrap()) } } } diff --git a/multi-era/rust/src/utils.rs b/multi-era/rust/src/utils.rs index 679d2e64..f526afe3 100644 --- a/multi-era/rust/src/utils.rs +++ b/multi-era/rust/src/utils.rs @@ -343,7 +343,7 @@ impl MultiEraBlockHeader { match self { Self::ByronEB(_) => None, Self::Byron(_) => None, - Self::Shelley(header) => Some(&header.body.v_r_f_vkey), + Self::Shelley(header) => Some(&header.body.vrf_vkey), Self::Babbage(header) => Some(&header.header_body.vrf_vkey), } } diff --git a/multi-era/wasm/Cargo.toml b/multi-era/wasm/Cargo.toml index e4c5bedc..51d6d997 100644 --- a/multi-era/wasm/Cargo.toml +++ b/multi-era/wasm/Cargo.toml @@ -14,19 +14,19 @@ keywords = ["cardano"] crate-type = ["cdylib", "rlib"] [dependencies] -cml-chain = { path = "../../chain/rust", version = "5.3.1" } +cml-chain = { path = "../../chain/rust", version = "5.3.1", features = ["used_from_wasm"] } cml-chain-wasm = { path = "../../chain/wasm", version = "5.3.1" } cml-crypto = { path = "../../crypto/rust", version = "5.3.1" } cml-crypto-wasm = { path = "../../crypto/wasm", version = "5.3.1" } cml-core = { path = "../../core/rust", version = "5.3.1" } cml-core-wasm = { path = "../../core/wasm", version = "5.3.1" } -cml-multi-era = { path = "../rust", version = "5.3.1" } +cml-multi-era = { path = "../rust", version = "5.3.1", features = ["used_from_wasm"] } cbor_event = "2.4.0" hex = "0.4.0" linked-hash-map = "0.5.3" serde_json = "1.0.57" serde-wasm-bindgen = "0.4.5" -wasm-bindgen = { version = "0.2", features=["serde-serialize"] } +wasm-bindgen = { version = "0.2.87" } # not actual multi-era dependencies but we re-export these for the wasm builds cml-cip25-wasm = { path = "../../cip25/wasm", version = "5.3.1" } -cml-cip36-wasm = { path = "../../cip36/wasm", version = "5.3.1" } \ No newline at end of file +cml-cip36-wasm = { path = "../../cip36/wasm", version = "5.3.1" } diff --git a/multi-era/wasm/json-gen/src/lib.rs b/multi-era/wasm/json-gen/src/lib.rs index c84629a4..7a1c6896 100644 --- a/multi-era/wasm/json-gen/src/lib.rs +++ b/multi-era/wasm/json-gen/src/lib.rs @@ -135,7 +135,7 @@ pub fn export_schemas() { gen_json_schema!(cml_multi_era::shelley::MultisigScript); gen_json_schema!(cml_multi_era::shelley::ShelleyBlock); gen_json_schema!(cml_multi_era::shelley::ShelleyCertificate); - gen_json_schema!(cml_multi_era::shelley::ShelleyDnsName); + gen_json_schema!(cml_multi_era::shelley::ShelleyDNSName); gen_json_schema!(cml_multi_era::shelley::ShelleyHeader); gen_json_schema!(cml_multi_era::shelley::ShelleyHeaderBody); gen_json_schema!(cml_multi_era::shelley::ShelleyMoveInstantaneousReward); diff --git a/multi-era/wasm/src/allegra/mod.rs b/multi-era/wasm/src/allegra/mod.rs index 223a9488..9465a22f 100644 --- a/multi-era/wasm/src/allegra/mod.rs +++ b/multi-era/wasm/src/allegra/mod.rs @@ -9,7 +9,7 @@ use crate::{ MapTransactionIndexToAllegraAuxiliaryData, ShelleyTransactionOutputList, }; use cml_chain_wasm::assets::Coin; -use cml_chain_wasm::auxdata::{ShelleyFormatAuxData, ShelleyMaFormatAuxData}; +use cml_chain_wasm::auxdata::{ShelleyFormatAuxData, ShelleyMAFormatAuxData}; use cml_chain_wasm::certs::StakeCredential; use cml_chain_wasm::certs::{ PoolRetirement, StakeDelegation, StakeDeregistration, StakeRegistration, @@ -44,11 +44,9 @@ impl AllegraAuxiliaryData { )) } - pub fn new_shelley_m_a(shelley_m_a: &ShelleyMaFormatAuxData) -> Self { + pub fn new_shelley_ma(shelley_ma: &ShelleyMAFormatAuxData) -> Self { Self( - cml_multi_era::allegra::AllegraAuxiliaryData::new_shelley_m_a( - shelley_m_a.clone().into(), - ), + cml_multi_era::allegra::AllegraAuxiliaryData::new_shelley_ma(shelley_ma.clone().into()), ) } @@ -72,10 +70,10 @@ impl AllegraAuxiliaryData { } } - pub fn as_shelley_m_a(&self) -> Option { + pub fn as_shelley_ma(&self) -> Option { match &self.0 { - cml_multi_era::allegra::AllegraAuxiliaryData::ShelleyMA(shelley_m_a) => { - Some(shelley_m_a.clone().into()) + cml_multi_era::allegra::AllegraAuxiliaryData::ShelleyMA(shelley_ma) => { + Some(shelley_ma.clone().into()) } _ => None, } @@ -190,13 +188,13 @@ impl AllegraCertificate { pub fn new_genesis_key_delegation( genesis_hash: &GenesisHash, genesis_delegate_hash: &GenesisDelegateHash, - v_r_f_key_hash: &VRFKeyHash, + vrf_key_hash: &VRFKeyHash, ) -> Self { Self( cml_multi_era::allegra::AllegraCertificate::new_genesis_key_delegation( genesis_hash.clone().into(), genesis_delegate_hash.clone().into(), - v_r_f_key_hash.clone().into(), + vrf_key_hash.clone().into(), ), ) } diff --git a/multi-era/wasm/src/alonzo/mod.rs b/multi-era/wasm/src/alonzo/mod.rs index 3ccffc41..887c4dc8 100644 --- a/multi-era/wasm/src/alonzo/mod.rs +++ b/multi-era/wasm/src/alonzo/mod.rs @@ -7,7 +7,7 @@ use crate::{ AlonzoTransactionWitnessSetList, GenesisHashList, MapTransactionIndexToAlonzoAuxiliaryData, }; use cml_chain_wasm::assets::{Coin, Mint}; -use cml_chain_wasm::auxdata::{Metadata, ShelleyFormatAuxData, ShelleyMaFormatAuxData}; +use cml_chain_wasm::auxdata::{Metadata, ShelleyFormatAuxData, ShelleyMAFormatAuxData}; use cml_chain_wasm::crypto::Nonce; use cml_chain_wasm::plutus::{CostModels, ExUnitPrices, ExUnits, PlutusData}; use cml_chain_wasm::RequiredSigners; @@ -42,9 +42,9 @@ impl AlonzoAuxiliaryData { )) } - pub fn new_shelley_m_a(shelley_m_a: &ShelleyMaFormatAuxData) -> Self { - Self(cml_multi_era::alonzo::AlonzoAuxiliaryData::new_shelley_m_a( - shelley_m_a.clone().into(), + pub fn new_shelley_ma(shelley_ma: &ShelleyMAFormatAuxData) -> Self { + Self(cml_multi_era::alonzo::AlonzoAuxiliaryData::new_shelley_ma( + shelley_ma.clone().into(), )) } @@ -77,10 +77,10 @@ impl AlonzoAuxiliaryData { } } - pub fn as_shelley_m_a(&self) -> Option { + pub fn as_shelley_ma(&self) -> Option { match &self.0 { - cml_multi_era::alonzo::AlonzoAuxiliaryData::ShelleyMA(shelley_m_a) => { - Some(shelley_m_a.clone().into()) + cml_multi_era::alonzo::AlonzoAuxiliaryData::ShelleyMA(shelley_ma) => { + Some(shelley_ma.clone().into()) } _ => None, } diff --git a/multi-era/wasm/src/babbage/mod.rs b/multi-era/wasm/src/babbage/mod.rs index 479818c5..fcaa77be 100644 --- a/multi-era/wasm/src/babbage/mod.rs +++ b/multi-era/wasm/src/babbage/mod.rs @@ -9,7 +9,7 @@ use crate::{ }; use cml_chain_wasm::address::Address; use cml_chain_wasm::assets::{Coin, Mint, Value}; -use cml_chain_wasm::auxdata::{ShelleyFormatAuxData, ShelleyMaFormatAuxData}; +use cml_chain_wasm::auxdata::{ShelleyFormatAuxData, ShelleyMAFormatAuxData}; use cml_chain_wasm::block::Header; use cml_chain_wasm::crypto::{AuxiliaryDataHash, GenesisHash, ScriptDataHash}; use cml_chain_wasm::plutus::{CostModels, ExUnitPrices, ExUnits, PlutusV1Script, PlutusV2Script}; @@ -43,11 +43,9 @@ impl BabbageAuxiliaryData { )) } - pub fn new_shelley_m_a(shelley_m_a: &ShelleyMaFormatAuxData) -> Self { + pub fn new_shelley_ma(shelley_ma: &ShelleyMAFormatAuxData) -> Self { Self( - cml_multi_era::babbage::BabbageAuxiliaryData::new_shelley_m_a( - shelley_m_a.clone().into(), - ), + cml_multi_era::babbage::BabbageAuxiliaryData::new_shelley_ma(shelley_ma.clone().into()), ) } @@ -80,10 +78,10 @@ impl BabbageAuxiliaryData { } } - pub fn as_shelley_m_a(&self) -> Option { + pub fn as_shelley_ma(&self) -> Option { match &self.0 { - cml_multi_era::babbage::BabbageAuxiliaryData::ShelleyMA(shelley_m_a) => { - Some(shelley_m_a.clone().into()) + cml_multi_era::babbage::BabbageAuxiliaryData::ShelleyMA(shelley_ma) => { + Some(shelley_ma.clone().into()) } _ => None, } @@ -703,6 +701,7 @@ impl BabbageTransactionBody { } pub fn set_mint(&mut self, mint: &Mint) { + // hand-edit self.0.mint = Some(cml_multi_era::babbage::utils::BabbageMint::from(Into::< cml_chain::assets::Mint, >::into( @@ -711,6 +710,7 @@ impl BabbageTransactionBody { } pub fn mint(&self) -> Option { + // hand-edit self.0 .mint .as_ref() diff --git a/multi-era/wasm/src/shelley/mod.rs b/multi-era/wasm/src/shelley/mod.rs index 76004a6c..f4ee6d3c 100644 --- a/multi-era/wasm/src/shelley/mod.rs +++ b/multi-era/wasm/src/shelley/mod.rs @@ -49,19 +49,19 @@ impl GenesisKeyDelegation { self.0.genesis_delegate_hash.into() } - pub fn v_r_f_key_hash(&self) -> VRFKeyHash { - self.0.v_r_f_key_hash.into() + pub fn vrf_key_hash(&self) -> VRFKeyHash { + self.0.vrf_key_hash.into() } pub fn new( genesis_hash: &GenesisHash, genesis_delegate_hash: &GenesisDelegateHash, - v_r_f_key_hash: &VRFKeyHash, + vrf_key_hash: &VRFKeyHash, ) -> Self { Self(cml_multi_era::shelley::GenesisKeyDelegation::new( genesis_hash.clone().into(), genesis_delegate_hash.clone().into(), - v_r_f_key_hash.clone().into(), + vrf_key_hash.clone().into(), )) } } @@ -378,13 +378,13 @@ impl ShelleyCertificate { pub fn new_genesis_key_delegation( genesis_hash: &GenesisHash, genesis_delegate_hash: &GenesisDelegateHash, - v_r_f_key_hash: &VRFKeyHash, + vrf_key_hash: &VRFKeyHash, ) -> Self { Self( cml_multi_era::shelley::ShelleyCertificate::new_genesis_key_delegation( genesis_hash.clone().into(), genesis_delegate_hash.clone().into(), - v_r_f_key_hash.clone().into(), + vrf_key_hash.clone().into(), ), ) } @@ -504,14 +504,14 @@ pub enum ShelleyCertificateKind { #[derive(Clone, Debug)] #[wasm_bindgen] -pub struct ShelleyDnsName(cml_multi_era::shelley::ShelleyDnsName); +pub struct ShelleyDNSName(cml_multi_era::shelley::ShelleyDNSName); -impl_wasm_cbor_json_api!(ShelleyDnsName); +impl_wasm_cbor_json_api!(ShelleyDNSName); -impl_wasm_conversions!(cml_multi_era::shelley::ShelleyDnsName, ShelleyDnsName); +impl_wasm_conversions!(cml_multi_era::shelley::ShelleyDNSName, ShelleyDNSName); #[wasm_bindgen] -impl ShelleyDnsName { +impl ShelleyDNSName { pub fn get(&self) -> String { self.0.get().clone() } @@ -569,8 +569,8 @@ impl ShelleyHeaderBody { self.0.issuer_vkey.clone().into() } - pub fn v_r_f_vkey(&self) -> VRFVkey { - self.0.v_r_f_vkey.into() + pub fn vrf_vkey(&self) -> VRFVkey { + self.0.vrf_vkey.into() } pub fn nonce_vrf(&self) -> VRFCert { @@ -602,7 +602,7 @@ impl ShelleyHeaderBody { slot: u64, prev_hash: Option, issuer_vkey: &Vkey, - v_r_f_vkey: &VRFVkey, + vrf_vkey: &VRFVkey, nonce_vrf: &VRFCert, leader_vrf: &VRFCert, block_body_size: u64, @@ -615,7 +615,7 @@ impl ShelleyHeaderBody { slot, prev_hash.map(Into::into), issuer_vkey.clone().into(), - v_r_f_vkey.clone().into(), + vrf_vkey.clone().into(), nonce_vrf.clone().into(), leader_vrf.clone().into(), block_body_size, @@ -696,11 +696,12 @@ impl_wasm_conversions!( #[wasm_bindgen] impl ShelleyMultiHostName { - pub fn shelley_dns_name(&self) -> ShelleyDnsName { + pub fn shelley_dns_name(&self) -> ShelleyDNSName { self.0.shelley_dns_name.clone().into() } - pub fn new(shelley_dns_name: &ShelleyDnsName) -> Self { + /// * `shelley_dns_name` - A SRV DNS record + pub fn new(shelley_dns_name: &ShelleyDNSName) -> Self { Self(cml_multi_era::shelley::ShelleyMultiHostName::new( shelley_dns_name.clone().into(), )) @@ -1024,7 +1025,7 @@ impl ShelleyRelay { pub fn new_shelley_single_host_name( port: Option, - shelley_dns_name: &ShelleyDnsName, + shelley_dns_name: &ShelleyDNSName, ) -> Self { Self( cml_multi_era::shelley::ShelleyRelay::new_shelley_single_host_name( @@ -1034,7 +1035,7 @@ impl ShelleyRelay { ) } - pub fn new_shelley_multi_host_name(shelley_dns_name: &ShelleyDnsName) -> Self { + pub fn new_shelley_multi_host_name(shelley_dns_name: &ShelleyDNSName) -> Self { Self( cml_multi_era::shelley::ShelleyRelay::new_shelley_multi_host_name( shelley_dns_name.clone().into(), @@ -1108,11 +1109,12 @@ impl ShelleySingleHostName { self.0.port } - pub fn shelley_dns_name(&self) -> ShelleyDnsName { + pub fn shelley_dns_name(&self) -> ShelleyDNSName { self.0.shelley_dns_name.clone().into() } - pub fn new(port: Option, shelley_dns_name: &ShelleyDnsName) -> Self { + /// * `shelley_dns_name` - An A or AAAA DNS record + pub fn new(port: Option, shelley_dns_name: &ShelleyDNSName) -> Self { Self(cml_multi_era::shelley::ShelleySingleHostName::new( port, shelley_dns_name.clone().into(), diff --git a/specs/conway/assets.cddl b/specs/conway/assets.cddl index 1fcef9bd..a1b86709 100644 --- a/specs/conway/assets.cddl +++ b/specs/conway/assets.cddl @@ -4,7 +4,7 @@ coin = uint ; but possibly it could be its own type with bounds checking (!=0) positive_coin = coin -asset_name = bytes .size (0..32) +asset_name = bytes .size (0..32) ; @doc Use TryFrom<&str> / TryInto<&str> for utf8 text conversion and RawBytesEncoding for direct bytes access ; these two are technically hand-written but we keep them here so that other code can ; continue thinking they're maps and handle them like so (encoding details, etc) diff --git a/specs/conway/auxdata.cddl b/specs/conway/auxdata.cddl index 5e801638..d9a08dda 100644 --- a/specs/conway/auxdata.cddl +++ b/specs/conway/auxdata.cddl @@ -12,7 +12,7 @@ metadata = _CDDL_CODEGEN_EXTERN_TYPE_ shelley_format_aux_data = metadata -shelley_ma_format_aux_data = [ transaction_metadata: metadata ; Shelley-ma +shelley_MA_format_aux_data = [ transaction_metadata: metadata ; Shelley-ma , auxiliary_scripts: [ * native_script ] ] @@ -26,5 +26,5 @@ conway_format_aux_data = #6.259({ auxiliary_data = shelley_format_aux_data ; @name shelley - / shelley_ma_format_aux_data ; @name shelley_m_a + / shelley_MA_format_aux_data ; @name shelley_MA / conway_format_aux_data ; @name conway diff --git a/specs/conway/certs.cddl b/specs/conway/certs.cddl index e104e593..3f01d3fe 100644 --- a/specs/conway/certs.cddl +++ b/specs/conway/certs.cddl @@ -1,22 +1,32 @@ -certificate = - [ stake_registration - // stake_deregistration - // stake_delegation - // pool_registration - // pool_retirement - // reg_cert - // unreg_cert - // vote_deleg_cert - // stake_vote_deleg_cert - // stake_reg_deleg_cert - // vote_reg_deleg_cert - // stake_vote_reg_deleg_cert - // auth_committee_hot_cert - // resign_committee_cold_cert - // reg_drep_cert - // unreg_drep_cert - // update_drep_cert - ] +certificate = [ + ; @doc Will be deprecated in the next era. Use RegCert instead which takes an explicit deposit amount, as that can change. + stake_registration // + ; @doc Will be deprecated in the next era. Use UnregCert instead which takes an explicit deposit amount, as that can change. + stake_deregistration // + ; @doc Delegate to a take pool only + stake_delegation // + pool_registration // + pool_retirement // + ; @doc Registers a stake credential. + reg_cert // + ; @doc Unregisters a stake credential. + unreg_cert // + ; @doc Delegate to a DRep for voting only + vote_deleg_cert // + ; @doc Delegate to a stake pool and a DRep + stake_vote_deleg_cert // + ; @doc Register a stake credential and delegate to a pool in a single cert + stake_reg_deleg_cert // + ; @doc Register a stake credential and delegate to a DRep in a single cert + vote_reg_deleg_cert // + ; @doc Register a stake credential and delegate to a pool and a DRep in a single cert + stake_vote_reg_deleg_cert // + auth_committee_hot_cert // + resign_committee_cold_cert // + reg_drep_cert // + unreg_drep_cert // + update_drep_cert +] stake_registration = (tag: 0, stake_credential) stake_deregistration = (tag: 1, stake_credential) @@ -39,8 +49,8 @@ pool_retirement = ( ; move_instantaneous_rewards_cert = (tag: 6, move_instantaneous_reward) ; DELEG -reg_cert = (tag: 7, stake_credential, coin) -unreg_cert = (tag: 8, stake_credential, coin) +reg_cert = (tag: 7, stake_credential, deposit: coin) +unreg_cert = (tag: 8, stake_credential, deposit: coin) vote_deleg_cert = (tag: 9, stake_credential, d_rep) stake_vote_deleg_cert = ( tag: 10, @@ -52,22 +62,22 @@ stake_reg_deleg_cert = ( tag: 11, stake_credential, pool: ed25519_key_hash, - coin + deposit: coin ) -vote_reg_deleg_cert = (tag: 12, stake_credential, d_rep, coin) +vote_reg_deleg_cert = (tag: 12, stake_credential, d_rep, deposit: coin) stake_vote_reg_deleg_cert = ( tag: 13, stake_credential, pool: ed25519_key_hash, d_rep, - coin + deposit: coin ) ; GOVCERT auth_committee_hot_cert = (14, committee_cold_credential, committee_hot_credential) resign_committee_cold_cert = (15, committee_cold_credential, anchor / null) -reg_drep_cert = (16, drep_credential, coin, anchor / null) -unreg_drep_cert = (17, drep_credential, coin) +reg_drep_cert = (16, drep_credential, deposit: coin, anchor / null) +unreg_drep_cert = (17, drep_credential, deposit: coin) update_drep_cert = (18, drep_credential, anchor / null) d_rep = [ @@ -104,7 +114,7 @@ pool_params = ( operator: ed25519_key_hash ipv4 = bytes .size 4 ; @custom_json ipv6 = bytes .size 16 ; @custom_json -dns_name = tstr .size (0..128) +DNS_name = tstr .size (0..128) single_host_addr = ( tag: 0 , port / null @@ -113,10 +123,10 @@ single_host_addr = ( tag: 0 ) single_host_name = ( tag: 1 , port / null - , dns_name ; An A or AAAA DNS record + , DNS_name ; @doc An A or AAAA DNS record ) multi_host_name = ( tag: 2 - , dns_name ; A SRV DNS record + , DNS_name ; @doc A SRV DNS record ) relay = [ single_host_addr diff --git a/specs/conway/governance.cddl b/specs/conway/governance.cddl index 16353f60..67037a2c 100644 --- a/specs/conway/governance.cddl +++ b/specs/conway/governance.cddl @@ -26,8 +26,8 @@ gov_action = [ parameter_change_action = ( tag: 0, - gov_action_id / null, - protocol_param_update, + action_id: gov_action_id / null, + update: protocol_param_update, policy_hash: script_hash / null, ) diff --git a/specs/conway/plutus.cddl b/specs/conway/plutus.cddl index f045cfbe..8e9bbaf9 100644 --- a/specs/conway/plutus.cddl +++ b/specs/conway/plutus.cddl @@ -14,9 +14,14 @@ plutus_v1_script = bytes ; @newtype plutus_v2_script = bytes ; @newtype plutus_v3_script = bytes ; @newtype +; original def: +; { * plutus_data => plutus_data } +; we hand-code it to handle duplicates +plutus_map = _CDDL_CODEGEN_EXTERN_TYPE_ + plutus_data = constr_plutus_data - / { * plutus_data => plutus_data } ; @name map + / plutus_map ; @name map / [ * plutus_data ] ; @name list / big_integer ; @name integer / bounded_bytes ; @name bytes @@ -45,9 +50,9 @@ legacy_redeemer = [ tag: redeemer_tag, index: uint, data: plutus_data, ex_units: ; Flat Array support is included for backwards compatibility and will be removed in the next era. ; It is recommended for tools to adopt using a Map instead of Array going forward. redeemers = - ; @name LegacyFlatFormat + ; @name LegacyFlatFormat @doc This format will be deprecated in the next era [ + legacy_redeemer ] / - ; @name MapFormat + ; @name MapFormat @doc New map format. Use this when possibe. { + redeemer_key => redeemer_val } redeemer_tag = @@ -67,8 +72,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 +;} diff --git a/specs/conway/transaction.cddl b/specs/conway/transaction.cddl index 1351f567..d7b151ad 100644 --- a/specs/conway/transaction.cddl +++ b/specs/conway/transaction.cddl @@ -149,18 +149,16 @@ transaction_witness_set = { ? 7: nonempty_set, ; @name plutus_v3_scripts } -native_script = - [ script_pubkey - // script_all - // script_any - // script_n_of_k - // script_invalid_before - ; Timelock validity intervals are half-open intervals [a, b). - ; This field specifies the left (included) endpoint a. - // script_invalid_hereafter - ; Timelock validity intervals are half-open intervals [a, b). - ; This field specifies the right (excluded) endpoint b. - ] +native_script = [ + script_pubkey // + script_all // + script_any // + script_n_of_k // + ; @doc Timelock validity intervals are half-open intervals [a, b). This field specifies the left (included) endpoint a. + script_invalid_before // + ; @doc Timelock validity intervals are half-open intervals [a, b). This field specifies the right (excluded) endpoint b. + script_invalid_hereafter +] script_pubkey = (tag: 0, ed25519_key_hash) script_all = (tag: 1, [ * native_script ]) diff --git a/specs/multiera/cml_chain/address.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/address.cddl similarity index 100% rename from specs/multiera/cml_chain/address.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/address.cddl diff --git a/specs/multiera/cml_chain/assets.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/assets.cddl similarity index 100% rename from specs/multiera/cml_chain/assets.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/assets.cddl diff --git a/specs/multiera/cml_chain/auxdata.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/auxdata.cddl similarity index 62% rename from specs/multiera/cml_chain/auxdata.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/auxdata.cddl index 2df63764..99ef11eb 100644 --- a/specs/multiera/cml_chain/auxdata.cddl +++ b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/auxdata.cddl @@ -1,3 +1,3 @@ metadata = _CDDL_CODEGEN_EXTERN_TYPE_ shelley_format_aux_data = _CDDL_CODEGEN_EXTERN_TYPE_ -shelley_ma_format_aux_data = _CDDL_CODEGEN_EXTERN_TYPE_ \ No newline at end of file +shelley_MA_format_aux_data = _CDDL_CODEGEN_EXTERN_TYPE_ \ No newline at end of file diff --git a/specs/multiera/cml_chain/block.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/block.cddl similarity index 100% rename from specs/multiera/cml_chain/block.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/block.cddl diff --git a/specs/multiera/cml_chain/byron.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/byron.cddl similarity index 100% rename from specs/multiera/cml_chain/byron.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/byron.cddl diff --git a/specs/multiera/cml_chain/certs.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/certs.cddl similarity index 100% rename from specs/multiera/cml_chain/certs.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/certs.cddl diff --git a/specs/multiera/cml_chain/crypto.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/crypto.cddl similarity index 100% rename from specs/multiera/cml_chain/crypto.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/crypto.cddl diff --git a/specs/multiera/cml_chain/mod.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/mod.cddl similarity index 100% rename from specs/multiera/cml_chain/mod.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/mod.cddl diff --git a/specs/multiera/cml_chain/plutus.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/plutus.cddl similarity index 100% rename from specs/multiera/cml_chain/plutus.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/plutus.cddl diff --git a/specs/multiera/cml_chain/transaction.cddl b/specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/transaction.cddl similarity index 100% rename from specs/multiera/cml_chain/transaction.cddl rename to specs/multiera/_CDDL_CODEGEN_EXTERN_DEPS_DIR_/cml_chain/transaction.cddl diff --git a/specs/multiera/allegra/mod.cddl b/specs/multiera/allegra/mod.cddl index f43a42a5..267cfbd8 100644 --- a/specs/multiera/allegra/mod.cddl +++ b/specs/multiera/allegra/mod.cddl @@ -51,7 +51,7 @@ allegra_transaction_witness_set = { allegra_auxiliary_data = shelley_format_aux_data ; @name shelley - / shelley_ma_format_aux_data ; @name shelley_m_a + / shelley_MA_format_aux_data ; @name shelley_MA ; allegra differences allegra_transaction_body = { diff --git a/specs/multiera/alonzo/mod.cddl b/specs/multiera/alonzo/mod.cddl index 15ae7642..9b738d13 100644 --- a/specs/multiera/alonzo/mod.cddl +++ b/specs/multiera/alonzo/mod.cddl @@ -95,7 +95,7 @@ alonzo_format_aux_data = #6.259({ alonzo_auxiliary_data = shelley_format_aux_data ; @name shelley - / shelley_ma_format_aux_data ; @name shelley_m_a + / shelley_MA_format_aux_data ; @name shelley_MA / alonzo_format_aux_data ; @name alonzo alonzo_redeemer = [ diff --git a/specs/multiera/babbage/mod.cddl b/specs/multiera/babbage/mod.cddl index 99d887d7..bb6db753 100644 --- a/specs/multiera/babbage/mod.cddl +++ b/specs/multiera/babbage/mod.cddl @@ -115,7 +115,7 @@ babbage_format_aux_data = #6.259({ babbage_auxiliary_data = shelley_format_aux_data ; @name shelley - / shelley_ma_format_aux_data ; @name shelley_m_a + / shelley_MA_format_aux_data ; @name shelley_MA / babbage_format_aux_data ; @name babbage ; babbage mint has duplicate keys so it needs its own special hand-written struct diff --git a/specs/multiera/shelley/mod.cddl b/specs/multiera/shelley/mod.cddl index 8eb0fb51..1e6791ba 100644 --- a/specs/multiera/shelley/mod.cddl +++ b/specs/multiera/shelley/mod.cddl @@ -49,16 +49,16 @@ shelley_pool_params = ( operator: ed25519_key_hash , pool_metadata: pool_metadata / null ) -shelley_dns_name = tstr .size (0..64) +shelley_DNS_name = tstr .size (0..64) shelley_single_host_name = ( tag: 1, port / null, - shelley_dns_name, ; An A or AAAA DNS record @name dns_name + shelley_DNS_name, ; @doc An A or AAAA DNS record @name dns_name ) shelley_multi_host_name = ( tag: 2, - shelley_dns_name, ; A SRV DNS record @name dns_name + shelley_DNS_name, ; @doc A SRV DNS record @name dns_name ) shelley_relay = [