From 8127a75c038ca5f9b92f9529f587c42ca8acaa5b Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Mon, 31 May 2021 19:32:14 -0500 Subject: [PATCH 01/11] Plutus parts of alonzo initial implementation --- rust/src/lib.rs | 2 + rust/src/plutus.rs | 1282 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1284 insertions(+) create mode 100644 rust/src/plutus.rs diff --git a/rust/src/lib.rs b/rust/src/lib.rs index bf0344fb..76cdf66b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -42,6 +42,7 @@ pub mod fees; pub mod impl_mockchain; pub mod legacy_address; pub mod metadata; +pub mod plutus; pub mod serialization; pub mod tx_builder; pub mod typed_bytes; @@ -52,6 +53,7 @@ pub mod utils; use address::*; use crypto::*; use error::*; +use plutus::*; use metadata::*; use utils::*; diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs new file mode 100644 index 00000000..98455ea9 --- /dev/null +++ b/rust/src/plutus.rs @@ -0,0 +1,1282 @@ +use std::io::{BufRead, Seek, Write}; +use super::*; + +// This library was code-generated using an experimental CDDL to rust tool: +// https://github.com/Emurgo/cddl-codegen + +use cbor_event::{self, de::Deserializer, se::{Serialize, Serializer}}; + + +// TODO: replace with its own struct if we decide to add other functionality? +type PlutusScript = Vec; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ConstrPlutusData { + tag: Int, + data: PlutusList, +} + +to_from_bytes!(ConstrPlutusData); + +#[wasm_bindgen] +impl ConstrPlutusData { + pub fn tag(&self) -> Int { + self.tag.clone() + } + + pub fn data(&self) -> PlutusList { + self.data.clone() + } + + pub fn new(tag: Int, data: &PlutusList) -> Self { + Self { + tag, + data: data.clone(), + } + } +} + +impl ConstrPlutusData { + fn is_tag_compact(tag: i128) -> bool { + (tag >= 121 && tag <= 127) || (tag >= 1280 && tag <= 1400) + } + + const GENERAL_FORM_TAG: u64 = 102; +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct CostModel(std::collections::BTreeMap); + +to_from_bytes!(CostModel); + +#[wasm_bindgen] +impl CostModel { + pub fn new() -> Self { + Self(std::collections::BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: String, value: &PreludeInteger) -> Option { + self.0.insert(key, value.clone()) + } + + pub fn get(&self, key: String) -> Option { + self.0.get(&key).map(|v| v.clone()) + } + + pub fn keys(&self) -> Strings { + Strings(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Costmdls(std::collections::BTreeMap); + +to_from_bytes!(Costmdls); + +#[wasm_bindgen] +impl Costmdls { + pub fn new() -> Self { + Self(std::collections::BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: &Language, value: &CostModel) -> Option { + self.0.insert(key.clone(), value.clone()) + } + + pub fn get(&self, key: &Language) -> Option { + self.0.get(key).map(|v| v.clone()) + } + + pub fn keys(&self) -> Languages { + Languages(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ExUnitPrices { + mem_price: Coin, + step_price: Coin, +} + +to_from_bytes!(ExUnitPrices); + +#[wasm_bindgen] +impl ExUnitPrices { + pub fn mem_price(&self) -> Coin { + self.mem_price.clone() + } + + pub fn step_price(&self) -> Coin { + self.step_price.clone() + } + + pub fn new(mem_price: &Coin, step_price: &Coin) -> Self { + Self { + mem_price: mem_price.clone(), + step_price: step_price.clone(), + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ExUnits { + mem: u64, + steps: u64, +} + +to_from_bytes!(ExUnits); + +#[wasm_bindgen] +impl ExUnits { + pub fn mem(&self) -> u64 { + self.mem.clone() + } + + pub fn steps(&self) -> u64 { + self.steps.clone() + } + + pub fn new(mem: u64, steps: u64) -> Self { + Self { + mem: mem, + steps: steps, + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum LanguageKind { + PlutusV1, +} + +#[wasm_bindgen] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Language(LanguageKind); + +to_from_bytes!(Language); + +#[wasm_bindgen] +impl Language { + pub fn new_plutus_v1() -> Self { + Self(LanguageKind::PlutusV1) + } + + pub fn kind(&self) -> LanguageKind { + self.0 + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Languages(Vec); + +#[wasm_bindgen] +impl Languages { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Language { + self.0[index] + } + + pub fn add(&mut self, elem: Language) { + self.0.push(elem); + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusMap(std::collections::BTreeMap); + +to_from_bytes!(PlutusMap); + +#[wasm_bindgen] +impl PlutusMap { + pub fn new() -> Self { + Self(std::collections::BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: &PlutusData, value: &PlutusData) -> Option { + self.0.insert(key.clone(), value.clone()) + } + + pub fn get(&self, key: &PlutusData) -> Option { + self.0.get(key).map(|v| v.clone()) + } + + pub fn keys(&self) -> PlutusList { + PlutusList(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum PlutusDataKind { + ConstrPlutusData, + PlutusMap, + PlutusList, + PreludeInteger, + Bytes, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum PlutusDataEnum { + ConstrPlutusData(ConstrPlutusData), + PlutusMap(PlutusMap), + PlutusList(PlutusList), + PreludeInteger(PreludeInteger), + Bytes(Vec), +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusData(PlutusDataEnum); + +const PLUTUS_BYTES_MAX_LEN: usize = 64; + +to_from_bytes!(PlutusDataEnum); + +#[wasm_bindgen] +impl PlutusData { + pub fn new_constr_plutus_data(constr_plutus_data: &ConstrPlutusData) -> Self { + Self(PlutusDataEnum::ConstrPlutusData(constr_plutus_data.clone())) + } + + pub fn new_map(map: &PlutusMap) -> Self { + Self(PlutusDataEnum::PlutusMap(map.clone())) + } + + pub fn new_list(list: &PlutusList) -> Self { + Self(PlutusDataEnum::PlutusList(list.clone())) + } + + pub fn new_integer(integer: &PreludeInteger) -> Self { + Self(PlutusDataEnum::PreludeInteger(integer.clone())) + } + + pub fn new_bytes(bytes: Vec) -> Result { + if bytes.len() > PLUTUS_BYTES_MAX_LEN { + Err(JsError::from_str(&format!("Max Plutus bytes too long: {}, max = {}", bytes.len(), PLUTUS_BYTES_MAX_LEN))) + } else { + Ok(Self(PlutusDataEnum::Bytes(bytes))) + } + } + + pub fn kind(&self) -> PlutusDataKind { + match &self.0 { + PlutusDataEnum::ConstrPlutusData(_) => PlutusDataKind::ConstrPlutusData, + PlutusDataEnum::PlutusMap(_) => PlutusDataKind::PlutusMap, + PlutusDataEnum::PlutusList(_) => PlutusDataKind::PlutusList, + PlutusDataEnum::PreludeInteger(_) => PlutusDataKind::PreludeInteger, + PlutusDataEnum::Bytes(_) => PlutusDataKind::Bytes, + } + } + + pub fn as_constr_plutus_data(&self) -> Option { + match &self.0 { + PlutusDataEnum::ConstrPlutusData(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_map(&self) -> Option { + match &self.0 { + PlutusDataEnum::PlutusMap(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_list(&self) -> Option { + match &self.0 { + PlutusDataEnum::PlutusList(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_integer(&self) -> Option { + match &self.0 { + PlutusDataEnum::PreludeInteger(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_bytes(&self) -> Option> { + match &self.0 { + PlutusDataEnum::Bytes(x) => Some(x.clone()), + _ => None, + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusList(Vec); + +#[wasm_bindgen] +impl PlutusList { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> PlutusData { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &PlutusData) { + self.0.push(elem.clone()); + } +} + +// TODO: replace these prelude ints with a generalized Integer class +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum PreludeBigintKind { + PreludeBiguint, + PreludeBignint, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum PreludeBigintEnum { + PreludeBiguint(PreludeBiguint), + PreludeBignint(PreludeBignint), +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PreludeBigint(PreludeBigintEnum); + +to_from_bytes!(PreludeBigintEnum); + +#[wasm_bindgen] +impl PreludeBigint { + pub fn new_prelude_biguint(prelude_biguint: &PreludeBiguint) -> Self { + Self(PreludeBigintEnum::PreludeBiguint(prelude_biguint.clone())) + } + + pub fn new_prelude_bignint(prelude_bignint: &PreludeBignint) -> Self { + Self(PreludeBigintEnum::PreludeBignint(prelude_bignint.clone())) + } + + pub fn kind(&self) -> PreludeBigintKind { + match &self.0 { + PreludeBigintEnum::PreludeBiguint(_) => PreludeBigintKind::PreludeBiguint, + PreludeBigintEnum::PreludeBignint(_) => PreludeBigintKind::PreludeBignint, + } + } + + pub fn as_prelude_biguint(&self) -> Option { + match &self.0 { + PreludeBigintEnum::PreludeBiguint(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_prelude_bignint(&self) -> Option { + match &self.0 { + PreludeBigintEnum::PreludeBignint(x) => Some(x.clone()), + _ => None, + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PreludeBignint(Vec); + +#[wasm_bindgen] +impl PreludeBignint { + pub fn new(data: Vec) -> Self { + Self(data) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PreludeBiguint(Vec); + +#[wasm_bindgen] +impl PreludeBiguint { + pub fn new(data: Vec) -> Self { + Self(data) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum PreludeIntegerKind { + Int, + PreludeBigint, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum PreludeIntegerEnum { + Int(Int), + PreludeBigint(PreludeBigint), +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PreludeInteger(PreludeIntegerEnum); + +to_from_bytes!(PreludeIntegerEnum); + +#[wasm_bindgen] +impl PreludeInteger { + pub fn new_int(int: &Int) -> Self { + Self(PreludeIntegerEnum::Int(int.clone())) + } + + pub fn new_prelude_bigint(prelude_bigint: &PreludeBigint) -> Self { + Self(PreludeIntegerEnum::PreludeBigint(prelude_bigint.clone())) + } + + pub fn kind(&self) -> PreludeIntegerKind { + match &self.0 { + PreludeIntegerEnum::Int(_) => PreludeIntegerKind::Int, + PreludeIntegerEnum::PreludeBigint(_) => PreludeIntegerKind::PreludeBigint, + } + } + + pub fn as_int(&self) -> Option { + match &self.0 { + PreludeIntegerEnum::Int(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_prelude_bigint(&self) -> Option { + match &self.0 { + PreludeIntegerEnum::PreludeBigint(x) => Some(x.clone()), + _ => None, + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Redeemer { + tag: RedeemerTag, + index: u64, + data: PlutusData, + ex_units: ExUnits, +} + +to_from_bytes!(Redeemer); + +#[wasm_bindgen] +impl Redeemer { + pub fn tag(&self) -> RedeemerTag { + self.tag.clone() + } + + pub fn index(&self) -> u64 { + self.index.clone() + } + + pub fn data(&self) -> PlutusData { + self.data.clone() + } + + pub fn ex_units(&self) -> ExUnits { + self.ex_units.clone() + } + + pub fn new(tag: &RedeemerTag, index: u64, data: &PlutusData, ex_units: &ExUnits) -> Self { + Self { + tag: tag.clone(), + index: index, + data: data.clone(), + ex_units: ex_units.clone(), + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum RedeemerTagKind { + Spend, + Mint, + Cert, + Reward, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum RedeemerTagEnum { + Spend, + Mint, + Cert, + Reward, +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct RedeemerTag(RedeemerTagEnum); + +to_from_bytes!(RedeemerTagEnum); + +#[wasm_bindgen] +impl RedeemerTag { + pub fn new_i0() -> Self { + Self(RedeemerTagEnum::Spend) + } + + pub fn new_i1() -> Self { + Self(RedeemerTagEnum::Mint) + } + + pub fn new_i2() -> Self { + Self(RedeemerTagEnum::Cert) + } + + pub fn new_i3() -> Self { + Self(RedeemerTagEnum::Reward) + } + + pub fn kind(&self) -> RedeemerTagKind { + match &self.0 { + RedeemerTagEnum::Spend => RedeemerTagKind::Spend, + RedeemerTagEnum::Mint => RedeemerTagKind::Mint, + RedeemerTagEnum::Cert => RedeemerTagKind::Cert, + RedeemerTagEnum::Reward => RedeemerTagKind::Reward, + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Strings(Vec); + +#[wasm_bindgen] +impl Strings { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> String { + self.0[index].clone() + } + + pub fn add(&mut self, elem: String) { + self.0.push(elem); + } +} + + + + + + + + + + +// Serialization + +use std::io::{SeekFrom}; + +// TODO: write tests for this hand-coded implementation? +impl cbor_event::se::Serialize for ConstrPlutusData { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + if Self::is_tag_compact(self.tag.0) { + // compact form + serializer.write_tag(self.tag.0 as u64)?; + self.data.serialize(serializer) + } else { + // general form + serializer.write_tag(Self::GENERAL_FORM_TAG)?; + serializer.write_array(cbor_event::Len::Len(2))?; + self.tag.serialize(serializer)?; + self.data.serialize(serializer) + } + } +} + +impl Deserialize for ConstrPlutusData { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let (tag, data) = match raw.tag()? { + // general form + Self::GENERAL_FORM_TAG => { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let tag = Int::deserialize(raw)?; + let data = (|| -> Result<_, DeserializeError> { + Ok(PlutusList::deserialize(raw)?) + })().map_err(|e| e.annotate("datas"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + (tag, data) + }, + // concise form + tag if Self::is_tag_compact(tag.into()) => (Int::new(&to_bignum(tag)), PlutusList::deserialize(raw)?), + invalid_tag => return Err(DeserializeFailure::TagMismatch{ + found: invalid_tag, + expected: Self::GENERAL_FORM_TAG, + }.into()), + }; + Ok(ConstrPlutusData{ + tag, + data, + }) + })().map_err(|e| e.annotate("ConstrPlutusData")) + } +} + +impl cbor_event::se::Serialize for CostModel { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + serializer.write_text(&key)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for CostModel { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { cbor_event::Len::Len(n) => table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let key = String::deserialize(raw)?; + let value = PreludeInteger::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(key)).into()); + } + } + Ok(()) + })().map_err(|e| e.annotate("CostModel"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for Costmdls { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Costmdls { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { cbor_event::Len::Len(n) => table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let key = Language::deserialize(raw)?; + let value = CostModel::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + } + } + Ok(()) + })().map_err(|e| e.annotate("Costmdls"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for ExUnitPrices { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.mem_price.serialize(serializer)?; + self.step_price.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ExUnitPrices { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let mem_price = (|| -> Result<_, DeserializeError> { + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("mem_price"))?; + let step_price = (|| -> Result<_, DeserializeError> { + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("step_price"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(ExUnitPrices { + mem_price, + step_price, + }) + })().map_err(|e| e.annotate("ExUnitPrices")) + } +} + +impl cbor_event::se::Serialize for ExUnits { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.mem.serialize(serializer)?; + self.steps.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ExUnits { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let mem = (|| -> Result<_, DeserializeError> { + Ok(u64::deserialize(raw)?) + })().map_err(|e| e.annotate("mem"))?; + let steps = (|| -> Result<_, DeserializeError> { + Ok(u64::deserialize(raw)?) + })().map_err(|e| e.annotate("steps"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(ExUnits { + mem, + steps, + }) + })().map_err(|e| e.annotate("ExUnits")) + } +} + +impl cbor_event::se::Serialize for Language { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self.0 { + LanguageKind::PlutusV1 => { + serializer.write_unsigned_integer(0u64) + }, + } + } +} + +impl Deserialize for Language { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.unsigned_integer()? { + 0 => Ok(Language::new_plutus_v1()), + _ => Err(DeserializeError::new("Language", DeserializeFailure::NoVariantMatched.into())), + } + })().map_err(|e| e.annotate("Language")) + } +} + +impl cbor_event::se::Serialize for Languages { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Languages { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(Language::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("Languages"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for PlutusMap { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for PlutusMap { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { cbor_event::Len::Len(n) => table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let key = PlutusData::deserialize(raw)?; + let value = PlutusData::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + } + } + Ok(()) + })().map_err(|e| e.annotate("PlutusMap"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for PlutusDataEnum { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self { + PlutusDataEnum::ConstrPlutusData(x) => { + x.serialize(serializer) + }, + PlutusDataEnum::PlutusMap(x) => { + x.serialize(serializer) + }, + PlutusDataEnum::PlutusList(x) => { + x.serialize(serializer) + }, + PlutusDataEnum::PreludeInteger(x) => { + x.serialize(serializer) + }, + PlutusDataEnum::Bytes(x) => { + serializer.write_bytes(&x) + }, + } + } +} + +impl Deserialize for PlutusDataEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(ConstrPlutusData::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::ConstrPlutusData(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PlutusMap::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::PlutusMap(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PlutusList::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::PlutusList(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PreludeInteger::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::PreludeInteger(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(raw.bytes()?) + })(raw) + { + Ok(variant) => if variant.len() <= PLUTUS_BYTES_MAX_LEN { + return Ok(PlutusDataEnum::Bytes(variant)); + } else { + return Err(DeserializeFailure::OutOfRange{ + min: 0, + max: PLUTUS_BYTES_MAX_LEN, + found: variant.len(), + }.into()); + } + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + Err(DeserializeError::new("PlutusDataEnum", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("PlutusDataEnum")) + } +} + +impl cbor_event::se::Serialize for PlutusData { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for PlutusData { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(PlutusDataEnum::deserialize(raw)?)) + } +} + +impl cbor_event::se::Serialize for PlutusList { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for PlutusList { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(PlutusData::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("PlutusList"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for PreludeBigintEnum { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self { + PreludeBigintEnum::PreludeBiguint(x) => { + x.serialize(serializer) + }, + PreludeBigintEnum::PreludeBignint(x) => { + x.serialize(serializer) + }, + } + } +} + +impl Deserialize for PreludeBigintEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PreludeBiguint::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PreludeBigintEnum::PreludeBiguint(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PreludeBignint::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PreludeBigintEnum::PreludeBignint(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + Err(DeserializeError::new("PreludeBigintEnum", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("PreludeBigintEnum")) + } +} + +impl cbor_event::se::Serialize for PreludeBigint { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for PreludeBigint { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(PreludeBigintEnum::deserialize(raw)?)) + } +} + +impl cbor_event::se::Serialize for PreludeBignint { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_tag(3u64)?; + serializer.write_bytes(&self.0) + } +} + +impl Deserialize for PreludeBignint { + fn deserialize(raw: &mut Deserializer) -> Result { + let tag = raw.tag().map_err(|e| DeserializeError::from(e).annotate("PreludeBignint"))?; + if tag != 3 { + return Err(DeserializeError::new("PreludeBignint", DeserializeFailure::TagMismatch{ found: tag, expected: 3 })); + } + Ok(Self(raw.bytes()?)) + } +} + +impl cbor_event::se::Serialize for PreludeBiguint { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_tag(2u64)?; + serializer.write_bytes(&self.0) + } +} + +impl Deserialize for PreludeBiguint { + fn deserialize(raw: &mut Deserializer) -> Result { + let tag = raw.tag().map_err(|e| DeserializeError::from(e).annotate("PreludeBiguint"))?; + if tag != 2 { + return Err(DeserializeError::new("PreludeBiguint", DeserializeFailure::TagMismatch{ found: tag, expected: 2 })); + } + Ok(Self(raw.bytes()?)) + } +} + +impl cbor_event::se::Serialize for PreludeIntegerEnum { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self { + PreludeIntegerEnum::Int(x) => { + x.serialize(serializer) + }, + PreludeIntegerEnum::PreludeBigint(x) => { + x.serialize(serializer) + }, + } + } +} + +impl Deserialize for PreludeIntegerEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(Int::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PreludeIntegerEnum::Int(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PreludeBigint::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PreludeIntegerEnum::PreludeBigint(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + Err(DeserializeError::new("PreludeIntegerEnum", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("PreludeIntegerEnum")) + } +} + +impl cbor_event::se::Serialize for PreludeInteger { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for PreludeInteger { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(PreludeIntegerEnum::deserialize(raw)?)) + } +} + +impl cbor_event::se::Serialize for Redeemer { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + self.tag.serialize(serializer)?; + self.index.serialize(serializer)?; + self.data.serialize(serializer)?; + self.ex_units.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for Redeemer { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(4)?; + let tag = (|| -> Result<_, DeserializeError> { + Ok(RedeemerTag::deserialize(raw)?) + })().map_err(|e| e.annotate("tag"))?; + let index = (|| -> Result<_, DeserializeError> { + Ok(u64::deserialize(raw)?) + })().map_err(|e| e.annotate("index"))?; + let data = (|| -> Result<_, DeserializeError> { + Ok(PlutusData::deserialize(raw)?) + })().map_err(|e| e.annotate("data"))?; + let ex_units = (|| -> Result<_, DeserializeError> { + Ok(ExUnits::deserialize(raw)?) + })().map_err(|e| e.annotate("ex_units"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(Redeemer { + tag, + index, + data, + ex_units, + }) + })().map_err(|e| e.annotate("Redeemer")) + } +} + +impl cbor_event::se::Serialize for RedeemerTagEnum { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self { + RedeemerTagEnum::Spend => { + serializer.write_unsigned_integer(0u64) + }, + RedeemerTagEnum::Mint => { + serializer.write_unsigned_integer(1u64) + }, + RedeemerTagEnum::Cert => { + serializer.write_unsigned_integer(2u64) + }, + RedeemerTagEnum::Reward => { + serializer.write_unsigned_integer(3u64) + }, + } + } +} + +impl Deserialize for RedeemerTagEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + let i0_value = raw.unsigned_integer()?; + if i0_value != 0 { + return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i0_value), expected: Key::Uint(0) }.into()); + } + Ok(()) + })(raw) + { + Ok(()) => return Ok(RedeemerTagEnum::Spend), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + let i1_value = raw.unsigned_integer()?; + if i1_value != 1 { + return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i1_value), expected: Key::Uint(1) }.into()); + } + Ok(()) + })(raw) + { + Ok(()) => return Ok(RedeemerTagEnum::Mint), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + let i2_value = raw.unsigned_integer()?; + if i2_value != 2 { + return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i2_value), expected: Key::Uint(2) }.into()); + } + Ok(()) + })(raw) + { + Ok(()) => return Ok(RedeemerTagEnum::Cert), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + let i3_value = raw.unsigned_integer()?; + if i3_value != 3 { + return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i3_value), expected: Key::Uint(3) }.into()); + } + Ok(()) + })(raw) + { + Ok(()) => return Ok(RedeemerTagEnum::Reward), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + Err(DeserializeError::new("RedeemerTagEnum", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("RedeemerTagEnum")) + } +} + +impl cbor_event::se::Serialize for RedeemerTag { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for RedeemerTag { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(RedeemerTagEnum::deserialize(raw)?)) + } +} + +impl cbor_event::se::Serialize for Strings { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + serializer.write_text(&element)?; + } + Ok(serializer) + } +} + +impl Deserialize for Strings { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(String::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("Strings"))?; + Ok(Self(arr)) + } +} \ No newline at end of file From a5a5ea87fbc449de0b7e561c292b2104a1e2c406 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Thu, 3 Jun 2021 22:03:15 -0500 Subject: [PATCH 02/11] Change existing structs for Alonzo Only the core data model was updated. Auxiliary functionality like tx_builder was only changed to compile, so it might need changing to take advantage of new functionality. Test vectors for fees and such were not changed and are possibly wrong if the underlying fee model has changed from Shelley-Mary to Alonzo. --- rust/src/crypto.rs | 56 ++- rust/src/lib.rs | 287 +++++++++++--- rust/src/metadata.rs | 3 + rust/src/plutus.rs | 29 +- rust/src/serialization.rs | 760 +++++++++++++++++++++++++------------- rust/src/tx_builder.rs | 22 +- rust/src/utils.rs | 4 +- 7 files changed, 846 insertions(+), 315 deletions(-) diff --git a/rust/src/crypto.rs b/rust/src/crypto.rs index bb8d1c2e..5f8cdff4 100644 --- a/rust/src/crypto.rs +++ b/rust/src/crypto.rs @@ -338,6 +338,57 @@ impl Deserialize for Vkey { } } +#[wasm_bindgen] +#[derive(Clone)] +pub struct Vkeys(Vec); + +#[wasm_bindgen] +impl Vkeys { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Vkey { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &Vkey) { + self.0.push(elem.clone()); + } +} + +impl cbor_event::se::Serialize for Vkeys { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Vkeys { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(Vkey::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("Vkeys"))?; + Ok(Self(arr)) + } +} + #[wasm_bindgen] #[derive(Clone)] pub struct Vkeywitness { @@ -769,9 +820,12 @@ impl_hash_type!(ScriptHash, 28); impl_hash_type!(TransactionHash, 32); impl_hash_type!(GenesisDelegateHash, 28); impl_hash_type!(GenesisHash, 28); -impl_hash_type!(MetadataHash, 32); +impl_hash_type!(AuxiliaryDataHash, 32); +impl_hash_type!(PoolMetadataHash, 32); impl_hash_type!(VRFKeyHash, 32); impl_hash_type!(BlockHash, 32); +impl_hash_type!(DataHash, 32); +impl_hash_type!(ScriptDataHash, 32); // We might want to make these two vkeys normal classes later but for now it's just arbitrary bytes for us (used in block parsing) impl_hash_type!(VRFVKey, 32); impl_hash_type!(KESVKey, 32); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 76cdf66b..08a97a3d 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -57,6 +57,8 @@ use plutus::*; use metadata::*; use utils::*; +type DeltaCoin = Int; + #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct UnitInterval { @@ -93,7 +95,7 @@ type Slot = u32; pub struct Transaction { body: TransactionBody, witness_set: TransactionWitnessSet, - metadata: Option, + auxiliary_data: Option, } to_from_bytes!(Transaction); @@ -108,19 +110,19 @@ impl Transaction { self.witness_set.clone() } - pub fn metadata(&self) -> Option { - self.metadata.clone() + pub fn auxiliary_data(&self) -> Option { + self.auxiliary_data.clone() } pub fn new( body: &TransactionBody, witness_set: &TransactionWitnessSet, - metadata: Option, + auxiliary_data: Option, ) -> Self { Self { body: body.clone(), witness_set: witness_set.clone(), - metadata: metadata.clone(), + auxiliary_data: auxiliary_data.clone(), } } } @@ -205,6 +207,8 @@ impl Certificates { } } +pub type RequiredSigners = Vkeys; + #[wasm_bindgen] #[derive(Clone)] pub struct TransactionBody { @@ -215,9 +219,13 @@ pub struct TransactionBody { certs: Option, withdrawals: Option, update: Option, - metadata_hash: Option, + auxiliary_data_hash: Option, validity_start_interval: Option, mint: Option, + script_data_hash: Option, + collateral: Option, + required_signers: Option, + network_id: Option, } to_from_bytes!(TransactionBody); @@ -263,12 +271,13 @@ impl TransactionBody { pub fn update(&self) -> Option { self.update.clone() } - pub fn set_metadata_hash(&mut self, metadata_hash: &MetadataHash) { - self.metadata_hash = Some(metadata_hash.clone()) + + pub fn set_metadata_hash(&mut self, auxiliary_data_hash: &AuxiliaryDataHash) { + self.auxiliary_data_hash = Some(auxiliary_data_hash.clone()) } - pub fn metadata_hash(&self) -> Option { - self.metadata_hash.clone() + pub fn auxiliary_data_hash(&self) -> Option { + self.auxiliary_data_hash.clone() } pub fn set_validity_start_interval(&mut self, validity_start_interval: Slot) { @@ -276,7 +285,7 @@ impl TransactionBody { } pub fn validity_start_interval(&self) -> Option { - self.validity_start_interval + self.validity_start_interval.clone() } pub fn set_mint(&mut self, mint: &Mint) { @@ -287,12 +296,43 @@ impl TransactionBody { self.mint.clone() } + pub fn set_script_data_hash(&mut self, script_data_hash: &ScriptDataHash) { + self.script_data_hash = Some(script_data_hash.clone()) + } + + pub fn script_data_hash(&self) -> Option { + self.script_data_hash.clone() + } + + pub fn set_collateral(&mut self, collateral: &TransactionInputs) { + self.collateral = Some(collateral.clone()) + } + + pub fn collateral(&self) -> Option { + self.collateral.clone() + } + + pub fn set_required_signers(&mut self, required_signers: &RequiredSigners) { + self.required_signers = Some(required_signers.clone()) + } + + pub fn required_signers(&self) -> Option { + self.required_signers.clone() + } + + pub fn set_network_id(&mut self, network_id: &NetworkId) { + self.network_id = Some(network_id.clone()) + } + + pub fn network_id(&self) -> Option { + self.network_id.clone() + } + pub fn new( inputs: &TransactionInputs, outputs: &TransactionOutputs, fee: &Coin, - ttl: Option, - ) -> Self { + ttl: Option) -> Self { Self { inputs: inputs.clone(), outputs: outputs.clone(), @@ -301,9 +341,13 @@ impl TransactionBody { certs: None, withdrawals: None, update: None, - metadata_hash: None, + auxiliary_data_hash: None, validity_start_interval: None, mint: None, + script_data_hash: None, + collateral: None, + required_signers: None, + network_id: None, } } } @@ -340,6 +384,7 @@ impl TransactionInput { pub struct TransactionOutput { address: Address, amount: Value, + data_hash: Option, } to_from_bytes!(TransactionOutput); @@ -354,10 +399,19 @@ impl TransactionOutput { self.amount.clone() } + pub fn data_hash(&self) -> Option { + self.data_hash.clone() + } + + pub fn set_data_hash(&mut self, data_hash: &DataHash) { + self.data_hash = Some(data_hash.clone()); + } + pub fn new(address: &Address, amount: &Value) -> Self { Self { address: address.clone(), amount: amount.clone(), + data_hash: None, } } } @@ -806,11 +860,12 @@ pub enum MIRPot { Treasury, } +// TODO: rewrite to handle the simple coin case (split this into 2 types?) #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct MoveInstantaneousReward { pot: MIRPot, - rewards: linked_hash_map::LinkedHashMap, + rewards: linked_hash_map::LinkedHashMap, } to_from_bytes!(MoveInstantaneousReward); @@ -828,12 +883,12 @@ impl MoveInstantaneousReward { self.rewards.len() } - pub fn insert(&mut self, key: &StakeCredential, value: &Coin) -> Option { - self.rewards.insert(key.clone(), value.clone()) + pub fn insert(&mut self, cred: &StakeCredential, delta: &DeltaCoin) -> Option { + self.rewards.insert(cred.clone(), delta.clone()) } - pub fn get(&self, key: &StakeCredential) -> Option { - self.rewards.get(key).map(|v| v.clone()) + pub fn get(&self, cred: &StakeCredential) -> Option { + self.rewards.get(cred).map(|v| v.clone()) } pub fn keys(&self) -> StakeCredentials { @@ -1143,7 +1198,7 @@ impl Relay { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct PoolMetadata { url: URL, - metadata_hash: MetadataHash, + pool_metadata_hash: PoolMetadataHash, } to_from_bytes!(PoolMetadata); @@ -1154,14 +1209,14 @@ impl PoolMetadata { self.url.clone() } - pub fn metadata_hash(&self) -> MetadataHash { - self.metadata_hash.clone() + pub fn pool_metadata_hash(&self) -> PoolMetadataHash { + self.pool_metadata_hash.clone() } - pub fn new(url: &URL, metadata_hash: &MetadataHash) -> Self { + pub fn new(url: &URL, pool_metadata_hash: &PoolMetadataHash) -> Self { Self { url: url.clone(), - metadata_hash: metadata_hash.clone(), + pool_metadata_hash: pool_metadata_hash.clone(), } } } @@ -1462,6 +1517,7 @@ to_from_bytes!(NativeScript); #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum ScriptHashNamespace { NativeScript, + // TODO: do we need to update this for Plutus? } #[wasm_bindgen] @@ -1743,6 +1799,7 @@ impl ProtocolVersions { } } + #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct ProtocolParamUpdate { @@ -1763,7 +1820,13 @@ pub struct ProtocolParamUpdate { d: Option, extra_entropy: Option, protocol_version: Option, - min_utxo_value: Option, + min_pool_cost: Option, + ada_per_utxo_byte: Option, + cost_models: Option, + execution_costs: Option, + max_tx_ex_units: Option, + max_block_ex_units: Option, + max_value_size: Option, } to_from_bytes!(ProtocolParamUpdate); @@ -1890,12 +1953,60 @@ impl ProtocolParamUpdate { self.protocol_version.clone() } - pub fn set_min_utxo_value(&mut self, min_utxo_value: &Coin) { - self.min_utxo_value = Some(min_utxo_value.clone()) + pub fn set_min_pool_cost(&mut self, min_pool_cost: &Coin) { + self.min_pool_cost = Some(min_pool_cost.clone()) + } + + pub fn min_pool_cost(&self) -> Option { + self.min_pool_cost.clone() + } + + pub fn set_ada_per_utxo_byte(&mut self, ada_per_utxo_byte: &Coin) { + self.ada_per_utxo_byte = Some(ada_per_utxo_byte.clone()) + } + + pub fn ada_per_utxo_byte(&self) -> Option { + self.ada_per_utxo_byte.clone() + } + + pub fn set_cost_models(&mut self, cost_models: &Costmdls) { + self.cost_models = Some(cost_models.clone()) + } + + pub fn cost_models(&self) -> Option { + self.cost_models.clone() + } + + pub fn set_execution_costs(&mut self, execution_costs: &ExUnitPrices) { + self.execution_costs = Some(execution_costs.clone()) + } + + pub fn execution_costs(&self) -> Option { + self.execution_costs.clone() + } + + pub fn set_max_tx_ex_units(&mut self, max_tx_ex_units: &ExUnits) { + self.max_tx_ex_units = Some(max_tx_ex_units.clone()) + } + + pub fn max_tx_ex_units(&self) -> Option { + self.max_tx_ex_units.clone() + } + + pub fn set_max_block_ex_units(&mut self, max_block_ex_units: &ExUnits) { + self.max_block_ex_units = Some(max_block_ex_units.clone()) + } + + pub fn max_block_ex_units(&self) -> Option { + self.max_block_ex_units.clone() + } + + pub fn set_max_value_size(&mut self, max_value_size: &PreludeUnsigned) { + self.max_value_size = Some(max_value_size.clone()) } - pub fn min_utxo_value(&self) -> Option { - self.min_utxo_value.clone() + pub fn max_value_size(&self) -> Option { + self.max_value_size.clone() } pub fn new() -> Self { @@ -1915,7 +2026,13 @@ impl ProtocolParamUpdate { d: None, extra_entropy: None, protocol_version: None, - min_utxo_value: None, + min_pool_cost: None, + ada_per_utxo_byte: None, + cost_models: None, + execution_costs: None, + max_tx_ex_units: None, + max_block_ex_units: None, + max_value_size: None, } } } @@ -1973,10 +2090,10 @@ pub type TransactionIndexes = Vec; #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct MapTransactionIndexToTransactionMetadata(linked_hash_map::LinkedHashMap); +pub struct AuxiliaryDataSet(linked_hash_map::LinkedHashMap); #[wasm_bindgen] -impl MapTransactionIndexToTransactionMetadata { +impl AuxiliaryDataSet { pub fn new() -> Self { Self(linked_hash_map::LinkedHashMap::new()) } @@ -1985,15 +2102,15 @@ impl MapTransactionIndexToTransactionMetadata { self.0.len() } - pub fn insert(&mut self, key: TransactionIndex, value: &TransactionMetadata) -> Option { - self.0.insert(key, value.clone()) + pub fn insert(&mut self, tx_index: TransactionIndex, data: &AuxiliaryData) -> Option { + self.0.insert(tx_index, data.clone()) } - pub fn get(&self, key: TransactionIndex) -> Option { - self.0.get(&key).map(|v| v.clone()) + pub fn get(&self, tx_index: TransactionIndex) -> Option { + self.0.get(&tx_index).map(|v| v.clone()) } - pub fn keys(&self) -> TransactionIndexes { + pub fn indices(&self) -> TransactionIndexes { self.0.iter().map(|(k, _v)| k.clone()).collect::>() } } @@ -2004,7 +2121,8 @@ pub struct Block { header: Header, transaction_bodies: TransactionBodies, transaction_witness_sets: TransactionWitnessSets, - transaction_metadata_set: MapTransactionIndexToTransactionMetadata, + auxiliary_data_set: AuxiliaryDataSet, + invalid_transactions: TransactionIndexes, } to_from_bytes!(Block); @@ -2023,16 +2141,21 @@ impl Block { self.transaction_witness_sets.clone() } - pub fn transaction_metadata_set(&self) -> MapTransactionIndexToTransactionMetadata { - self.transaction_metadata_set.clone() + pub fn auxiliary_data_set(&self) -> AuxiliaryDataSet { + self.auxiliary_data_set.clone() } - pub fn new(header: &Header, transaction_bodies: &TransactionBodies, transaction_witness_sets: &TransactionWitnessSets, transaction_metadata_set: &MapTransactionIndexToTransactionMetadata) -> Self { + pub fn invalid_transactions(&self) -> TransactionIndexes { + self.invalid_transactions.clone() + } + + pub fn new(header: &Header, transaction_bodies: &TransactionBodies, transaction_witness_sets: &TransactionWitnessSets, auxiliary_data_set: &AuxiliaryDataSet, invalid_transactions: &TransactionIndexes) -> Self { Self { header: header.clone(), transaction_bodies: transaction_bodies.clone(), transaction_witness_sets: transaction_witness_sets.clone(), - transaction_metadata_set: transaction_metadata_set.clone(), + auxiliary_data_set: auxiliary_data_set.clone(), + invalid_transactions: invalid_transactions.clone(), } } } @@ -2434,6 +2557,86 @@ impl Mint { } } + +#[wasm_bindgen] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum NetworkIdKind { + Testnet, + Mainnet, +} + +#[wasm_bindgen] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct NetworkId(NetworkIdKind); + +to_from_bytes!(NetworkId); + +#[wasm_bindgen] +impl NetworkId { + pub fn testnet() -> Self { + Self(NetworkIdKind::Testnet) + } + + pub fn mainnet() -> Self { + Self(NetworkIdKind::Mainnet) + } + + pub fn kind(&self) -> NetworkIdKind { + self.0 + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum PreludeUnsignedKind { + U64, + PreludeBiguint, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum PreludeUnsignedEnum { + U64(BigNum), + PreludeBiguint(PreludeBiguint), +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PreludeUnsigned(PreludeUnsignedEnum); + +to_from_bytes!(PreludeUnsignedEnum); + +#[wasm_bindgen] +impl PreludeUnsigned { + pub fn new_u64(uint: BigNum) -> Self { + Self(PreludeUnsignedEnum::U64(uint)) + } + + pub fn new_prelude_biguint(prelude_biguint: &PreludeBiguint) -> Self { + Self(PreludeUnsignedEnum::PreludeBiguint(prelude_biguint.clone())) + } + + pub fn kind(&self) -> PreludeUnsignedKind { + match &self.0 { + PreludeUnsignedEnum::U64(_) => PreludeUnsignedKind::U64, + PreludeUnsignedEnum::PreludeBiguint(_) => PreludeUnsignedKind::PreludeBiguint, + } + } + + pub fn as_u64(&self) -> Option { + match &self.0 { + PreludeUnsignedEnum::U64(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_prelude_biguint(&self) -> Option { + match &self.0 { + PreludeUnsignedEnum::PreludeBiguint(x) => Some(x.clone()), + _ => None, + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index 085d63a0..4d49dc35 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -284,6 +284,9 @@ impl GeneralTransactionMetadata { } } +// TODO: hand-write this as AuxiliaryData +pub type AuxiliaryData = TransactionMetadata; + #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct TransactionMetadata { diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs index 98455ea9..b36919c5 100644 --- a/rust/src/plutus.rs +++ b/rust/src/plutus.rs @@ -133,26 +133,27 @@ impl ExUnitPrices { #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct ExUnits { - mem: u64, - steps: u64, + // TODO: should these be u32 or BigNum? + mem: BigNum, + steps: BigNum, } to_from_bytes!(ExUnits); #[wasm_bindgen] impl ExUnits { - pub fn mem(&self) -> u64 { + pub fn mem(&self) -> BigNum { self.mem.clone() } - pub fn steps(&self) -> u64 { + pub fn steps(&self) -> BigNum { self.steps.clone() } - pub fn new(mem: u64, steps: u64) -> Self { + pub fn new(mem: &BigNum, steps: &BigNum) -> Self { Self { - mem: mem, - steps: steps, + mem: mem.clone(), + steps: steps.clone(), } } } @@ -483,7 +484,7 @@ impl PreludeInteger { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct Redeemer { tag: RedeemerTag, - index: u64, + index: BigNum, data: PlutusData, ex_units: ExUnits, } @@ -496,7 +497,7 @@ impl Redeemer { self.tag.clone() } - pub fn index(&self) -> u64 { + pub fn index(&self) -> BigNum { self.index.clone() } @@ -508,10 +509,10 @@ impl Redeemer { self.ex_units.clone() } - pub fn new(tag: &RedeemerTag, index: u64, data: &PlutusData, ex_units: &ExUnits) -> Self { + pub fn new(tag: &RedeemerTag, index: &BigNum, data: &PlutusData, ex_units: &ExUnits) -> Self { Self { tag: tag.clone(), - index: index, + index: index.clone(), data: data.clone(), ex_units: ex_units.clone(), } @@ -777,10 +778,10 @@ impl Deserialize for ExUnits { let mut read_len = CBORReadLen::new(len); read_len.read_elems(2)?; let mem = (|| -> Result<_, DeserializeError> { - Ok(u64::deserialize(raw)?) + Ok(BigNum::deserialize(raw)?) })().map_err(|e| e.annotate("mem"))?; let steps = (|| -> Result<_, DeserializeError> { - Ok(u64::deserialize(raw)?) + Ok(BigNum::deserialize(raw)?) })().map_err(|e| e.annotate("steps"))?; match len { cbor_event::Len::Len(_) => (), @@ -1144,7 +1145,7 @@ impl Deserialize for Redeemer { Ok(RedeemerTag::deserialize(raw)?) })().map_err(|e| e.annotate("tag"))?; let index = (|| -> Result<_, DeserializeError> { - Ok(u64::deserialize(raw)?) + Ok(BigNum::deserialize(raw)?) })().map_err(|e| e.annotate("index"))?; let data = (|| -> Result<_, DeserializeError> { Ok(PlutusData::deserialize(raw)?) diff --git a/rust/src/serialization.rs b/rust/src/serialization.rs index 7d8d0993..05128e2a 100644 --- a/rust/src/serialization.rs +++ b/rust/src/serialization.rs @@ -59,7 +59,7 @@ impl cbor_event::se::Serialize for Transaction { serializer.write_array(cbor_event::Len::Len(3))?; self.body.serialize(serializer)?; self.witness_set.serialize(serializer)?; - match &self.metadata { + match &self.auxiliary_data { Some(x) => { x.serialize(serializer) }, @@ -94,7 +94,7 @@ impl DeserializeEmbeddedGroup for Transaction { let witness_set = (|| -> Result<_, DeserializeError> { Ok(TransactionWitnessSet::deserialize(raw)?) })().map_err(|e| e.annotate("witness_set"))?; - let metadata = (|| -> Result<_, DeserializeError> { + let auxiliary_data = (|| -> Result<_, DeserializeError> { Ok(match raw.cbor_type()? != CBORType::Special { true => { Some(TransactionMetadata::deserialize(raw)?) @@ -106,11 +106,11 @@ impl DeserializeEmbeddedGroup for Transaction { None } }) - })().map_err(|e| e.annotate("metadata"))?; + })().map_err(|e| e.annotate("auxiliary_data"))?; Ok(Transaction { body, witness_set, - metadata, + auxiliary_data, }) } } @@ -201,7 +201,7 @@ impl Deserialize for Certificates { impl cbor_event::se::Serialize for TransactionBody { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(3 + match &self.ttl { Some(x) => 1, None => 0 } + match &self.certs { Some(x) => 1, None => 0 } + match &self.withdrawals { Some(x) => 1, None => 0 } + match &self.update { Some(x) => 1, None => 0 } + match &self.metadata_hash { Some(x) => 1, None => 0 } + match &self.validity_start_interval { Some(x) => 1, None => 0 } + match &self.mint { Some(x) => 1, None => 0 }))?; + serializer.write_map(cbor_event::Len::Len(3 + match &self.ttl { Some(x) => 1, None => 0 } + match &self.certs { Some(x) => 1, None => 0 } + match &self.withdrawals { Some(x) => 1, None => 0 } + match &self.update { Some(x) => 1, None => 0 } + match &self.auxiliary_data_hash { Some(x) => 1, None => 0 } + match &self.validity_start_interval { Some(x) => 1, None => 0 } + match &self.mint { Some(x) => 1, None => 0 } + match &self.script_data_hash { Some(x) => 1, None => 0 } + match &self.collateral { Some(x) => 1, None => 0 } + match &self.required_signers { Some(x) => 1, None => 0 } + match &self.network_id { Some(x) => 1, None => 0 }))?; serializer.write_unsigned_integer(0)?; self.inputs.serialize(serializer)?; serializer.write_unsigned_integer(1)?; @@ -224,7 +224,7 @@ impl cbor_event::se::Serialize for TransactionBody { serializer.write_unsigned_integer(6)?; field.serialize(serializer)?; } - if let Some(field) = &self.metadata_hash { + if let Some(field) = &self.auxiliary_data_hash { serializer.write_unsigned_integer(7)?; field.serialize(serializer)?; } @@ -236,6 +236,22 @@ impl cbor_event::se::Serialize for TransactionBody { serializer.write_unsigned_integer(9)?; field.serialize(serializer)?; } + if let Some(field) = &self.script_data_hash { + serializer.write_unsigned_integer(11)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.collateral { + serializer.write_unsigned_integer(13)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.required_signers { + serializer.write_unsigned_integer(14)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.network_id { + serializer.write_unsigned_integer(15)?; + field.serialize(serializer)?; + } Ok(serializer) } } @@ -244,8 +260,8 @@ impl Deserialize for TransactionBody { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { let len = raw.map()?; - //let mut read_len = CBORReadLen::new(len); - //read_len.read_elems(3)?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(3)?; let mut inputs = None; let mut outputs = None; let mut fee = None; @@ -253,9 +269,13 @@ impl Deserialize for TransactionBody { let mut certs = None; let mut withdrawals = None; let mut update = None; - let mut metadata_hash = None; + let mut auxiliary_data_hash = None; let mut validity_start_interval = None; let mut mint = None; + let mut script_data_hash = None; + let mut collateral = None; + let mut required_signers = None; + let mut network_id = None; let mut read = 0; while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { match raw.cbor_type()? { @@ -289,7 +309,7 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); } ttl = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Slot::deserialize(raw)?) })().map_err(|e| e.annotate("ttl"))?); }, @@ -298,7 +318,7 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); } certs = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Certificates::deserialize(raw)?) })().map_err(|e| e.annotate("certs"))?); }, @@ -307,7 +327,7 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); } withdrawals = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Withdrawals::deserialize(raw)?) })().map_err(|e| e.annotate("withdrawals"))?); }, @@ -316,25 +336,25 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); } update = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Update::deserialize(raw)?) })().map_err(|e| e.annotate("update"))?); }, 7 => { - if metadata_hash.is_some() { + if auxiliary_data_hash.is_some() { return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); } - metadata_hash = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; - Ok(MetadataHash::deserialize(raw)?) - })().map_err(|e| e.annotate("metadata_hash"))?); + auxiliary_data_hash = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(AuxiliaryDataHash::deserialize(raw)?) + })().map_err(|e| e.annotate("auxiliary_data_hash"))?); }, 8 => { if validity_start_interval.is_some() { return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); } validity_start_interval = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Slot::deserialize(raw)?) })().map_err(|e| e.annotate("validity_start_interval"))?); }, @@ -343,10 +363,46 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); } mint = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Mint::deserialize(raw)?) })().map_err(|e| e.annotate("mint"))?); }, + 11 => { + if script_data_hash.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); + } + script_data_hash = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ScriptDataHash::deserialize(raw)?) + })().map_err(|e| e.annotate("script_data_hash"))?); + }, + 13 => { + if collateral.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); + } + collateral = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(TransactionInputs::deserialize(raw)?) + })().map_err(|e| e.annotate("collateral"))?); + }, + 14 => { + if required_signers.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); + } + required_signers = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(RequiredSigners::deserialize(raw)?) + })().map_err(|e| e.annotate("required_signers"))?); + }, + 15 => { + if network_id.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(15)).into()); + } + network_id = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(NetworkId::deserialize(raw)?) + })().map_err(|e| e.annotate("network_id"))?); + }, unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), }, CBORType::Text => match raw.text()?.as_str() { @@ -375,7 +431,7 @@ impl Deserialize for TransactionBody { Some(x) => x, None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(2)).into()), }; - //read_len.finish()?; + read_len.finish()?; Ok(Self { inputs, outputs, @@ -384,9 +440,13 @@ impl Deserialize for TransactionBody { certs, withdrawals, update, - metadata_hash, + auxiliary_data_hash, validity_start_interval, mint, + script_data_hash, + collateral, + required_signers, + network_id, }) })().map_err(|e| e.annotate("TransactionBody")) } @@ -467,9 +527,11 @@ impl DeserializeEmbeddedGroup for TransactionOutput { let amount = (|| -> Result<_, DeserializeError> { Ok(Value::deserialize(raw)?) })().map_err(|e| e.annotate("amount"))?; + // TODO: how are we supposed to do this inside of a plain group?! Ok(TransactionOutput { address, amount, + data_hash: None, }) } } @@ -1157,7 +1219,7 @@ impl Deserialize for MoveInstantaneousReward { break; } let key = StakeCredential::deserialize(raw)?; - let value = Coin::deserialize(raw)?; + let value = DeltaCoin::deserialize(raw)?; if table.insert(key.clone(), value).is_some() { return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); } @@ -1536,7 +1598,7 @@ impl cbor_event::se::Serialize for PoolMetadata { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { serializer.write_array(cbor_event::Len::Len(2))?; self.url.serialize(serializer)?; - self.metadata_hash.serialize(serializer)?; + self.pool_metadata_hash.serialize(serializer)?; Ok(serializer) } } @@ -1563,12 +1625,12 @@ impl DeserializeEmbeddedGroup for PoolMetadata { let url = (|| -> Result<_, DeserializeError> { Ok(URL::deserialize(raw)?) })().map_err(|e| e.annotate("url"))?; - let metadata_hash = (|| -> Result<_, DeserializeError> { - Ok(MetadataHash::deserialize(raw)?) - })().map_err(|e| e.annotate("metadata_hash"))?; + let pool_metadata_hash = (|| -> Result<_, DeserializeError> { + Ok(PoolMetadataHash::deserialize(raw)?) + })().map_err(|e| e.annotate("pool_metadata_hash"))?; Ok(PoolMetadata { url, - metadata_hash, + pool_metadata_hash, }) } } @@ -2354,10 +2416,9 @@ impl Deserialize for ProtocolVersions { Ok(Self(arr)) } } - impl cbor_event::se::Serialize for ProtocolParamUpdate { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(match &self.minfee_a { Some(x) => 1, None => 0 } + match &self.minfee_b { Some(x) => 1, None => 0 } + match &self.max_block_body_size { Some(x) => 1, None => 0 } + match &self.max_tx_size { Some(x) => 1, None => 0 } + match &self.max_block_header_size { Some(x) => 1, None => 0 } + match &self.key_deposit { Some(x) => 1, None => 0 } + match &self.pool_deposit { Some(x) => 1, None => 0 } + match &self.max_epoch { Some(x) => 1, None => 0 } + match &self.n_opt { Some(x) => 1, None => 0 } + match &self.pool_pledge_influence { Some(x) => 1, None => 0 } + match &self.expansion_rate { Some(x) => 1, None => 0 } + match &self.treasury_growth_rate { Some(x) => 1, None => 0 } + match &self.d { Some(x) => 1, None => 0 } + match &self.extra_entropy { Some(x) => 1, None => 0 } + match &self.protocol_version { Some(x) => 1, None => 0 } + match &self.min_utxo_value { Some(x) => 1, None => 0 }))?; + serializer.write_map(cbor_event::Len::Len(match &self.minfee_a { Some(x) => 1, None => 0 } + match &self.minfee_b { Some(x) => 1, None => 0 } + match &self.max_block_body_size { Some(x) => 1, None => 0 } + match &self.max_tx_size { Some(x) => 1, None => 0 } + match &self.max_block_header_size { Some(x) => 1, None => 0 } + match &self.key_deposit { Some(x) => 1, None => 0 } + match &self.pool_deposit { Some(x) => 1, None => 0 } + match &self.max_epoch { Some(x) => 1, None => 0 } + match &self.n_opt { Some(x) => 1, None => 0 } + match &self.pool_pledge_influence { Some(x) => 1, None => 0 } + match &self.expansion_rate { Some(x) => 1, None => 0 } + match &self.treasury_growth_rate { Some(x) => 1, None => 0 } + match &self.d { Some(x) => 1, None => 0 } + match &self.extra_entropy { Some(x) => 1, None => 0 } + match &self.protocol_version { Some(x) => 1, None => 0 } + match &self.min_pool_cost { Some(x) => 1, None => 0 } + match &self.ada_per_utxo_byte { Some(x) => 1, None => 0 } + match &self.cost_models { Some(x) => 1, None => 0 } + match &self.execution_costs { Some(x) => 1, None => 0 } + match &self.max_tx_ex_units { Some(x) => 1, None => 0 } + match &self.max_block_ex_units { Some(x) => 1, None => 0 } + match &self.max_value_size { Some(x) => 1, None => 0 }))?; if let Some(field) = &self.minfee_a { serializer.write_unsigned_integer(0)?; field.serialize(serializer)?; @@ -2418,8 +2479,32 @@ impl cbor_event::se::Serialize for ProtocolParamUpdate { serializer.write_unsigned_integer(14)?; field.serialize(serializer)?; } - if let Some(field) = &self.min_utxo_value { - serializer.write_unsigned_integer(15)?; + if let Some(field) = &self.min_pool_cost { + serializer.write_unsigned_integer(16)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.ada_per_utxo_byte { + serializer.write_unsigned_integer(17)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.cost_models { + serializer.write_unsigned_integer(18)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.execution_costs { + serializer.write_unsigned_integer(19)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_tx_ex_units { + serializer.write_unsigned_integer(20)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_block_ex_units { + serializer.write_unsigned_integer(21)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_value_size { + serializer.write_unsigned_integer(22)?; field.serialize(serializer)?; } Ok(serializer) @@ -2430,195 +2515,273 @@ impl Deserialize for ProtocolParamUpdate { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { let len = raw.map()?; - Self::deserialize_as_embedded_group(raw, len) - })().map_err(|e| e.annotate("ProtocolParamUpdate")) - } -} - -impl DeserializeEmbeddedGroup for ProtocolParamUpdate { - fn deserialize_as_embedded_group(raw: &mut Deserializer, len: cbor_event::Len) -> Result { - let mut minfee_a = None; - let mut minfee_b = None; - let mut max_block_body_size = None; - let mut max_tx_size = None; - let mut max_block_header_size = None; - let mut key_deposit = None; - let mut pool_deposit = None; - let mut max_epoch = None; - let mut n_opt = None; - let mut pool_pledge_influence = None; - let mut expansion_rate = None; - let mut treasury_growth_rate = None; - let mut d = None; - let mut extra_entropy = None; - let mut protocol_version = None; - let mut min_utxo_value = None; - let mut read = 0; - while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { - match raw.cbor_type()? { - CBORType::UnsignedInteger => match raw.unsigned_integer()? { - 0 => { - if minfee_a.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); - } - minfee_a = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("minfee_a"))?); - }, - 1 => { - if minfee_b.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); - } - minfee_b = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("minfee_b"))?); - }, - 2 => { - if max_block_body_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); - } - max_block_body_size = Some((|| -> Result<_, DeserializeError> { - Ok(u32::deserialize(raw)?) - })().map_err(|e| e.annotate("max_block_body_size"))?); - }, - 3 => { - if max_tx_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); - } - max_tx_size = Some((|| -> Result<_, DeserializeError> { - Ok(u32::deserialize(raw)?) - })().map_err(|e| e.annotate("max_tx_size"))?); - }, - 4 => { - if max_block_header_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); - } - max_block_header_size = Some((|| -> Result<_, DeserializeError> { - Ok(u32::deserialize(raw)?) - })().map_err(|e| e.annotate("max_block_header_size"))?); - }, - 5 => { - if key_deposit.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); - } - key_deposit = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("key_deposit"))?); - }, - 6 => { - if pool_deposit.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); - } - pool_deposit = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("pool_deposit"))?); - }, - 7 => { - if max_epoch.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); - } - max_epoch = Some((|| -> Result<_, DeserializeError> { - Ok(Epoch::deserialize(raw)?) - })().map_err(|e| e.annotate("max_epoch"))?); - }, - 8 => { - if n_opt.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); - } - n_opt = Some((|| -> Result<_, DeserializeError> { - Ok(u32::deserialize(raw)?) - })().map_err(|e| e.annotate("n_opt"))?); - }, - 9 => { - if pool_pledge_influence.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); - } - pool_pledge_influence = Some((|| -> Result<_, DeserializeError> { - Ok(Rational::deserialize(raw)?) - })().map_err(|e| e.annotate("pool_pledge_influence"))?); - }, - 10 => { - if expansion_rate.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(10)).into()); - } - expansion_rate = Some((|| -> Result<_, DeserializeError> { - Ok(UnitInterval::deserialize(raw)?) - })().map_err(|e| e.annotate("expansion_rate"))?); - }, - 11 => { - if treasury_growth_rate.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); - } - treasury_growth_rate = Some((|| -> Result<_, DeserializeError> { - Ok(UnitInterval::deserialize(raw)?) - })().map_err(|e| e.annotate("treasury_growth_rate"))?); - }, - 12 => { - if d.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(12)).into()); - } - d = Some((|| -> Result<_, DeserializeError> { - Ok(UnitInterval::deserialize(raw)?) - })().map_err(|e| e.annotate("d"))?); - }, - 13 => { - if extra_entropy.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); - } - extra_entropy = Some((|| -> Result<_, DeserializeError> { - Ok(Nonce::deserialize(raw)?) - })().map_err(|e| e.annotate("extra_entropy"))?); - }, - 14 => { - if protocol_version.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); - } - protocol_version = Some((|| -> Result<_, DeserializeError> { - Ok(ProtocolVersions::deserialize(raw)?) - })().map_err(|e| e.annotate("protocol_version"))?); + let mut read_len = CBORReadLen::new(len); + let mut minfee_a = None; + let mut minfee_b = None; + let mut max_block_body_size = None; + let mut max_tx_size = None; + let mut max_block_header_size = None; + let mut key_deposit = None; + let mut pool_deposit = None; + let mut max_epoch = None; + let mut n_opt = None; + let mut pool_pledge_influence = None; + let mut expansion_rate = None; + let mut treasury_growth_rate = None; + let mut d = None; + let mut extra_entropy = None; + let mut protocol_version = None; + let mut min_pool_cost = None; + let mut ada_per_utxo_byte = None; + let mut cost_models = None; + let mut execution_costs = None; + let mut max_tx_ex_units = None; + let mut max_block_ex_units = None; + let mut max_value_size = None; + let mut read = 0; + while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + 0 => { + if minfee_a.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + minfee_a = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("minfee_a"))?); + }, + 1 => { + if minfee_b.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + minfee_b = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("minfee_b"))?); + }, + 2 => { + if max_block_body_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + max_block_body_size = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })().map_err(|e| e.annotate("max_block_body_size"))?); + }, + 3 => { + if max_tx_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); + } + max_tx_size = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })().map_err(|e| e.annotate("max_tx_size"))?); + }, + 4 => { + if max_block_header_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); + } + max_block_header_size = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })().map_err(|e| e.annotate("max_block_header_size"))?); + }, + 5 => { + if key_deposit.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); + } + key_deposit = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("key_deposit"))?); + }, + 6 => { + if pool_deposit.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); + } + pool_deposit = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("pool_deposit"))?); + }, + 7 => { + if max_epoch.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); + } + max_epoch = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Epoch::deserialize(raw)?) + })().map_err(|e| e.annotate("max_epoch"))?); + }, + 8 => { + if n_opt.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); + } + n_opt = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })().map_err(|e| e.annotate("n_opt"))?); + }, + 9 => { + if pool_pledge_influence.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); + } + pool_pledge_influence = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Rational::deserialize(raw)?) + })().map_err(|e| e.annotate("pool_pledge_influence"))?); + }, + 10 => { + if expansion_rate.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(10)).into()); + } + expansion_rate = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })().map_err(|e| e.annotate("expansion_rate"))?); + }, + 11 => { + if treasury_growth_rate.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); + } + treasury_growth_rate = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })().map_err(|e| e.annotate("treasury_growth_rate"))?); + }, + 12 => { + if d.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(12)).into()); + } + d = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })().map_err(|e| e.annotate("d"))?); + }, + 13 => { + if extra_entropy.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); + } + extra_entropy = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Nonce::deserialize(raw)?) + })().map_err(|e| e.annotate("extra_entropy"))?); + }, + 14 => { + if protocol_version.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); + } + protocol_version = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ProtocolVersions::deserialize(raw)?) + })().map_err(|e| e.annotate("protocol_version"))?); + }, + 16 => { + if min_pool_cost.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(16)).into()); + } + min_pool_cost = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("min_pool_cost"))?); + }, + 17 => { + if ada_per_utxo_byte.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(17)).into()); + } + ada_per_utxo_byte = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("ada_per_utxo_byte"))?); + }, + 18 => { + if cost_models.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(18)).into()); + } + cost_models = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Costmdls::deserialize(raw)?) + })().map_err(|e| e.annotate("cost_models"))?); + }, + 19 => { + if execution_costs.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(19)).into()); + } + execution_costs = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ExUnitPrices::deserialize(raw)?) + })().map_err(|e| e.annotate("execution_costs"))?); + }, + 20 => { + if max_tx_ex_units.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(20)).into()); + } + max_tx_ex_units = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ExUnits::deserialize(raw)?) + })().map_err(|e| e.annotate("max_tx_ex_units"))?); + }, + 21 => { + if max_block_ex_units.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(21)).into()); + } + max_block_ex_units = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ExUnits::deserialize(raw)?) + })().map_err(|e| e.annotate("max_block_ex_units"))?); + }, + 22 => { + if max_value_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(22)).into()); + } + max_value_size = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PreludeUnsigned::deserialize(raw)?) + })().map_err(|e| e.annotate("max_value_size"))?); + }, + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), }, - 15 => { - if min_utxo_value.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(15)).into()); - } - min_utxo_value = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("min_utxo_value"))?); + CBORType::Text => match raw.text()?.as_str() { + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), }, - unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), - }, - CBORType::Text => match raw.text()?.as_str() { - unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), - }, - CBORType::Special => match len { - cbor_event::Len::Len(_) => return Err(DeserializeFailure::BreakInDefiniteLen.into()), - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => break, - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + CBORType::Special => match len { + cbor_event::Len::Len(_) => return Err(DeserializeFailure::BreakInDefiniteLen.into()), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, }, - }, - other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + } + read += 1; } - read += 1; - } - Ok(Self { - minfee_a, - minfee_b, - max_block_body_size, - max_tx_size, - max_block_header_size, - key_deposit, - pool_deposit, - max_epoch, - n_opt, - pool_pledge_influence, - expansion_rate, - treasury_growth_rate, - d, - extra_entropy, - protocol_version, - min_utxo_value, - }) + read_len.finish()?; + Ok(Self { + minfee_a, + minfee_b, + max_block_body_size, + max_tx_size, + max_block_header_size, + key_deposit, + pool_deposit, + max_epoch, + n_opt, + pool_pledge_influence, + expansion_rate, + treasury_growth_rate, + d, + extra_entropy, + protocol_version, + min_pool_cost, + ada_per_utxo_byte, + cost_models, + execution_costs, + max_tx_ex_units, + max_block_ex_units, + max_value_size, + }) + })().map_err(|e| e.annotate("ProtocolParamUpdate")) } } @@ -2678,7 +2841,7 @@ impl Deserialize for TransactionWitnessSets { } } -impl cbor_event::se::Serialize for MapTransactionIndexToTransactionMetadata { +impl cbor_event::se::Serialize for AuxiliaryDataSet { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; for (key, value) in &self.0 { @@ -2689,7 +2852,7 @@ impl cbor_event::se::Serialize for MapTransactionIndexToTransactionMetadata { } } -impl Deserialize for MapTransactionIndexToTransactionMetadata { +impl Deserialize for AuxiliaryDataSet { fn deserialize(raw: &mut Deserializer) -> Result { let mut table = linked_hash_map::LinkedHashMap::new(); (|| -> Result<_, DeserializeError> { @@ -2706,18 +2869,22 @@ impl Deserialize for MapTransactionIndexToTransactionMetadata { } } Ok(()) - })().map_err(|e| e.annotate("MapTransactionIndexToTransactionMetadata"))?; + })().map_err(|e| e.annotate("AuxiliaryDataSet"))?; Ok(Self(table)) } } impl cbor_event::se::Serialize for Block { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(4))?; + serializer.write_array(cbor_event::Len::Len(5))?; self.header.serialize(serializer)?; self.transaction_bodies.serialize(serializer)?; self.transaction_witness_sets.serialize(serializer)?; - self.transaction_metadata_set.serialize(serializer)?; + self.auxiliary_data_set.serialize(serializer)?; + serializer.write_array(cbor_event::Len::Len(self.invalid_transactions.len() as u64))?; + for element in self.invalid_transactions.iter() { + element.serialize(serializer)?; + } Ok(serializer) } } @@ -2726,42 +2893,50 @@ impl Deserialize for Block { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(5)?; + let header = (|| -> Result<_, DeserializeError> { + Ok(Header::deserialize(raw)?) + })().map_err(|e| e.annotate("header"))?; + let transaction_bodies = (|| -> Result<_, DeserializeError> { + Ok(TransactionBodies::deserialize(raw)?) + })().map_err(|e| e.annotate("transaction_bodies"))?; + let transaction_witness_sets = (|| -> Result<_, DeserializeError> { + Ok(TransactionWitnessSets::deserialize(raw)?) + })().map_err(|e| e.annotate("transaction_witness_sets"))?; + let auxiliary_data_set = (|| -> Result<_, DeserializeError> { + Ok(AuxiliaryDataSet::deserialize(raw)?) + })().map_err(|e| e.annotate("auxiliary_data_set"))?; + let invalid_transactions = (|| -> Result<_, DeserializeError> { + let mut arr = Vec::new(); + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(TransactionIndex::deserialize(raw)?); + } + Ok(arr) + })().map_err(|e| e.annotate("invalid_transactions"))?; match len { - cbor_event::Len::Len(_) => /* TODO: check finite len somewhere */(), + cbor_event::Len::Len(_) => (), cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => /* it's ok */(), + CBORSpecial::Break => (), _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, } - ret + Ok(Block { + header, + transaction_bodies, + transaction_witness_sets, + auxiliary_data_set, + invalid_transactions, + }) })().map_err(|e| e.annotate("Block")) } } -impl DeserializeEmbeddedGroup for Block { - fn deserialize_as_embedded_group(raw: &mut Deserializer, len: cbor_event::Len) -> Result { - let header = (|| -> Result<_, DeserializeError> { - Ok(Header::deserialize(raw)?) - })().map_err(|e| e.annotate("header"))?; - let transaction_bodies = (|| -> Result<_, DeserializeError> { - Ok(TransactionBodies::deserialize(raw)?) - })().map_err(|e| e.annotate("transaction_bodies"))?; - let transaction_witness_sets = (|| -> Result<_, DeserializeError> { - Ok(TransactionWitnessSets::deserialize(raw)?) - })().map_err(|e| e.annotate("transaction_witness_sets"))?; - let transaction_metadata_set = (|| -> Result<_, DeserializeError> { - Ok(MapTransactionIndexToTransactionMetadata::deserialize(raw)?) - })().map_err(|e| e.annotate("transaction_metadata_set"))?; - Ok(Block { - header, - transaction_bodies, - transaction_witness_sets, - transaction_metadata_set, - }) - } -} - impl cbor_event::se::Serialize for Header { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { serializer.write_array(cbor_event::Len::Len(2))?; @@ -3133,3 +3308,90 @@ impl Deserialize for Mint { } } +impl cbor_event::se::Serialize for NetworkId { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self.0 { + // TODO: this is what I understood from the Haskell code + // but we should double-check it + NetworkIdKind::Testnet => { + serializer.write_unsigned_integer(0u64) + }, + NetworkIdKind::Mainnet => { + serializer.write_unsigned_integer(1u64) + }, + } + } +} + +impl Deserialize for NetworkId { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.unsigned_integer()? { + 0 => Ok(NetworkId::testnet()), + 1 => Ok(NetworkId::mainnet()), + _ => Err(DeserializeError::new("NetworkId", DeserializeFailure::NoVariantMatched.into())), + } + })().map_err(|e| e.annotate("NetworkId")) + } +} + +impl cbor_event::se::Serialize for PreludeUnsignedEnum { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self { + PreludeUnsignedEnum::U64(x) => { + x.serialize(serializer) + }, + PreludeUnsignedEnum::PreludeBiguint(x) => { + x.serialize(serializer) + }, + } + } +} + +impl Deserialize for PreludeUnsignedEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(BigNum::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PreludeUnsignedEnum::U64(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PreludeBiguint::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PreludeUnsignedEnum::PreludeBiguint(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + Err(DeserializeError::new("PreludeUnsignedEnum", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("PreludeUnsignedEnum")) + } +} + +impl cbor_event::se::Serialize for PreludeUnsigned { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for PreludeUnsigned { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(PreludeUnsignedEnum::deserialize(raw)?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn testing() { + let hex = "83a40082825820c9f112a8f20e0cf618918f76c936e859769f02f25539c3b90844dbf6fa3d3250008258207162f3d9a1edc1a20c0a38c2acb854e221329ca76f7666ee3c82c026b5dadfbf000182825839000c82e5ee957c7c3758b310e41a4ff501ab984aee7214275c330f2e3ec11ef08c44f3610b7e56d46e086b90186c12e9a68f0521b7c4c72e4b821a00160a5ba1581c8c4662efcb7fd069c9e4003192b430e9e153e5c3e11099e3dab29772a1454d4152454b181e825839005e0954bcf3f67374ac7a74d5246b63231650d543b1661bff6687943bc11ef08c44f3610b7e56d46e086b90186c12e9a68f0521b7c4c72e4b821a3ba09e60a1581c8c4662efcb7fd069c9e4003192b430e9e153e5c3e11099e3dab29772a1454d4152454b19027c021a0002a5c5031a0121ca4da10081825820ad34b8a04bc18ec5f55afca0d3bfee2d64b870f125a9808c2d74bc21d9d480855840a6289508fe121e14f2ebf58ac0453ba02a7af084042159f833bc6cd3a911e3fe0bfbc6a44980ca2a129fe6dc8c190fa00e883f0b4db0f03ced6b246d5c2ac80bf6"; + let bytes = hex::decode(hex).unwrap(); + Transaction::from_bytes(bytes).unwrap(); + } + +} \ No newline at end of file diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs index 1579b282..d75bd8a9 100644 --- a/rust/src/tx_builder.rs +++ b/rust/src/tx_builder.rs @@ -91,7 +91,7 @@ fn min_fee(tx_builder: &TransactionBuilder) -> Result { let full_tx = Transaction { body, witness_set, - metadata: tx_builder.metadata.clone(), + auxiliary_data: tx_builder.auxiliary_data.clone(), }; fees::min_fee(&full_tx, &tx_builder.fee_algo) } @@ -124,7 +124,7 @@ pub struct TransactionBuilder { ttl: Option, // absolute slot number certs: Option, withdrawals: Option, - metadata: Option, + auxiliary_data: Option, validity_start_interval: Option, input_types: MockWitnessSet, mint: Option, @@ -277,8 +277,8 @@ impl TransactionBuilder { }; } - pub fn set_metadata(&mut self, metadata: &TransactionMetadata) { - self.metadata = Some(metadata.clone()) + pub fn set_auxiliary_data(&mut self, auxiliary_data: &AuxiliaryData) { + self.auxiliary_data = Some(auxiliary_data.clone()) } pub fn new( @@ -299,7 +299,7 @@ impl TransactionBuilder { ttl: None, certs: None, withdrawals: None, - metadata: None, + auxiliary_data: None, input_types: MockWitnessSet { vkeys: BTreeSet::new(), scripts: BTreeSet::new(), @@ -394,6 +394,8 @@ impl TransactionBuilder { let fee_for_change = builder.fee_for_output(&TransactionOutput { address: address.clone(), amount: change_estimator.clone(), + // TODO: data hash? + data_hash: None, })?; let new_fee = fee.checked_add(&fee_for_change)?; @@ -430,6 +432,7 @@ impl TransactionBuilder { builder.add_output(&TransactionOutput { address: address.clone(), amount: change_estimator.checked_sub(&Value::new(&new_fee.clone()))?, + data_hash: None, // TODO: How do we get DataHash? })?; Ok(true) @@ -455,12 +458,17 @@ impl TransactionBuilder { certs: self.certs.clone(), withdrawals: self.withdrawals.clone(), update: None, - metadata_hash: match &self.metadata { + auxiliary_data_hash: match &self.auxiliary_data { None => None, - Some(x) => Some(utils::hash_metadata(x)), + Some(x) => Some(utils::hash_auxiliary_data(x)), }, validity_start_interval: self.validity_start_interval, mint: self.mint.clone(), + // TODO: update for use with Alonzo + script_data_hash: None, + collateral: None, + required_signers: None, + network_id: None, }) } diff --git a/rust/src/utils.rs b/rust/src/utils.rs index 0050e6a1..93da08f6 100644 --- a/rust/src/utils.rs +++ b/rust/src/utils.rs @@ -536,8 +536,8 @@ pub fn make_vkey_witness( } #[wasm_bindgen] -pub fn hash_metadata(metadata: &TransactionMetadata) -> MetadataHash { - MetadataHash::from(blake2b256(&metadata.to_bytes())) +pub fn hash_auxiliary_data(auxiliary_data: &AuxiliaryData) -> AuxiliaryDataHash { + AuxiliaryDataHash::from(blake2b256(&auxiliary_data.to_bytes())) } #[wasm_bindgen] pub fn hash_transaction(tx_body: &TransactionBody) -> TransactionHash { From 64cf0121f3ed4fff7d1504213c31faae04e5f352 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Thu, 17 Jun 2021 22:22:46 -0500 Subject: [PATCH 03/11] TransactionOutput serialization This had to be hand-written as we can't codegen optional types within arrays as it is hard to come up with a general approach for that without a possible backtrack when you have multiple optional types. It gets trickier since this is actually a plain group being embedded into TransactionOutputs so you get [addr, val, addr, val, datahash, addr, val] instead of [[addr, val], [addr, val, datahash], [addr, val]] --- rust/src/serialization.rs | 62 ++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/rust/src/serialization.rs b/rust/src/serialization.rs index 05128e2a..d7f5c30a 100644 --- a/rust/src/serialization.rs +++ b/rust/src/serialization.rs @@ -495,13 +495,18 @@ impl DeserializeEmbeddedGroup for TransactionInput { impl cbor_event::se::Serialize for TransactionOutput { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; + serializer.write_array(cbor_event::Len::Len(if self.data_hash.is_some() { 3 } else { 2 }))?; self.address.serialize(serializer)?; self.amount.serialize(serializer)?; + if let Some(data_hash) = &self.data_hash { + data_hash.serialize(serializer)?; + } Ok(serializer) } } +// this is used when deserializing it on its own, but the more likely case +// is when it's done via TransactionOutputs impl Deserialize for TransactionOutput { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { @@ -519,19 +524,49 @@ impl Deserialize for TransactionOutput { } } +// this is used by both TransactionOutput (on its own)'s deserialize +// but also for TransactionOutputs +// This implementation was hand-coded since cddl-codegen doesn't support deserialization +// with array-encoded types with optional fields, due to the complexity. +// This is made worse as this is a plain group... impl DeserializeEmbeddedGroup for TransactionOutput { fn deserialize_as_embedded_group(raw: &mut Deserializer, len: cbor_event::Len) -> Result { + use std::convert::TryInto; let address = (|| -> Result<_, DeserializeError> { Ok(Address::deserialize(raw)?) })().map_err(|e| e.annotate("address"))?; let amount = (|| -> Result<_, DeserializeError> { Ok(Value::deserialize(raw)?) })().map_err(|e| e.annotate("amount"))?; - // TODO: how are we supposed to do this inside of a plain group?! + // there are only two cases so far where this is used: + // 1) on its own inside of TransactionOutput's Deserialize trait (only used if someone calls to_bytes() on it) + // 2) from TransactionOutput's deserialization + // in 1) we would encounter an array-end (or track it for definite deserialization - which we don't do right now) + // and in 2) we would encounter the same OR we would encounter the next TransactionOutput in the array + // Unfortunately, both address and data hash are bytes type, so we can't just check the type, but instead + // must check the length, and backtrack if that wasn't the case. + let data_hash = match raw.cbor_type() { + Ok(cbor_event::Type::Bytes) => { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + let bytes = raw.bytes().unwrap(); + if bytes.len() == DataHash::BYTE_COUNT { + Some(DataHash(bytes[..DataHash::BYTE_COUNT].try_into().unwrap())) + } else { + // This is an address of the next output in sequence, which luckily is > 32 bytes so there's no confusion + // Go to previous place in array then carry on + raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(); + None + } + }, + // not possibly a data hash + Ok(_) | + // end of input + Err(_) => None, + }; Ok(TransactionOutput { address, amount, - data_hash: None, + data_hash, }) } } @@ -3388,10 +3423,23 @@ mod tests { use super::*; #[test] - fn testing() { - let hex = "83a40082825820c9f112a8f20e0cf618918f76c936e859769f02f25539c3b90844dbf6fa3d3250008258207162f3d9a1edc1a20c0a38c2acb854e221329ca76f7666ee3c82c026b5dadfbf000182825839000c82e5ee957c7c3758b310e41a4ff501ab984aee7214275c330f2e3ec11ef08c44f3610b7e56d46e086b90186c12e9a68f0521b7c4c72e4b821a00160a5ba1581c8c4662efcb7fd069c9e4003192b430e9e153e5c3e11099e3dab29772a1454d4152454b181e825839005e0954bcf3f67374ac7a74d5246b63231650d543b1661bff6687943bc11ef08c44f3610b7e56d46e086b90186c12e9a68f0521b7c4c72e4b821a3ba09e60a1581c8c4662efcb7fd069c9e4003192b430e9e153e5c3e11099e3dab29772a1454d4152454b19027c021a0002a5c5031a0121ca4da10081825820ad34b8a04bc18ec5f55afca0d3bfee2d64b870f125a9808c2d74bc21d9d480855840a6289508fe121e14f2ebf58ac0453ba02a7af084042159f833bc6cd3a911e3fe0bfbc6a44980ca2a129fe6dc8c190fa00e883f0b4db0f03ced6b246d5c2ac80bf6"; - let bytes = hex::decode(hex).unwrap(); - Transaction::from_bytes(bytes).unwrap(); + fn tx_output_deser() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput::new(&addr, &val); + let mut txo_dh = txo.clone(); + txo_dh.set_data_hash(&DataHash::from([47u8; DataHash::BYTE_COUNT])); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let bytes = txos.to_bytes(); + let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); + let bytes_deser = txos_deser.to_bytes(); + assert_eq!(bytes, bytes_deser); } } \ No newline at end of file From 6846183fdace6cff4d8527563ed4e459d0ff11bd Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Thu, 17 Jun 2021 23:24:19 -0500 Subject: [PATCH 04/11] AuxilaryData implementation Like the Shelley -> Shelley Mary with native scripts, the type definition is quite awkward with multiple types when it boils down to simply having optional fields, so instead of working with super ugly codegened types with like 5 types instead of 1, I just hand-wrote this code. I kept the old shelley / mary formats for serialization as they reduce on-chain space usage, and they're required to be implemented for deserialization anyway in alonzo. I didn't keep the old TransacitonMetadata name as I think it is different enough that it makes sense to break older code which would likely have to be updated anyway to go through with the AuxilaryData name IOHK is using in their binary spec now, rather than storing plutus stuff inside of the "metadata". --- rust/src/metadata.rs | 196 ++++++++++++++++++++++++++++++-------- rust/src/plutus.rs | 85 ++++++++++++++++- rust/src/serialization.rs | 4 +- 3 files changed, 241 insertions(+), 44 deletions(-) diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index 4d49dc35..23d83c07 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -284,22 +284,32 @@ impl GeneralTransactionMetadata { } } -// TODO: hand-write this as AuxiliaryData -pub type AuxiliaryData = TransactionMetadata; - #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct TransactionMetadata { - general: GeneralTransactionMetadata, +pub struct AuxiliaryData { + metadata: Option, native_scripts: Option, + plutus_scripts: Option, } -to_from_bytes!(TransactionMetadata); +to_from_bytes!(AuxiliaryData); #[wasm_bindgen] -impl TransactionMetadata { - pub fn general(&self) -> GeneralTransactionMetadata { - self.general.clone() +impl AuxiliaryData { + pub fn new() -> Self { + Self { + metadata: None, + native_scripts: None, + plutus_scripts: None, + } + } + + pub fn metadata(&self) -> Option { + self.metadata.clone() + } + + pub fn set_metadata(&mut self, metadata: &GeneralTransactionMetadata) { + self.metadata = Some(metadata.clone()); } pub fn native_scripts(&self) -> Option { @@ -310,11 +320,12 @@ impl TransactionMetadata { self.native_scripts = Some(native_scripts.clone()) } - pub fn new(general: &GeneralTransactionMetadata) -> Self { - Self { - general: general.clone(), - native_scripts: None, - } + pub fn plutus_scripts(&self) -> Option { + self.plutus_scripts.clone() + } + + pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { + self.plutus_scripts = Some(plutus_scripts.clone()) } } @@ -755,30 +766,121 @@ impl Deserialize for GeneralTransactionMetadata { } } -impl cbor_event::se::Serialize for TransactionMetadata { +impl cbor_event::se::Serialize for AuxiliaryData { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - match &self.native_scripts() { - Some(native_scripts) => { - serializer.write_array(cbor_event::Len::Len(2))?; - self.general.serialize(serializer)?; - native_scripts.serialize(serializer) - }, - None => self.general.serialize(serializer) + // we still serialize using the shelley-mary era format as it is still supported + // and it takes up less space on-chain so this should be better for scaling. + // Plus the code was already written for shelley-mary anyway + if self.metadata.is_some() && self.plutus_scripts.is_none() { + match &self.native_scripts() { + Some(native_scripts) => { + serializer.write_array(cbor_event::Len::Len(2))?; + self.metadata.as_ref().unwrap().serialize(serializer)?; + native_scripts.serialize(serializer) + }, + None => self.metadata.as_ref().unwrap().serialize(serializer), + } + } else { + // new format with plutus support + serializer.write_tag(259u64)?; + serializer.write_map(cbor_event::Len::Len( + if self.metadata.is_some() { 1 } else { 0 } + + if self.native_scripts.is_some() { 1 } else { 0 } + + if self.plutus_scripts.is_some() { 1 } else { 0 }))?; + if let Some(metadata) = &self.metadata { + serializer.write_unsigned_integer(0)?; + metadata.serialize(serializer)?; + } + if let Some(native_scripts) = &self.native_scripts { + serializer.write_unsigned_integer(1)?; + native_scripts.serialize(serializer)?; + } + if let Some(plutus_scripts) = &self.plutus_scripts { + serializer.write_unsigned_integer(2)?; + plutus_scripts.serialize(serializer)?; + } + Ok(serializer) } } } -impl Deserialize for TransactionMetadata { +impl Deserialize for AuxiliaryData { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { match raw.cbor_type()? { + // alonzo format + CBORType::Tag => { + let tag = raw.tag()?; + if tag != 259 { + return Err(DeserializeError::new("AuxiliaryData", DeserializeFailure::TagMismatch{ found: tag, expected: 259 })); + } + let len = raw.map()?; + let mut read_len = CBORReadLen::new(len); + let mut metadata = None; + let mut native_scripts = None; + let mut plutus_scripts = None; + let mut read = 0; + while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + 0 => { + if metadata.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + metadata = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(GeneralTransactionMetadata::deserialize(raw)?) + })().map_err(|e| e.annotate("metadata"))?); + }, + 1 => { + if native_scripts.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + native_scripts = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(NativeScripts::deserialize(raw)?) + })().map_err(|e| e.annotate("native_scripts"))?); + }, + 2 => { + if plutus_scripts.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + plutus_scripts = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize(raw)?) + })().map_err(|e| e.annotate("plutus_scripts"))?); + }, + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), + }, + CBORType::Text => match raw.text()?.as_str() { + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), + }, + CBORType::Special => match len { + cbor_event::Len::Len(_) => return Err(DeserializeFailure::BreakInDefiniteLen.into()), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }, + other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + } + read += 1; + } + read_len.finish()?; + Ok(Self { + metadata, + native_scripts, + plutus_scripts, + }) + }, + // shelley mary format (still valid for alonzo) CBORType::Array => { let len = raw.array()?; let mut read_len = CBORReadLen::new(len); read_len.read_elems(2)?; - let general = (|| -> Result<_, DeserializeError> { + let metadata = (|| -> Result<_, DeserializeError> { Ok(GeneralTransactionMetadata::deserialize(raw)?) - })().map_err(|e| e.annotate("general"))?; + })().map_err(|e| e.annotate("metadata"))?; let native_scripts = (|| -> Result<_, DeserializeError> { Ok(NativeScripts::deserialize(raw)?) })().map_err(|e| e.annotate("native_scripts"))?; @@ -789,18 +891,21 @@ impl Deserialize for TransactionMetadata { _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, } - Ok(TransactionMetadata { - general, + Ok(Self { + metadata: Some(metadata), native_scripts: Some(native_scripts), + plutus_scripts: None, }) }, - CBORType::Map => Ok(TransactionMetadata { - general: GeneralTransactionMetadata::deserialize(raw).map_err(|e| e.annotate("general"))?, + // shelley pre-mary format (still valid for alonzo + mary) + CBORType::Map => Ok(Self { + metadata: Some(GeneralTransactionMetadata::deserialize(raw).map_err(|e| e.annotate("metadata"))?), native_scripts: None, + plutus_scripts: None, }), _ => return Err(DeserializeFailure::NoVariantMatched)? } - })().map_err(|e| e.annotate("TransactionMetadata")) + })().map_err(|e| e.annotate("AuxiliaryData")) } } @@ -936,18 +1041,29 @@ mod tests { } #[test] - fn allegra_metadata() { + fn metadata_serialize() { let mut gmd = GeneralTransactionMetadata::new(); let mdatum = TransactionMetadatum::new_text(String::from("string md")).unwrap(); gmd.insert(&to_bignum(100), &mdatum); - let md1 = TransactionMetadata::new(&gmd); - let md1_deser = TransactionMetadata::from_bytes(md1.to_bytes()).unwrap(); - assert_eq!(md1.to_bytes(), md1_deser.to_bytes()); - let mut md2 = TransactionMetadata::new(&gmd); - let mut scripts = NativeScripts::new(); - scripts.add(&NativeScript::new_timelock_start(&TimelockStart::new(20))); - md2.set_native_scripts(&scripts); - let md2_deser = TransactionMetadata::from_bytes(md2.to_bytes()).unwrap(); - assert_eq!(md2.to_bytes(), md2_deser.to_bytes()); + let mut aux_data = AuxiliaryData::new(); + // alonzo (empty) + let ad0_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad0_deser.to_bytes()); + // pre-mary shelley + aux_data.set_metadata(&gmd); + let ad1_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad1_deser.to_bytes()); + // mary shelley + let mut native_scripts = NativeScripts::new(); + native_scripts.add(&NativeScript::new_timelock_start(&TimelockStart::new(20))); + aux_data.set_native_scripts(&native_scripts); + let ad2_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad2_deser.to_bytes()); + // alonzo + let mut plutus_scripts = PlutusScripts::new(); + plutus_scripts.add(&PlutusScript::new([61u8; 29].to_vec())); + aux_data.set_plutus_scripts(&plutus_scripts); + let ad3_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad3_deser.to_bytes()); } } diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs index b36919c5..9ef8ea36 100644 --- a/rust/src/plutus.rs +++ b/rust/src/plutus.rs @@ -7,8 +7,47 @@ use super::*; use cbor_event::{self, de::Deserializer, se::{Serialize, Serializer}}; -// TODO: replace with its own struct if we decide to add other functionality? -type PlutusScript = Vec; +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusScript(Vec); + +to_from_bytes!(PlutusScript); + +#[wasm_bindgen] +impl PlutusScript { + pub fn new(bytes: Vec) -> PlutusScript { + Self(bytes) + } + + pub fn bytes(&self) -> Vec { + self.0.clone() + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusScripts(Vec); + +to_from_bytes!(PlutusScripts); + +#[wasm_bindgen] +impl PlutusScripts { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> PlutusScript { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &PlutusScript) { + self.0.push(elem.clone()); + } +} #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -606,6 +645,48 @@ impl Strings { use std::io::{SeekFrom}; + +impl cbor_event::se::Serialize for PlutusScript { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(&self.0) + } +} + +impl Deserialize for PlutusScript { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(raw.bytes()?)) + } +} + +impl cbor_event::se::Serialize for PlutusScripts { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for PlutusScripts { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(PlutusScript::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("PlutusScripts"))?; + Ok(Self(arr)) + } +} + + // TODO: write tests for this hand-coded implementation? impl cbor_event::se::Serialize for ConstrPlutusData { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { diff --git a/rust/src/serialization.rs b/rust/src/serialization.rs index d7f5c30a..37ef1265 100644 --- a/rust/src/serialization.rs +++ b/rust/src/serialization.rs @@ -97,7 +97,7 @@ impl DeserializeEmbeddedGroup for Transaction { let auxiliary_data = (|| -> Result<_, DeserializeError> { Ok(match raw.cbor_type()? != CBORType::Special { true => { - Some(TransactionMetadata::deserialize(raw)?) + Some(AuxiliaryData::deserialize(raw)?) }, false => { if raw.special()? != CBORSpecial::Null { @@ -2898,7 +2898,7 @@ impl Deserialize for AuxiliaryDataSet { break; } let key = TransactionIndex::deserialize(raw)?; - let value = TransactionMetadata::deserialize(raw)?; + let value = AuxiliaryData::deserialize(raw)?; if table.insert(key.clone(), value).is_some() { return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); } From 2e4d945a851585d101170e1b343c5695f1948a4d Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Mon, 21 Jun 2021 21:13:01 -0500 Subject: [PATCH 05/11] MIR impl --- rust/src/lib.rs | 78 ++++++++++++++++++++++++++++++++---- rust/src/serialization.rs | 84 ++++++++++++++++++++++++++++----------- 2 files changed, 131 insertions(+), 31 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 08a97a3d..a07df6d6 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -854,27 +854,37 @@ impl Certificate { } #[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum MIRPot { Reserves, Treasury, } -// TODO: rewrite to handle the simple coin case (split this into 2 types?) +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum MIREnum { + ToOtherPot(Coin), + ToStakeCredentials(MIRToStakeCredentials), +} + #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct MoveInstantaneousReward { - pot: MIRPot, +pub enum MIRKind { + ToOtherPot, + ToStakeCredentials, +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct MIRToStakeCredentials { rewards: linked_hash_map::LinkedHashMap, } -to_from_bytes!(MoveInstantaneousReward); +to_from_bytes!(MIRToStakeCredentials); #[wasm_bindgen] -impl MoveInstantaneousReward { - pub fn new(pot: MIRPot) -> Self { +impl MIRToStakeCredentials { + pub fn new() -> Self { Self { - pot, rewards: linked_hash_map::LinkedHashMap::new(), } } @@ -901,6 +911,58 @@ impl MoveInstantaneousReward { } } +// TODO: rewrite to handle the simple coin case (split this into 2 types?) +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct MoveInstantaneousReward { + pot: MIRPot, + variant: MIREnum, +} + +to_from_bytes!(MoveInstantaneousReward); + +#[wasm_bindgen] +impl MoveInstantaneousReward { + pub fn new_to_other_pot(pot: MIRPot, amount: &Coin) -> Self { + Self { + pot, + variant: MIREnum::ToOtherPot(amount.clone()), + } + } + + pub fn new_to_stake_creds(pot: MIRPot, amounts: &MIRToStakeCredentials) -> Self { + Self { + pot, + variant: MIREnum::ToStakeCredentials(amounts.clone()), + } + } + + pub fn pot(&self) -> MIRPot { + self.pot + } + + pub fn kind(&self) -> MIRKind { + match &self.variant { + MIREnum::ToOtherPot(_) => MIRKind::ToOtherPot, + MIREnum::ToStakeCredentials(_) => MIRKind::ToStakeCredentials, + } + } + + pub fn as_to_other_pot(&self) -> Option { + match &self.variant { + MIREnum::ToOtherPot(amount) => Some(amount.clone()), + MIREnum::ToStakeCredentials(_) => None, + } + } + + pub fn as_to_stake_creds(&self) -> Option { + match &self.variant { + MIREnum::ToOtherPot(_) => None, + MIREnum::ToStakeCredentials(amounts) => Some(amounts.clone()), + } + } +} + type Port = u16; #[wasm_bindgen] diff --git a/rust/src/serialization.rs b/rust/src/serialization.rs index 37ef1265..549970f4 100644 --- a/rust/src/serialization.rs +++ b/rust/src/serialization.rs @@ -1221,13 +1221,8 @@ impl Deserialize for StakeCredentials { } } -impl cbor_event::se::Serialize for MoveInstantaneousReward { +impl cbor_event::se::Serialize for MIRToStakeCredentials { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - match self.pot { - MIRPot::Reserves => serializer.write_unsigned_integer(0u64), - MIRPot::Treasury => serializer.write_unsigned_integer(1u64), - }?; serializer.write_map(cbor_event::Len::Len(self.rewards.len() as u64))?; for (key, value) in &self.rewards { key.serialize(serializer)?; @@ -1237,17 +1232,10 @@ impl cbor_event::se::Serialize for MoveInstantaneousReward { } } -impl Deserialize for MoveInstantaneousReward { +impl Deserialize for MIRToStakeCredentials { fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = linked_hash_map::LinkedHashMap::new(); - let pot = (|| -> Result<_, DeserializeError> { - let outer_len = raw.array()?; - let pot = match raw.unsigned_integer()? { - 0 => MIRPot::Reserves, - 1 => MIRPot::Treasury, - n => return Err(DeserializeFailure::UnknownKey(Key::Uint(n)).into()), - }; - let len = raw.map()?; + (|| -> Result<_, DeserializeError> { + let mut table = linked_hash_map::LinkedHashMap::new();let len = raw.map()?; while match len { cbor_event::Len::Len(n) => table.len() < n as usize, cbor_event::Len::Indefinite => true, } { if raw.cbor_type()? == CBORType::Special { assert_eq!(raw.special()?, CBORSpecial::Break); @@ -1256,9 +1244,45 @@ impl Deserialize for MoveInstantaneousReward { let key = StakeCredential::deserialize(raw)?; let value = DeltaCoin::deserialize(raw)?; if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + return Err(DeserializeFailure::DuplicateKey(Key::Str(format!("StakeCred: {} (hex bytes)", hex::encode(key.to_bytes())))).into()); } } + Ok(Self { + rewards: table + }) + })().map_err(|e| e.annotate("MIRToStakeCredentials")) + + } +} + +impl cbor_event::se::Serialize for MoveInstantaneousReward { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + match self.pot { + MIRPot::Reserves => serializer.write_unsigned_integer(0u64), + MIRPot::Treasury => serializer.write_unsigned_integer(1u64), + }?; + match &self.variant { + MIREnum::ToOtherPot(amount) => amount.serialize(serializer), + MIREnum::ToStakeCredentials(amounts) => amounts.serialize(serializer), + } + } +} + +impl Deserialize for MoveInstantaneousReward { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let outer_len = raw.array()?; + let pot = match raw.unsigned_integer()? { + 0 => MIRPot::Reserves, + 1 => MIRPot::Treasury, + n => return Err(DeserializeFailure::UnknownKey(Key::Uint(n)).into()), + }; + let variant = match raw.cbor_type()? { + CBORType::UnsignedInteger => MIREnum::ToOtherPot(Coin::deserialize(raw)?), + CBORType::Map => MIREnum::ToStakeCredentials(MIRToStakeCredentials::deserialize(raw)?), + _ => return Err(DeserializeFailure::NoVariantMatched.into()), + }; match outer_len { cbor_event::Len::Len(n) => if n != 2 { return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen(n, outer_len, "MoveInstantaneousReward")).into()) @@ -1268,12 +1292,11 @@ impl Deserialize for MoveInstantaneousReward { _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, }; - Ok(pot) - })().map_err(|e| e.annotate("MoveInstantaneousReward"))?; - Ok(Self { - pot, - rewards: table - }) + Ok(Self { + pot, + variant, + }) + })().map_err(|e| e.annotate("MoveInstantaneousReward")) } } @@ -3442,4 +3465,19 @@ mod tests { assert_eq!(bytes, bytes_deser); } + #[test] + fn mir_deser() { + let reserves_to_pot = MoveInstantaneousReward::new_to_other_pot(MIRPot::Treasury, &Coin::from_str("143546464").unwrap()); + let reserves_to_pot_deser = MoveInstantaneousReward::from_bytes(reserves_to_pot.to_bytes()).unwrap(); + assert_eq!(reserves_to_pot.to_bytes(), reserves_to_pot_deser.to_bytes()); + let treasury_to_pot = MoveInstantaneousReward::new_to_other_pot(MIRPot::Treasury, &Coin::from_str("0").unwrap()); + let treasury_to_pot_deser = MoveInstantaneousReward::from_bytes(treasury_to_pot.to_bytes()).unwrap(); + assert_eq!(treasury_to_pot.to_bytes(), treasury_to_pot_deser.to_bytes()); + let mut stake_creds = MIRToStakeCredentials::new(); + stake_creds.insert(&StakeCredential::from_scripthash(&ScriptHash([54u8; ScriptHash::BYTE_COUNT])), &Int::new_i32(-314159265)); + let to_stake_creds = MoveInstantaneousReward::new_to_stake_creds(MIRPot::Treasury, &stake_creds); + let to_stake_creds_deser = MoveInstantaneousReward::from_bytes(to_stake_creds.to_bytes()).unwrap(); + assert_eq!(to_stake_creds.to_bytes(), to_stake_creds_deser.to_bytes()); + + } } \ No newline at end of file From 1a1561df4104f11f44446e5bb9b5a2415b5a7ecd Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Tue, 22 Jun 2021 14:43:55 -0500 Subject: [PATCH 06/11] add alonzo example block for testing + some fixes --- rust/src/lib.rs | 45 ++++++++-- rust/src/plutus.rs | 60 +++++++++++++- rust/src/serialization.rs | 170 +++++++++++++++++++++++++------------- rust/src/tx_builder.rs | 6 +- 4 files changed, 209 insertions(+), 72 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a07df6d6..b753d8d1 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -207,7 +207,7 @@ impl Certificates { } } -pub type RequiredSigners = Vkeys; +pub type RequiredSigners = Ed25519KeyHashes; #[wasm_bindgen] #[derive(Clone)] @@ -911,7 +911,6 @@ impl MIRToStakeCredentials { } } -// TODO: rewrite to handle the simple coin case (split this into 2 types?) #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct MoveInstantaneousReward { @@ -1371,8 +1370,11 @@ impl Withdrawals { #[derive(Clone)] pub struct TransactionWitnessSet { vkeys: Option, - scripts: Option, + native_scripts: Option, bootstraps: Option, + plutus_scripts: Option, + plutus_data: Option, + redeemers: Option, } to_from_bytes!(TransactionWitnessSet); @@ -1387,12 +1389,12 @@ impl TransactionWitnessSet { self.vkeys.clone() } - pub fn set_scripts(&mut self, scripts: &NativeScripts) { - self.scripts = Some(scripts.clone()) + pub fn set_native_scripts(&mut self, native_scripts: &NativeScripts) { + self.native_scripts = Some(native_scripts.clone()) } - pub fn scripts(&self) -> Option { - self.scripts.clone() + pub fn native_scripts(&self) -> Option { + self.native_scripts.clone() } pub fn set_bootstraps(&mut self, bootstraps: &BootstrapWitnesses) { @@ -1403,11 +1405,38 @@ impl TransactionWitnessSet { self.bootstraps.clone() } + pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { + self.plutus_scripts = Some(plutus_scripts.clone()) + } + + pub fn plutus_scripts(&self) -> Option { + self.plutus_scripts.clone() + } + + pub fn set_plutus_data(&mut self, plutus_data: &PlutusList) { + self.plutus_data = Some(plutus_data.clone()) + } + + pub fn plutus_data(&self) -> Option { + self.plutus_data.clone() + } + + pub fn set_redeemers(&mut self, redeemers: &Redeemers) { + self.redeemers = Some(redeemers.clone()) + } + + pub fn redeemers(&self) -> Option { + self.redeemers.clone() + } + pub fn new() -> Self { Self { vkeys: None, - scripts: None, + native_scripts: None, bootstraps: None, + plutus_scripts: None, + plutus_data: None, + redeemers: None, } } } diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs index 9ef8ea36..3e2f5e71 100644 --- a/rust/src/plutus.rs +++ b/rust/src/plutus.rs @@ -567,6 +567,7 @@ pub enum RedeemerTagKind { Reward, } +// TODO: simplify these two into one struct if possible: see NetworkId #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] enum RedeemerTagEnum { Spend, @@ -583,19 +584,19 @@ to_from_bytes!(RedeemerTagEnum); #[wasm_bindgen] impl RedeemerTag { - pub fn new_i0() -> Self { + pub fn new_spend() -> Self { Self(RedeemerTagEnum::Spend) } - pub fn new_i1() -> Self { + pub fn new_mint() -> Self { Self(RedeemerTagEnum::Mint) } - pub fn new_i2() -> Self { + pub fn new_cert() -> Self { Self(RedeemerTagEnum::Cert) } - pub fn new_i3() -> Self { + pub fn new_reward() -> Self { Self(RedeemerTagEnum::Reward) } @@ -609,6 +610,29 @@ impl RedeemerTag { } } +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Redeemers(Vec); + +#[wasm_bindgen] +impl Redeemers { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Redeemer { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &Redeemer) { + self.0.push(elem.clone()); + } +} + #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct Strings(Vec); @@ -1335,6 +1359,34 @@ impl Deserialize for RedeemerTag { } } +impl cbor_event::se::Serialize for Redeemers { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Redeemers { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(Redeemer::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("Redeemers"))?; + Ok(Self(arr)) + } +} + impl cbor_event::se::Serialize for Strings { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; diff --git a/rust/src/serialization.rs b/rust/src/serialization.rs index 549970f4..245746df 100644 --- a/rust/src/serialization.rs +++ b/rust/src/serialization.rs @@ -1757,12 +1757,12 @@ impl Deserialize for Withdrawals { impl cbor_event::se::Serialize for TransactionWitnessSet { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(match &self.vkeys { Some(x) => 1, None => 0 } + match &self.scripts { Some(x) => 1, None => 0 } + match &self.bootstraps { Some(x) => 1, None => 0 }))?; + serializer.write_map(cbor_event::Len::Len(match &self.vkeys { Some(x) => 1, None => 0 } + match &self.native_scripts { Some(x) => 1, None => 0 } + match &self.bootstraps { Some(x) => 1, None => 0 } + match &self.plutus_scripts { Some(x) => 1, None => 0 } + match &self.plutus_data { Some(x) => 1, None => 0 } + match &self.redeemers { Some(x) => 1, None => 0 }))?; if let Some(field) = &self.vkeys { serializer.write_unsigned_integer(0)?; field.serialize(serializer)?; } - if let Some(field) = &self.scripts { + if let Some(field) = &self.native_scripts { serializer.write_unsigned_integer(1)?; field.serialize(serializer)?; } @@ -1770,6 +1770,18 @@ impl cbor_event::se::Serialize for TransactionWitnessSet { serializer.write_unsigned_integer(2)?; field.serialize(serializer)?; } + if let Some(field) = &self.plutus_scripts { + serializer.write_unsigned_integer(3)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.plutus_data { + serializer.write_unsigned_integer(4)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.redeemers { + serializer.write_unsigned_integer(5)?; + field.serialize(serializer)?; + } Ok(serializer) } } @@ -1778,65 +1790,97 @@ impl Deserialize for TransactionWitnessSet { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { let len = raw.map()?; - Self::deserialize_as_embedded_group(raw, len) - })().map_err(|e| e.annotate("TransactionWitnessSet")) - } -} - -impl DeserializeEmbeddedGroup for TransactionWitnessSet { - fn deserialize_as_embedded_group(raw: &mut Deserializer, len: cbor_event::Len) -> Result { - let mut vkeys = None; - let mut scripts = None; - let mut bootstraps = None; - let mut read = 0; - while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { - match raw.cbor_type()? { - CBORType::UnsignedInteger => match raw.unsigned_integer()? { - 0 => { - if vkeys.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); - } - vkeys = Some((|| -> Result<_, DeserializeError> { - Ok(Vkeywitnesses::deserialize(raw)?) - })().map_err(|e| e.annotate("vkeys"))?); - }, - 1 => { - if scripts.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); - } - scripts = Some((|| -> Result<_, DeserializeError> { - Ok(NativeScripts::deserialize(raw)?) - })().map_err(|e| e.annotate("scripts"))?); + let mut read_len = CBORReadLen::new(len); + let mut vkeys = None; + let mut native_scripts = None; + let mut bootstraps = None; + let mut plutus_scripts = None; + let mut plutus_data = None; + let mut redeemers = None; + let mut read = 0; + while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + 0 => { + if vkeys.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + vkeys = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Vkeywitnesses::deserialize(raw)?) + })().map_err(|e| e.annotate("vkeys"))?); + }, + 1 => { + if native_scripts.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + native_scripts = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(NativeScripts::deserialize(raw)?) + })().map_err(|e| e.annotate("native_scripts"))?); + }, + 2 => { + if bootstraps.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + bootstraps = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(BootstrapWitnesses::deserialize(raw)?) + })().map_err(|e| e.annotate("bootstraps"))?); + }, + 3 => { + if plutus_scripts.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); + } + plutus_scripts = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize(raw)?) + })().map_err(|e| e.annotate("plutus_scripts"))?); + }, + 4 => { + if plutus_data.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); + } + plutus_data = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusList::deserialize(raw)?) + })().map_err(|e| e.annotate("plutus_data"))?); + }, + 5 => { + if redeemers.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); + } + redeemers = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Redeemers::deserialize(raw)?) + })().map_err(|e| e.annotate("redeemers"))?); + }, + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), }, - 2 => { - if bootstraps.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); - } - bootstraps = Some((|| -> Result<_, DeserializeError> { - Ok(BootstrapWitnesses::deserialize(raw)?) - })().map_err(|e| e.annotate("bootstraps"))?); + CBORType::Text => match raw.text()?.as_str() { + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), }, - unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), - }, - CBORType::Text => match raw.text()?.as_str() { - unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), - }, - CBORType::Special => match len { - cbor_event::Len::Len(_) => return Err(DeserializeFailure::BreakInDefiniteLen.into()), - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => break, - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + CBORType::Special => match len { + cbor_event::Len::Len(_) => return Err(DeserializeFailure::BreakInDefiniteLen.into()), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, }, - }, - other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + } + read += 1; } - read += 1; - } - Ok(Self { - vkeys, - scripts, - bootstraps, - }) + read_len.finish()?; + Ok(Self { + vkeys, + native_scripts, + bootstraps, + plutus_scripts, + plutus_data, + redeemers, + }) + })().map_err(|e| e.annotate("TransactionWitnessSet")) } } @@ -3369,8 +3413,6 @@ impl Deserialize for Mint { impl cbor_event::se::Serialize for NetworkId { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { match self.0 { - // TODO: this is what I understood from the Haskell code - // but we should double-check it NetworkIdKind::Testnet => { serializer.write_unsigned_integer(0u64) }, @@ -3480,4 +3522,14 @@ mod tests { assert_eq!(to_stake_creds.to_bytes(), to_stake_creds_deser.to_bytes()); } + + #[test] + #[ignore] + fn alonzo_block() { + // this test for some reason has 2-byte pool metadata hashes so don't run this without changing that + let bytes = hex::decode("85828f03095820bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa58208a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c58208a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c8258404fefc7c718693b57c87170ceba220382afbdd148c0a53b4a009ca63ad1f101483a6170c83a77f23d362a68dcb502802df7f98fa4f7a78b4082b211530e1234305850f770f6769ae9871d42b970fc6254bb927c2181fff45897f241bd72221d86d33c8df64c0a3c8cbb9aa52fef191d7202465c52df8d33727a38c7dc5d40864d753348a340f8afcbb3bb05d4a03f16b1080d825840fe682775f0fa232e909ddc9ec3210ea7a0ee6514cd8b0815190a08f7cef3985463152e10dfad9ed6c09b641b6c1824498e77814a7c12e03096a63cd62056446358500951ed3ef2065e4196d008b50a63bb3e2bdc9a64df67eff4e230b35429291def476684114e074357a5c834bf79eacf583b6fe9fcd1d17f3719a31de97aa4da5e4630b05421359e0b6e4a9bd76c6b920b190929582010c865acec05c84c2c0d0b889f7dbe9bf3b5561f8552da1eb286eac4ccdabc5e5820d298da3803eb9958f01c02e73f2410f2f9bb2ecbc346526b1b76772e1bdd7db500005840940f8a3696847e4a238705bdd27a345086282067b9bc5cb7b69847ca8756085844d576f59ab056c169a504320cc1eab4c11fd529482b3c57da6fa96d44635b0802005901c0a1b2ee63b357fe0b19c6bb8dc3fc865c0608a89626505c5f9aff3b74a0809ca2635e0c4235c247306987c7fd76a4a06210ebf74178e72a1faa78fb8865a69005cc6a5ab5c9b40f817c715df558af7d07b6186f0ccf31715ec2fb00980730ac166af657e6670608afe1bf651d496e01b1c7ff1eb44614d8cfd1b7e32b2c2939349236cc0ada145d8d8d7ad919ef1e60c8bbad31dbedf9f395849705a00c14a8785106aae31f55abc5b1f2089cbef16d9401f158704c1e4f740f7125cfc700a99d97d0332eacb33e4bbc8dab2872ec2b3df9e113addaebd156bfc64fdfc732614d2aedd10a58a34993b7b08c822af3aa615b6bbb9b267bc902e4f1075e194aed084ca18f8bcde1a6b094bf3f5295a0d454c0a083ed5b74f7092fc0a7346c03979a30eeea76d686e512ba48d21544ba874886cdd166cbf275b11f1f3881f4c4277c09a24b88fc6168f4578267bdc9d62cb9b78b8dfc888ccce226a177725f39e7d50930552861d1e88b7898971c780dc3b773321ba1854422b5cecead7d50e77783050eeae2cd9595b9cd91681c72e5d53bb7d12f28dec9b2847ee70a3d7781fb1133aea3b169f536ff5945ec0a76950e51beded0627bb78120617a2f0842e50e3981ae0081825820ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25000d81825820bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa01018183583900cb9358529df4729c3246a2a033cb9821abbfd16de4888005904abc410d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb06821864a1581ca646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520aa14a636f75747473436f696e1903e85820ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25021903e70304048382008200581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb068a03581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb065820c5e21ab1c9f6022d81c3b25e3436cb7f1df77f9652ae3e1310c28e621dd87b4c0105d81e82010a581de00d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0681581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0680826e636f6e73656e7375732e706f6f6c427b7d82068200a18200581c008b47844d92812fc30d1f0ac9b6fbf38778ccba9db8312ad9079079186e05a1581de00d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0618640682a1581ce0a714319812c3f773ba04ec5d6b3ffcd5aad85006805b047b082541a104190fa00008020e81581cf81ce66e0f52da5ca48193386e7511fde5b030a307b4c3681736c6f009a1581cb16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5a14a636f75747473436f696e1903e80b58209e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b0758209e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b0f0181a400818258203b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da295840815671b581b4b02a30108a799a85c7f2e5487fb667e748e8fde59e466ab987ce133ecb77ffa0dc53c5804e6706e26b94e17803235da28112bc747de48ccbd70903814c4b0100002002002002000011048118bf058184000019039782191388191388a100d90103a300a40166737472696e67024562797465730382010204a10341620181820180028148470100002002006180").unwrap(); + let block = Block::from_bytes(bytes).unwrap(); + let block2 = Block::from_bytes(block.to_bytes()).unwrap(); + assert_eq!(block.to_bytes(), block2.to_bytes()); + } } \ No newline at end of file diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs index d75bd8a9..2adf60c9 100644 --- a/rust/src/tx_builder.rs +++ b/rust/src/tx_builder.rs @@ -85,8 +85,12 @@ fn min_fee(tx_builder: &TransactionBuilder) -> Result { }; let witness_set = TransactionWitnessSet { vkeys: vkeys, - scripts: script_keys, + native_scripts: script_keys, bootstraps: bootstrap_keys, + // TODO: plutus support? + plutus_scripts: None, + plutus_data: None, + redeemers: None, }; let full_tx = Transaction { body, From 817e256b9db4852aadce4938194dee451b3e159e Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Tue, 29 Jun 2021 16:06:52 -0500 Subject: [PATCH 07/11] fixes for wasm builds --- rust/Cargo.toml | 2 +- rust/src/lib.rs | 6 +++--- rust/src/plutus.rs | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index aec73a4d..3db3b7bf 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -38,7 +38,7 @@ noop_proc_macro = "0.3.0" # wasm [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] -wasm-bindgen = "=0.2.65" +wasm-bindgen = "=0.2.74" rand_os = { version = "0.1", features = ["wasm-bindgen"] } js-sys = "=0.3.24" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b753d8d1..7869f79c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -2240,13 +2240,13 @@ impl Block { self.invalid_transactions.clone() } - pub fn new(header: &Header, transaction_bodies: &TransactionBodies, transaction_witness_sets: &TransactionWitnessSets, auxiliary_data_set: &AuxiliaryDataSet, invalid_transactions: &TransactionIndexes) -> Self { + pub fn new(header: &Header, transaction_bodies: &TransactionBodies, transaction_witness_sets: &TransactionWitnessSets, auxiliary_data_set: &AuxiliaryDataSet, invalid_transactions: TransactionIndexes) -> Self { Self { header: header.clone(), transaction_bodies: transaction_bodies.clone(), transaction_witness_sets: transaction_witness_sets.clone(), auxiliary_data_set: auxiliary_data_set.clone(), - invalid_transactions: invalid_transactions.clone(), + invalid_transactions: invalid_transactions, } } } @@ -2694,7 +2694,7 @@ enum PreludeUnsignedEnum { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct PreludeUnsigned(PreludeUnsignedEnum); -to_from_bytes!(PreludeUnsignedEnum); +to_from_bytes!(PreludeUnsigned); #[wasm_bindgen] impl PreludeUnsigned { diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs index 3e2f5e71..133c3b2b 100644 --- a/rust/src/plutus.rs +++ b/rust/src/plutus.rs @@ -297,7 +297,7 @@ pub struct PlutusData(PlutusDataEnum); const PLUTUS_BYTES_MAX_LEN: usize = 64; -to_from_bytes!(PlutusDataEnum); +to_from_bytes!(PlutusData); #[wasm_bindgen] impl PlutusData { @@ -412,7 +412,7 @@ enum PreludeBigintEnum { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct PreludeBigint(PreludeBigintEnum); -to_from_bytes!(PreludeBigintEnum); +to_from_bytes!(PreludeBigint); #[wasm_bindgen] impl PreludeBigint { @@ -485,7 +485,7 @@ enum PreludeIntegerEnum { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct PreludeInteger(PreludeIntegerEnum); -to_from_bytes!(PreludeIntegerEnum); +to_from_bytes!(PreludeInteger); #[wasm_bindgen] impl PreludeInteger { @@ -580,7 +580,7 @@ enum RedeemerTagEnum { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct RedeemerTag(RedeemerTagEnum); -to_from_bytes!(RedeemerTagEnum); +to_from_bytes!(RedeemerTag); #[wasm_bindgen] impl RedeemerTag { @@ -1413,4 +1413,4 @@ impl Deserialize for Strings { })().map_err(|e| e.annotate("Strings"))?; Ok(Self(arr)) } -} \ No newline at end of file +} From 3b6854070cc8719c6f2f7e9c0c0660d5a30a859e Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Thu, 8 Jul 2021 01:04:08 -0500 Subject: [PATCH 08/11] Implement sane wrapper over all the plutus integer types CDDL defines: ``` int = uint / nint biguint = #6.2(bstr) bignint = #6.3(bstr) bigint = biguint / bignint integer = int / bigint unsigned = uint / biguint ``` which is very unwieldly to use and not to mention to have to interpret the bytes correctly and everything so this commit introduces a single wrapper type `BigInt` that covers all of these cases and tries to encode the integer in the smallest possible type. --- rust/Cargo.lock | 55 +++++-- rust/Cargo.toml | 1 + rust/src/lib.rs | 57 +------ rust/src/plutus.rs | 311 ++++---------------------------------- rust/src/serialization.rs | 50 +----- rust/src/utils.rs | 144 +++++++++++++++++- 6 files changed, 222 insertions(+), 396 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 0e4806cf..55f9a275 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -15,11 +15,17 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "bech32" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" +checksum = "cdcf67bb7ba7797a081cd19009948ab533af7c355d5caf1d08c777582d351e9c" [[package]] name = "bitflags" @@ -65,6 +71,7 @@ dependencies = [ "js-sys", "linked-hash-map", "noop_proc_macro", + "num-bigint", "quickcheck", "quickcheck_macros", "rand_chacha 0.1.1", @@ -124,9 +131,9 @@ checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" [[package]] name = "cryptoxide" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46212f5d1792f89c3e866fb10636139464060110c568edd7f73ab5e9f736c26d" +checksum = "b8c4fdc86023bc33b265f256ce8205329125b86c38a8a96e243a6a705b7230ec" [[package]] name = "curve25519-dalek" @@ -279,6 +286,36 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "num-bigint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg 1.0.1", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg 1.0.1", +] + [[package]] name = "opaque-debug" version = "0.3.0" @@ -293,9 +330,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ "unicode-xid", ] @@ -351,7 +388,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "autocfg", + "autocfg 0.1.7", "rand_core 0.3.1", ] @@ -490,9 +527,9 @@ checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd" [[package]] name = "syn" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" dependencies = [ "proc-macro2", "quote", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index df0854f8..bcad013b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -27,6 +27,7 @@ hex = "0.4.0" cfg-if = "1" linked-hash-map = "0.5.3" serde_json = "1.0.57" +num-bigint = "0.4.0" # The default can't be compiled to wasm, so it's necessary to use either the 'nightly' # feature or this one clear_on_drop = { version = "0.2", features = ["no_cc"] } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index ba23fbcb..f7bf445f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1918,7 +1918,7 @@ pub struct ProtocolParamUpdate { execution_costs: Option, max_tx_ex_units: Option, max_block_ex_units: Option, - max_value_size: Option, + max_value_size: Option, } to_from_bytes!(ProtocolParamUpdate); @@ -2093,11 +2093,11 @@ impl ProtocolParamUpdate { self.max_block_ex_units.clone() } - pub fn set_max_value_size(&mut self, max_value_size: &PreludeUnsigned) { + pub fn set_max_value_size(&mut self, max_value_size: u32) { self.max_value_size = Some(max_value_size.clone()) } - pub fn max_value_size(&self) -> Option { + pub fn max_value_size(&self) -> Option { self.max_value_size.clone() } @@ -2678,57 +2678,6 @@ impl NetworkId { } } -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum PreludeUnsignedKind { - U64, - PreludeBiguint, -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -enum PreludeUnsignedEnum { - U64(BigNum), - PreludeBiguint(PreludeBiguint), -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct PreludeUnsigned(PreludeUnsignedEnum); - -to_from_bytes!(PreludeUnsigned); - -#[wasm_bindgen] -impl PreludeUnsigned { - pub fn new_u64(uint: BigNum) -> Self { - Self(PreludeUnsignedEnum::U64(uint)) - } - - pub fn new_prelude_biguint(prelude_biguint: &PreludeBiguint) -> Self { - Self(PreludeUnsignedEnum::PreludeBiguint(prelude_biguint.clone())) - } - - pub fn kind(&self) -> PreludeUnsignedKind { - match &self.0 { - PreludeUnsignedEnum::U64(_) => PreludeUnsignedKind::U64, - PreludeUnsignedEnum::PreludeBiguint(_) => PreludeUnsignedKind::PreludeBiguint, - } - } - - pub fn as_u64(&self) -> Option { - match &self.0 { - PreludeUnsignedEnum::U64(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_prelude_biguint(&self) -> Option { - match &self.0 { - PreludeUnsignedEnum::PreludeBiguint(x) => Some(x.clone()), - _ => None, - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs index 133c3b2b..3a23c192 100644 --- a/rust/src/plutus.rs +++ b/rust/src/plutus.rs @@ -86,7 +86,7 @@ impl ConstrPlutusData { #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct CostModel(std::collections::BTreeMap); +pub struct CostModel(std::collections::BTreeMap); to_from_bytes!(CostModel); @@ -100,11 +100,11 @@ impl CostModel { self.0.len() } - pub fn insert(&mut self, key: String, value: &PreludeInteger) -> Option { + pub fn insert(&mut self, key: String, value: &BigInt) -> Option { self.0.insert(key, value.clone()) } - pub fn get(&self, key: String) -> Option { + pub fn get(&self, key: String) -> Option { self.0.get(&key).map(|v| v.clone()) } @@ -276,18 +276,18 @@ impl PlutusMap { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum PlutusDataKind { ConstrPlutusData, - PlutusMap, - PlutusList, - PreludeInteger, + Map, + List, + Integer, Bytes, } #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] enum PlutusDataEnum { ConstrPlutusData(ConstrPlutusData), - PlutusMap(PlutusMap), - PlutusList(PlutusList), - PreludeInteger(PreludeInteger), + Map(PlutusMap), + List(PlutusList), + Integer(BigInt), Bytes(Vec), } @@ -306,15 +306,15 @@ impl PlutusData { } pub fn new_map(map: &PlutusMap) -> Self { - Self(PlutusDataEnum::PlutusMap(map.clone())) + Self(PlutusDataEnum::Map(map.clone())) } pub fn new_list(list: &PlutusList) -> Self { - Self(PlutusDataEnum::PlutusList(list.clone())) + Self(PlutusDataEnum::List(list.clone())) } - pub fn new_integer(integer: &PreludeInteger) -> Self { - Self(PlutusDataEnum::PreludeInteger(integer.clone())) + pub fn new_integer(integer: &BigInt) -> Self { + Self(PlutusDataEnum::Integer(integer.clone())) } pub fn new_bytes(bytes: Vec) -> Result { @@ -328,9 +328,9 @@ impl PlutusData { pub fn kind(&self) -> PlutusDataKind { match &self.0 { PlutusDataEnum::ConstrPlutusData(_) => PlutusDataKind::ConstrPlutusData, - PlutusDataEnum::PlutusMap(_) => PlutusDataKind::PlutusMap, - PlutusDataEnum::PlutusList(_) => PlutusDataKind::PlutusList, - PlutusDataEnum::PreludeInteger(_) => PlutusDataKind::PreludeInteger, + PlutusDataEnum::Map(_) => PlutusDataKind::Map, + PlutusDataEnum::List(_) => PlutusDataKind::List, + PlutusDataEnum::Integer(_) => PlutusDataKind::Integer, PlutusDataEnum::Bytes(_) => PlutusDataKind::Bytes, } } @@ -344,21 +344,21 @@ impl PlutusData { pub fn as_map(&self) -> Option { match &self.0 { - PlutusDataEnum::PlutusMap(x) => Some(x.clone()), + PlutusDataEnum::Map(x) => Some(x.clone()), _ => None, } } pub fn as_list(&self) -> Option { match &self.0 { - PlutusDataEnum::PlutusList(x) => Some(x.clone()), + PlutusDataEnum::List(x) => Some(x.clone()), _ => None, } } - pub fn as_integer(&self) -> Option { + pub fn as_integer(&self) -> Option { match &self.0 { - PlutusDataEnum::PreludeInteger(x) => Some(x.clone()), + PlutusDataEnum::Integer(x) => Some(x.clone()), _ => None, } } @@ -394,131 +394,6 @@ impl PlutusList { } } -// TODO: replace these prelude ints with a generalized Integer class -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum PreludeBigintKind { - PreludeBiguint, - PreludeBignint, -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -enum PreludeBigintEnum { - PreludeBiguint(PreludeBiguint), - PreludeBignint(PreludeBignint), -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct PreludeBigint(PreludeBigintEnum); - -to_from_bytes!(PreludeBigint); - -#[wasm_bindgen] -impl PreludeBigint { - pub fn new_prelude_biguint(prelude_biguint: &PreludeBiguint) -> Self { - Self(PreludeBigintEnum::PreludeBiguint(prelude_biguint.clone())) - } - - pub fn new_prelude_bignint(prelude_bignint: &PreludeBignint) -> Self { - Self(PreludeBigintEnum::PreludeBignint(prelude_bignint.clone())) - } - - pub fn kind(&self) -> PreludeBigintKind { - match &self.0 { - PreludeBigintEnum::PreludeBiguint(_) => PreludeBigintKind::PreludeBiguint, - PreludeBigintEnum::PreludeBignint(_) => PreludeBigintKind::PreludeBignint, - } - } - - pub fn as_prelude_biguint(&self) -> Option { - match &self.0 { - PreludeBigintEnum::PreludeBiguint(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_prelude_bignint(&self) -> Option { - match &self.0 { - PreludeBigintEnum::PreludeBignint(x) => Some(x.clone()), - _ => None, - } - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct PreludeBignint(Vec); - -#[wasm_bindgen] -impl PreludeBignint { - pub fn new(data: Vec) -> Self { - Self(data) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct PreludeBiguint(Vec); - -#[wasm_bindgen] -impl PreludeBiguint { - pub fn new(data: Vec) -> Self { - Self(data) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum PreludeIntegerKind { - Int, - PreludeBigint, -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -enum PreludeIntegerEnum { - Int(Int), - PreludeBigint(PreludeBigint), -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct PreludeInteger(PreludeIntegerEnum); - -to_from_bytes!(PreludeInteger); - -#[wasm_bindgen] -impl PreludeInteger { - pub fn new_int(int: &Int) -> Self { - Self(PreludeIntegerEnum::Int(int.clone())) - } - - pub fn new_prelude_bigint(prelude_bigint: &PreludeBigint) -> Self { - Self(PreludeIntegerEnum::PreludeBigint(prelude_bigint.clone())) - } - - pub fn kind(&self) -> PreludeIntegerKind { - match &self.0 { - PreludeIntegerEnum::Int(_) => PreludeIntegerKind::Int, - PreludeIntegerEnum::PreludeBigint(_) => PreludeIntegerKind::PreludeBigint, - } - } - - pub fn as_int(&self) -> Option { - match &self.0 { - PreludeIntegerEnum::Int(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_prelude_bigint(&self) -> Option { - match &self.0 { - PreludeIntegerEnum::PreludeBigint(x) => Some(x.clone()), - _ => None, - } - } -} - #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct Redeemer { @@ -787,7 +662,7 @@ impl Deserialize for CostModel { break; } let key = String::deserialize(raw)?; - let value = PreludeInteger::deserialize(raw)?; + let value = BigInt::deserialize(raw)?; if table.insert(key.clone(), value).is_some() { return Err(DeserializeFailure::DuplicateKey(Key::Str(key)).into()); } @@ -991,13 +866,13 @@ impl cbor_event::se::Serialize for PlutusDataEnum { PlutusDataEnum::ConstrPlutusData(x) => { x.serialize(serializer) }, - PlutusDataEnum::PlutusMap(x) => { + PlutusDataEnum::Map(x) => { x.serialize(serializer) }, - PlutusDataEnum::PlutusList(x) => { + PlutusDataEnum::List(x) => { x.serialize(serializer) }, - PlutusDataEnum::PreludeInteger(x) => { + PlutusDataEnum::Integer(x) => { x.serialize(serializer) }, PlutusDataEnum::Bytes(x) => { @@ -1022,21 +897,21 @@ impl Deserialize for PlutusDataEnum { Ok(PlutusMap::deserialize(raw)?) })(raw) { - Ok(variant) => return Ok(PlutusDataEnum::PlutusMap(variant)), + Ok(variant) => return Ok(PlutusDataEnum::Map(variant)), Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), }; match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { Ok(PlutusList::deserialize(raw)?) })(raw) { - Ok(variant) => return Ok(PlutusDataEnum::PlutusList(variant)), + Ok(variant) => return Ok(PlutusDataEnum::List(variant)), Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), }; match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(PreludeInteger::deserialize(raw)?) + Ok(BigInt::deserialize(raw)?) })(raw) { - Ok(variant) => return Ok(PlutusDataEnum::PreludeInteger(variant)), + Ok(variant) => return Ok(PlutusDataEnum::Integer(variant)), Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), }; match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { @@ -1099,136 +974,6 @@ impl Deserialize for PlutusList { } } -impl cbor_event::se::Serialize for PreludeBigintEnum { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - match self { - PreludeBigintEnum::PreludeBiguint(x) => { - x.serialize(serializer) - }, - PreludeBigintEnum::PreludeBignint(x) => { - x.serialize(serializer) - }, - } - } -} - -impl Deserialize for PreludeBigintEnum { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(PreludeBiguint::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PreludeBigintEnum::PreludeBiguint(variant)), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(PreludeBignint::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PreludeBigintEnum::PreludeBignint(variant)), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - Err(DeserializeError::new("PreludeBigintEnum", DeserializeFailure::NoVariantMatched.into())) - })().map_err(|e| e.annotate("PreludeBigintEnum")) - } -} - -impl cbor_event::se::Serialize for PreludeBigint { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - self.0.serialize(serializer) - } -} - -impl Deserialize for PreludeBigint { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(PreludeBigintEnum::deserialize(raw)?)) - } -} - -impl cbor_event::se::Serialize for PreludeBignint { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_tag(3u64)?; - serializer.write_bytes(&self.0) - } -} - -impl Deserialize for PreludeBignint { - fn deserialize(raw: &mut Deserializer) -> Result { - let tag = raw.tag().map_err(|e| DeserializeError::from(e).annotate("PreludeBignint"))?; - if tag != 3 { - return Err(DeserializeError::new("PreludeBignint", DeserializeFailure::TagMismatch{ found: tag, expected: 3 })); - } - Ok(Self(raw.bytes()?)) - } -} - -impl cbor_event::se::Serialize for PreludeBiguint { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_tag(2u64)?; - serializer.write_bytes(&self.0) - } -} - -impl Deserialize for PreludeBiguint { - fn deserialize(raw: &mut Deserializer) -> Result { - let tag = raw.tag().map_err(|e| DeserializeError::from(e).annotate("PreludeBiguint"))?; - if tag != 2 { - return Err(DeserializeError::new("PreludeBiguint", DeserializeFailure::TagMismatch{ found: tag, expected: 2 })); - } - Ok(Self(raw.bytes()?)) - } -} - -impl cbor_event::se::Serialize for PreludeIntegerEnum { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - match self { - PreludeIntegerEnum::Int(x) => { - x.serialize(serializer) - }, - PreludeIntegerEnum::PreludeBigint(x) => { - x.serialize(serializer) - }, - } - } -} - -impl Deserialize for PreludeIntegerEnum { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(Int::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PreludeIntegerEnum::Int(variant)), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(PreludeBigint::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PreludeIntegerEnum::PreludeBigint(variant)), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - Err(DeserializeError::new("PreludeIntegerEnum", DeserializeFailure::NoVariantMatched.into())) - })().map_err(|e| e.annotate("PreludeIntegerEnum")) - } -} - -impl cbor_event::se::Serialize for PreludeInteger { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - self.0.serialize(serializer) - } -} - -impl Deserialize for PreludeInteger { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(PreludeIntegerEnum::deserialize(raw)?)) - } -} - impl cbor_event::se::Serialize for Redeemer { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { serializer.write_array(cbor_event::Len::Len(4))?; diff --git a/rust/src/serialization.rs b/rust/src/serialization.rs index 245746df..e4d48984 100644 --- a/rust/src/serialization.rs +++ b/rust/src/serialization.rs @@ -2839,7 +2839,7 @@ impl Deserialize for ProtocolParamUpdate { } max_value_size = Some((|| -> Result<_, DeserializeError> { read_len.read_elems(1)?; - Ok(PreludeUnsigned::deserialize(raw)?) + Ok(u32::deserialize(raw)?) })().map_err(|e| e.annotate("max_value_size"))?); }, unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), @@ -3435,54 +3435,6 @@ impl Deserialize for NetworkId { } } -impl cbor_event::se::Serialize for PreludeUnsignedEnum { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - match self { - PreludeUnsignedEnum::U64(x) => { - x.serialize(serializer) - }, - PreludeUnsignedEnum::PreludeBiguint(x) => { - x.serialize(serializer) - }, - } - } -} - -impl Deserialize for PreludeUnsignedEnum { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(BigNum::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PreludeUnsignedEnum::U64(variant)), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(PreludeBiguint::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PreludeUnsignedEnum::PreludeBiguint(variant)), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - Err(DeserializeError::new("PreludeUnsignedEnum", DeserializeFailure::NoVariantMatched.into())) - })().map_err(|e| e.annotate("PreludeUnsignedEnum")) - } -} - -impl cbor_event::se::Serialize for PreludeUnsigned { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - self.0.serialize(serializer) - } -} - -impl Deserialize for PreludeUnsigned { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(PreludeUnsignedEnum::deserialize(raw)?)) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/rust/src/utils.rs b/rust/src/utils.rs index 93da08f6..0c822ca5 100644 --- a/rust/src/utils.rs +++ b/rust/src/utils.rs @@ -411,6 +411,107 @@ impl Deserialize for Int { } } +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct BigInt(num_bigint::BigInt); + +to_from_bytes!(BigInt); + +#[wasm_bindgen] +impl BigInt { + pub fn as_u64(&self) -> Option { + let (sign, u64_digits) = self.0.to_u64_digits(); + if sign == num_bigint::Sign::Minus { + return None; + } + match u64_digits.len() { + 1 => Some(to_bignum(*u64_digits.first().unwrap())), + _ => None, + } + } + + pub fn from_str(text: &str) -> Result { + use std::str::FromStr; + num_bigint::BigInt::from_str(text) + .map_err(|e| JsError::from_str(&format! {"{:?}", e})) + .map(BigInt) + } + + pub fn to_str(&self) -> String { + self.0.to_string() + } +} + +impl cbor_event::se::Serialize for BigInt { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + let (sign, u64_digits) = self.0.to_u64_digits(); + // we use the uint/nint encodings to use a minimum of space + if u64_digits.len() == 1 { + match sign { + // uint + num_bigint::Sign::Plus | + num_bigint::Sign::NoSign => serializer.write_unsigned_integer(*u64_digits.first().unwrap())?, + // nint + num_bigint::Sign::Minus => serializer.write_negative_integer(-(*u64_digits.first().unwrap() as i128) as i64)?, + }; + } else { + let (sign, bytes) = self.0.to_bytes_be(); + match sign { + // positive bigint + num_bigint::Sign::Plus | + num_bigint::Sign::NoSign => { + serializer.write_tag(2u64)?; + serializer.write_bytes(bytes)?; + }, + // negative bigint + num_bigint::Sign::Minus => { + serializer.write_tag(3u64)?; + use std::ops::Neg; + // CBOR RFC defines this as the bytes of -n -1 + let adjusted = self.0.clone().neg().checked_sub(&num_bigint::BigInt::from(1u32)).unwrap().to_biguint().unwrap(); + serializer.write_bytes(adjusted.to_bytes_be())?; + }, + } + } + Ok(serializer) + } +} + +impl Deserialize for BigInt { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.cbor_type()? { + // bigint + CBORType::Tag => { + let tag = raw.tag()?; + let bytes = raw.bytes()?; + if bytes.len() > 64 { + return Err(DeserializeFailure::OutOfRange{ found: bytes.len(), min: 0, max: 64}.into()) + } + match tag { + // positive bigint + 2 => Ok(Self(num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes))), + // negative bigint + 3 => { + // CBOR RFC defines this as the bytes of -n -1 + let initial = num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes); + use std::ops::Neg; + let adjusted = initial.checked_add(&num_bigint::BigInt::from(1u32)).unwrap().neg(); + Ok(Self(adjusted)) + }, + _ => return Err(DeserializeFailure::TagMismatch{ found: tag, expected: 2 }.into()), + } + }, + // uint + CBORType::UnsignedInteger => Ok(Self(num_bigint::BigInt::from(raw.unsigned_integer()?))), + // nint + CBORType::NegativeInteger => Ok(Self(num_bigint::BigInt::from(raw.negative_integer()?))), + _ => return Err(DeserializeFailure::NoVariantMatched.into()), + } + })().map_err(|e| e.annotate("BigInt")) + } +} + // we use the cbor_event::Serialize trait directly // This is only for use for plain cddl groups who need to be embedded within outer groups. @@ -1454,4 +1555,45 @@ mod tests { assert_eq!(a.partial_cmp(&b), None); } } -} \ No newline at end of file + + #[test] + fn bigint_serialization() { + let zero = BigInt::from_str("0").unwrap(); + let zero_rt = BigInt::from_bytes(zero.to_bytes()).unwrap(); + assert_eq!(zero.to_str(), zero_rt.to_str()); + + let pos_small = BigInt::from_str("100").unwrap(); + let pos_small_rt = BigInt::from_bytes(pos_small.to_bytes()).unwrap(); + assert_eq!(pos_small.to_str(), pos_small_rt.to_str()); + + let pos_big = BigInt::from_str("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap(); + let pos_big_rt = BigInt::from_bytes(pos_big.to_bytes()).unwrap(); + assert_eq!(pos_big.to_str(), pos_big_rt.to_str()); + + let neg_small = BigInt::from_str("-100").unwrap(); + let neg_small_rt = BigInt::from_bytes(neg_small.to_bytes()).unwrap(); + assert_eq!(neg_small.to_str(), neg_small_rt.to_str()); + + let neg_big = BigInt::from_str("-123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap(); + let neg_big_rt = BigInt::from_bytes(neg_big.to_bytes()).unwrap(); + assert_eq!(neg_big.to_str(), neg_big_rt.to_str()); + + // taken from CBOR RFC examples + // negative big int + assert_eq!(hex::decode("c349010000000000000000").unwrap(), BigInt::from_str("-18446744073709551617").unwrap().to_bytes()); + // positive big int + assert_eq!(hex::decode("c249010000000000000000").unwrap(), BigInt::from_str("18446744073709551616").unwrap().to_bytes()); + // uint + assert_eq!(hex::decode("1b000000e8d4a51000").unwrap(), BigInt::from_str("1000000000000").unwrap().to_bytes()); + // nint + // we can't use this due to cbor_event actually not supporting the full NINT spectrum as it uses an i64 for some reason... + //assert_eq!(hex::decode("3bffffffffffffffff").unwrap(), BigInt::from_str("-18446744073709551616").unwrap().to_bytes()); + // this one fits in an i64 though + assert_eq!(hex::decode("3903e7").unwrap(), BigInt::from_str("-1000").unwrap().to_bytes()); + + + let x = BigInt::from_str("-18446744073709551617").unwrap(); + let x_rt = BigInt::from_bytes(x.to_bytes()).unwrap(); + assert_eq!(x.to_str(), x_rt.to_str()); + } +} From 110e5d873756c47fc7386534f0c65748017b87ac Mon Sep 17 00:00:00 2001 From: stackchain <30806844+stackchain@users.noreply.github.com> Date: Tue, 13 Jul 2021 22:22:02 -0300 Subject: [PATCH 09/11] [fix] Setter typo --- rust/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index ba23fbcb..8616d3ea 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -273,7 +273,7 @@ impl TransactionBody { self.update.clone() } - pub fn set_metadata_hash(&mut self, auxiliary_data_hash: &AuxiliaryDataHash) { + pub fn set_auxiliary_data_hash(&mut self, auxiliary_data_hash: &AuxiliaryDataHash) { self.auxiliary_data_hash = Some(auxiliary_data_hash.clone()) } From 03c23ddd53df04f79a40b9ec562babd8c9b75a3f Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Fri, 16 Jul 2021 18:19:27 -0500 Subject: [PATCH 10/11] Update to recent change in ex_unit_prices cddl definition --- rust/src/lib.rs | 1 + rust/src/plutus.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index ba23fbcb..5ce13b95 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -87,6 +87,7 @@ impl UnitInterval { } } +type SubCoin = UnitInterval; type Rational = UnitInterval; type Epoch = u32; type Slot = u32; diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs index 133c3b2b..742ef562 100644 --- a/rust/src/plutus.rs +++ b/rust/src/plutus.rs @@ -145,23 +145,23 @@ impl Costmdls { #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct ExUnitPrices { - mem_price: Coin, - step_price: Coin, + mem_price: SubCoin, + step_price: SubCoin, } to_from_bytes!(ExUnitPrices); #[wasm_bindgen] impl ExUnitPrices { - pub fn mem_price(&self) -> Coin { + pub fn mem_price(&self) -> SubCoin { self.mem_price.clone() } - pub fn step_price(&self) -> Coin { + pub fn step_price(&self) -> SubCoin { self.step_price.clone() } - pub fn new(mem_price: &Coin, step_price: &Coin) -> Self { + pub fn new(mem_price: &SubCoin, step_price: &SubCoin) -> Self { Self { mem_price: mem_price.clone(), step_price: step_price.clone(), @@ -847,10 +847,10 @@ impl Deserialize for ExUnitPrices { let mut read_len = CBORReadLen::new(len); read_len.read_elems(2)?; let mem_price = (|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) + Ok(SubCoin::deserialize(raw)?) })().map_err(|e| e.annotate("mem_price"))?; let step_price = (|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) + Ok(SubCoin::deserialize(raw)?) })().map_err(|e| e.annotate("step_price"))?; match len { cbor_event::Len::Len(_) => (), From 6d41c221137c7b776a7edd2f755b5e213c4e44e4 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Fri, 16 Jul 2021 18:27:01 -0500 Subject: [PATCH 11/11] Get rid of RedeemerTagEnum --- rust/src/plutus.rs | 95 ++++++++++------------------------------------ 1 file changed, 21 insertions(+), 74 deletions(-) diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs index 742ef562..ba84c9d4 100644 --- a/rust/src/plutus.rs +++ b/rust/src/plutus.rs @@ -559,7 +559,7 @@ impl Redeemer { } #[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum RedeemerTagKind { Spend, Mint, @@ -567,46 +567,32 @@ pub enum RedeemerTagKind { Reward, } -// TODO: simplify these two into one struct if possible: see NetworkId -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -enum RedeemerTagEnum { - Spend, - Mint, - Cert, - Reward, -} - #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct RedeemerTag(RedeemerTagEnum); +pub struct RedeemerTag(RedeemerTagKind); to_from_bytes!(RedeemerTag); #[wasm_bindgen] impl RedeemerTag { pub fn new_spend() -> Self { - Self(RedeemerTagEnum::Spend) + Self(RedeemerTagKind::Spend) } pub fn new_mint() -> Self { - Self(RedeemerTagEnum::Mint) + Self(RedeemerTagKind::Mint) } pub fn new_cert() -> Self { - Self(RedeemerTagEnum::Cert) + Self(RedeemerTagKind::Cert) } pub fn new_reward() -> Self { - Self(RedeemerTagEnum::Reward) + Self(RedeemerTagKind::Reward) } pub fn kind(&self) -> RedeemerTagKind { - match &self.0 { - RedeemerTagEnum::Spend => RedeemerTagKind::Spend, - RedeemerTagEnum::Mint => RedeemerTagKind::Mint, - RedeemerTagEnum::Cert => RedeemerTagKind::Cert, - RedeemerTagEnum::Reward => RedeemerTagKind::Reward, - } + self.0 } } @@ -1275,74 +1261,35 @@ impl Deserialize for Redeemer { } } -impl cbor_event::se::Serialize for RedeemerTagEnum { +impl cbor_event::se::Serialize for RedeemerTagKind { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { match self { - RedeemerTagEnum::Spend => { + RedeemerTagKind::Spend => { serializer.write_unsigned_integer(0u64) }, - RedeemerTagEnum::Mint => { + RedeemerTagKind::Mint => { serializer.write_unsigned_integer(1u64) }, - RedeemerTagEnum::Cert => { + RedeemerTagKind::Cert => { serializer.write_unsigned_integer(2u64) }, - RedeemerTagEnum::Reward => { + RedeemerTagKind::Reward => { serializer.write_unsigned_integer(3u64) }, } } } -impl Deserialize for RedeemerTagEnum { +impl Deserialize for RedeemerTagKind { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - let i0_value = raw.unsigned_integer()?; - if i0_value != 0 { - return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i0_value), expected: Key::Uint(0) }.into()); - } - Ok(()) - })(raw) - { - Ok(()) => return Ok(RedeemerTagEnum::Spend), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - let i1_value = raw.unsigned_integer()?; - if i1_value != 1 { - return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i1_value), expected: Key::Uint(1) }.into()); - } - Ok(()) - })(raw) - { - Ok(()) => return Ok(RedeemerTagEnum::Mint), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - let i2_value = raw.unsigned_integer()?; - if i2_value != 2 { - return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i2_value), expected: Key::Uint(2) }.into()); - } - Ok(()) - })(raw) - { - Ok(()) => return Ok(RedeemerTagEnum::Cert), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - let i3_value = raw.unsigned_integer()?; - if i3_value != 3 { - return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i3_value), expected: Key::Uint(3) }.into()); - } - Ok(()) - })(raw) - { - Ok(()) => return Ok(RedeemerTagEnum::Reward), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - Err(DeserializeError::new("RedeemerTagEnum", DeserializeFailure::NoVariantMatched.into())) + match raw.unsigned_integer() { + Ok(0) => Ok(RedeemerTagKind::Spend), + Ok(1) => Ok(RedeemerTagKind::Mint), + Ok(2) => Ok(RedeemerTagKind::Cert), + Ok(3) => Ok(RedeemerTagKind::Reward), + Ok(_) | Err(_) => Err(DeserializeFailure::NoVariantMatched.into()), + } })().map_err(|e| e.annotate("RedeemerTagEnum")) } } @@ -1355,7 +1302,7 @@ impl cbor_event::se::Serialize for RedeemerTag { impl Deserialize for RedeemerTag { fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(RedeemerTagEnum::deserialize(raw)?)) + Ok(Self(RedeemerTagKind::deserialize(raw)?)) } }