diff --git a/.gitignore b/.gitignore index 1a1f6a6..1f902d2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ result .direnv .pre-commit-config.yaml .DS_Store +target +.extras diff --git a/flake.nix b/flake.nix index 4f5785b..8d68e4d 100644 --- a/flake.nix +++ b/flake.nix @@ -23,6 +23,7 @@ ./hercules-ci.nix ./plutus-ledger-api/build.nix + ./is-plutus-data-derive/build.nix ]; debug = true; systems = [ "x86_64-linux" "x86_64-darwin" ]; diff --git a/is-plutus-data-derive/.envrc b/is-plutus-data-derive/.envrc new file mode 100644 index 0000000..f7f6ae5 --- /dev/null +++ b/is-plutus-data-derive/.envrc @@ -0,0 +1 @@ +use flake ../#dev-is-plutus-data-derive-rust diff --git a/is-plutus-data-derive/Cargo.lock b/is-plutus-data-derive/Cargo.lock new file mode 100644 index 0000000..00cbc00 --- /dev/null +++ b/is-plutus-data-derive/Cargo.lock @@ -0,0 +1,68 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "is-plutus-data-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "thiserror", +] + +[[package]] +name = "proc-macro2" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/is-plutus-data-derive/Cargo.toml b/is-plutus-data-derive/Cargo.toml new file mode 100644 index 0000000..770f519 --- /dev/null +++ b/is-plutus-data-derive/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "is-plutus-data-derive" +version = "0.1.0" +edition = "2021" + +[dependencies] +proc-macro2 = "^1.0.66" +quote = "1.0.37" +syn = { version = "2.0.79", features = ["full", "extra-traits"]} +thiserror = "1.0.64" + +[lib] +proc-macro = true diff --git a/is-plutus-data-derive/build.nix b/is-plutus-data-derive/build.nix new file mode 100644 index 0000000..ac6ecba --- /dev/null +++ b/is-plutus-data-derive/build.nix @@ -0,0 +1,18 @@ +{ inputs, ... }: { + perSystem = { config, system, ... }: + let + rustFlake = + inputs.flake-lang.lib.${system}.rustFlake { + src = ./.; + version = "0"; + crateName = "is-plutus-data-derive"; + devShellHook = config.settings.shell.hook; + cargoNextestExtraArgs = "--all-features"; + generateDocs = false; + }; + + in + { + inherit (rustFlake) packages checks devShells; + }; +} diff --git a/is-plutus-data-derive/src/derive_impl.rs b/is-plutus-data-derive/src/derive_impl.rs new file mode 100644 index 0000000..7195196 --- /dev/null +++ b/is-plutus-data-derive/src/derive_impl.rs @@ -0,0 +1,695 @@ +use std::str::FromStr; + +use quote::format_ident; +use syn::{ + parse::{Parse, ParseStream}, + parse_quote, + spanned::Spanned, + Arm, Attribute, Block, Data, DataEnum, DataStruct, DeriveInput, Error, Expr, ExprLit, Fields, + FieldsNamed, FieldsUnnamed, Ident, Index, ItemImpl, Lit, Meta, Path, Result, Stmt, +}; + +pub(crate) fn get_is_plutus_data_instance(input: DeriveInput) -> Result { + let type_name = &input.ident; + + let strategy = get_derive_strategy(&input)?; + + let plutus_data_input_var: Ident = parse_quote!(plutus_data); + + let (encoder, decoder) = match strategy { + DeriveStrategy::Newtype => get_newtype_encoder_decoder(&input), + DeriveStrategy::List => get_list_encoder_decoder(&input, &plutus_data_input_var), + DeriveStrategy::Constr => get_constr_encoder_decoder(&input, &plutus_data_input_var), + }?; + + let mut generics = input.generics; + + // TODO(chfanghr): Do we care about type role? Can we determine it? + generics.type_params_mut().for_each(|param| { + param.bounds.push(parse_quote!(plutus_data::IsPlutusData)); + }); + + let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); + + Ok(parse_quote!( + impl #impl_generics plutus_ledger_api::plutus_data::IsPlutusData for #type_name #type_generics #where_clause { + fn to_plutus_data(&self) -> plutus_ledger_api::plutus_data::PlutusData { + #encoder + } + + fn from_plutus_data(plutus_data: &plutus_ledger_api::plutus_data::PlutusData) -> Result + where Self: Sized { + #decoder + } + } + )) +} + +#[derive(Debug)] +enum DeriveStrategy { + Newtype, + List, + Constr, +} + +#[derive(Debug, thiserror::Error)] +enum DeriveStrategyError { + #[error("Unknown strategy {0}. Should be one of Newtype, List and Constr.")] + UnknownStrategy(String), + #[error("Unable to parse strategy. Should be a string literal Newtype, Constr or List.")] + UnexpectedToken, + #[error("More than one strategies specified.")] + MoreThanOneSpecified, +} + +impl Default for DeriveStrategy { + fn default() -> Self { + Self::Constr + } +} + +impl FromStr for DeriveStrategy { + type Err = DeriveStrategyError; + + fn from_str(s: &str) -> std::result::Result { + match s { + "Newtype" => Ok(Self::Newtype), + "List" => Ok(Self::List), + "Constr" => Ok(Self::Constr), + _ => Err(DeriveStrategyError::UnknownStrategy(s.into())), + } + } +} +impl Parse for DeriveStrategy { + fn parse(input: ParseStream) -> Result { + let ident = input.call(Ident::parse)?; + Self::from_str(&ident.to_string()).map_err(|unknown_strategy| { + Error::new( + ident.span(), + format!("unknown strategy: {}", unknown_strategy), + ) + }) + } +} + +fn try_parse_derive_strategy(attr: &Attribute) -> Option> { + let value = match &attr.meta { + Meta::NameValue(name_value) => name_value + .path + .is_ident("is_plutus_data_derive_strategy") + .then_some(&name_value.value), + _ => None, + }?; + + Some(match &value { + Expr::Lit(ExprLit { + lit: Lit::Str(str_lit), + .. + }) => (|| -> Result { + DeriveStrategy::from_str(&str_lit.value()).map_err(|err| Error::new(attr.span(), err)) + })(), + _ => Err(Error::new( + value.span(), + DeriveStrategyError::UnexpectedToken, + )), + }) +} + +fn get_derive_strategy(input: &DeriveInput) -> Result { + let mut derive_strategy_results: Vec<_> = input + .attrs + .iter() + .map(try_parse_derive_strategy) + .flatten() + .collect(); + + match derive_strategy_results.len() { + 0 => Ok(DeriveStrategy::default()), + 1 => derive_strategy_results.remove(0), + _ => Err(Error::new( + input.span(), + DeriveStrategyError::MoreThanOneSpecified, + )), + } +} + +#[derive(Debug, thiserror::Error)] +enum NewtypeStrategyError { + #[error("Only struct types are supported by newtype strategy")] + UnexpectedDataVariant, + #[error("Newtype derivation expects exactly one filed")] + NotSingleField, +} + +fn get_newtype_encoder_decoder(input: &DeriveInput) -> Result<(Block, Block)> { + let s = match &input.data { + Data::Struct(s) => Ok(s), + _ => Err(Error::new( + input.span(), + NewtypeStrategyError::UnexpectedDataVariant, + )), + }?; + + if s.fields.len() != 1 { + Err(Error::new( + input.span(), + NewtypeStrategyError::NotSingleField, + ))? + } + + let field = s.fields.iter().next().unwrap(); + + let encoder = match &field.ident { + None => parse_quote!({ self.0.to_plutus_data() }), + Some(ident) => parse_quote!({ + self.#ident.to_plutus_data() + }), + }; + + let decoder = match &field.ident { + Some(field_name) => { + parse_quote!({ + Ok(Self { + #field_name: plutus_ledger_api::plutus_data::IsPlutusData::from_plutus_data(plutus_data)? + }) + }) + } + None => { + parse_quote!({ + Ok(Self( + plutus_ledger_api::plutus_data::IsPlutusData::from_plutus_data(plutus_data)?, + )) + }) + } + }; + + Ok((encoder, decoder)) +} + +#[derive(Debug, thiserror::Error)] +enum ListStrategyError { + #[error("Only struct types are supported by list strategy")] + UnexpectedDataVariant, +} + +fn get_list_encoder_decoder( + input: &DeriveInput, + plutus_data_input_var: &Ident, +) -> Result<(Block, Block)> { + match &input.data { + Data::Struct(s) => match &s.fields { + Fields::Named(fields_named) => Ok(( + struct_with_named_fields_to_plutus_data_list(fields_named), + struct_with_named_fields_from_plutus_data_list(fields_named, plutus_data_input_var), + )), + Fields::Unnamed(fields_unnamed) => Ok(( + struct_with_unnamed_fields_to_plutus_data_list(fields_unnamed), + struct_with_unnamed_fields_from_plutus_data_list( + fields_unnamed, + plutus_data_input_var, + ), + )), + Fields::Unit => Ok(( + struct_with_no_field_to_plutus_data_list(), + struct_with_no_field_from_plutus_data_list(plutus_data_input_var), + )), + }, + _ => Err(Error::new( + input.span(), + ListStrategyError::UnexpectedDataVariant, + )), + } +} + +#[derive(Debug, thiserror::Error)] +enum ConstrStrategyError { + #[error("Union types are supported by constr strategy")] + UnexpectedDataVariant, +} + +fn get_constr_encoder_decoder( + input: &DeriveInput, + plutus_data_input_var: &Ident, +) -> Result<(Block, Block)> { + Ok(match &input.data { + Data::Enum(e) => get_enum_constr_encoder_decoder(e, plutus_data_input_var), + Data::Struct(s) => get_struct_constr_encoder_decoder(s, plutus_data_input_var), + _ => Err(Error::new( + input.span(), + ConstrStrategyError::UnexpectedDataVariant, + ))?, + }) +} + +fn get_enum_constr_encoder_decoder(e: &DataEnum, plutus_data_input_var: &Ident) -> (Block, Block) { + ( + enum_to_plutus_data_constr(&e), + enum_from_plutus_data_constr(&e, plutus_data_input_var), + ) +} + +fn get_struct_constr_encoder_decoder( + s: &DataStruct, + plutus_data_input_var: &Ident, +) -> (Block, Block) { + match &s.fields { + Fields::Named(fields_named) => ( + struct_with_named_fields_to_plutus_data_constr(fields_named), + struct_with_named_fields_from_plutus_data_constr(fields_named, plutus_data_input_var), + ), + Fields::Unnamed(fields_unnamed) => ( + struct_with_unnamed_fields_to_plutus_data_constr(fields_unnamed), + struct_with_unnamed_fields_from_plutus_data_constr( + fields_unnamed, + plutus_data_input_var, + ), + ), + Fields::Unit => ( + struct_with_no_field_to_plutus_data_constr(), + struct_with_no_field_from_plutus_data_constr(plutus_data_input_var), + ), + } +} + +fn enum_to_plutus_data_constr(e: &DataEnum) -> Block { + let variants = &e.variants; + let tags = 0..variants.len(); + + let arms = tags.zip(variants.iter()).map(|(tag, variant)| { + let variant_name = &variant.ident; + let constructor: Path = parse_quote!(Self::#variant_name); + let fields = &variant.fields; + variant_to_plutus_data(&constructor, tag, fields) + }); + + parse_quote!({ + match &self { + #(#arms),* + } + }) +} + +fn enum_from_plutus_data_constr(e: &DataEnum, plutus_data_input_var: &Ident) -> Block { + let variants = &e.variants; + let tags = 0..variants.len(); + let expected_tags_str = String::from("Constr with tag: ") + + &tags + .clone() + .map(|t| t.to_string()) + .collect::>() + .join("/"); + let plutus_data_list_var: Ident = parse_quote!(plutus_data_list); + + let arms = tags.zip(variants.iter()).map(|(tag, variant)| { + let variant_name = &variant.ident; + let constructor: Path = parse_quote!(Self::#variant_name); + let fields = &variant.fields; + + variant_from_plutus_data(&constructor, tag, fields, &plutus_data_list_var) + }); + + parse_quote!( + { + let (tag, #plutus_data_list_var) = plutus_ledger_api::plutus_data::parse_constr(#plutus_data_input_var)?; + + match tag { + #(#arms),* + tag => Err(plutus_ledger_api::plutus_data::PlutusDataError::UnexpectedPlutusInvariant { + wanted: format!(#expected_tags_str), + got: tag.to_string(), + }), + } + } + ) +} + +fn variant_to_plutus_data(constructor: &Path, tag: usize, fields: &Fields) -> Arm { + match fields { + Fields::Named(named) => variant_with_named_fields_to_plutus_data(&constructor, tag, &named), + Fields::Unnamed(unnamed) => { + variant_with_unnamed_field_to_plutus_data(&constructor, tag, &unnamed) + } + Fields::Unit => variant_with_no_field_to_plutus_data(&constructor, tag), + } +} + +fn variant_from_plutus_data( + constructor: &Path, + tag: usize, + fields: &Fields, + plutus_data_list_var: &Ident, +) -> Arm { + let block = match fields { + Fields::Named(named) => variant_with_named_fields_from_plutus_data_list( + constructor, + named, + plutus_data_list_var, + ), + Fields::Unnamed(unnamed) => variant_with_unnamed_fields_from_plutus_data_list( + constructor, + unnamed, + plutus_data_list_var, + ), + Fields::Unit => { + variant_with_no_field_from_plutus_data_list(constructor, plutus_data_list_var) + } + }; + + let tag = tag as u32; + + parse_quote!( + #tag => #block + ) +} + +fn variant_with_named_fields_to_plutus_data( + constructor: &Path, + tag: usize, + fields_named: &FieldsNamed, +) -> Arm { + let field_names = fields_named + .named + .iter() + .map(|field| field.ident.as_ref().unwrap()); + + let field_accessors = field_names + .clone() + .map(|field_name| -> Expr { parse_quote!(#field_name) }) + .collect::>(); + + let plutus_data_list = data_fields_to_list_of_plutus_data(&field_accessors); + + parse_quote!( + #constructor{ #(#field_names),* } => plutus_ledger_api::plutus_data::PlutusData::Constr(#tag.into(), #plutus_data_list) + ) +} + +fn variant_with_named_fields_from_plutus_data_list( + constructor: &Path, + fields_named: &FieldsNamed, + plutus_data_list_var: &Ident, +) -> Block { + data_with_named_fields_from_list_of_plutus_data(constructor, fields_named, plutus_data_list_var) +} + +fn variant_with_unnamed_field_to_plutus_data( + constructor: &Path, + tag: usize, + fields_unnamed: &FieldsUnnamed, +) -> Arm { + let field_names = (0..fields_unnamed.unnamed.len()).map(|idx| format_ident!("field_{}", idx)); + + let field_accessors = field_names + .clone() + .map(|field_name| -> Expr { parse_quote!(#field_name) }) + .collect::>(); + + let plutus_data_list = data_fields_to_list_of_plutus_data(&field_accessors); + + parse_quote!( + #constructor(#(#field_names),*) => plutus_ledger_api::plutus_data::PlutusData::Constr(#tag.into(), #plutus_data_list) + ) +} + +fn variant_with_unnamed_fields_from_plutus_data_list( + constructor: &Path, + fields_unnamed: &FieldsUnnamed, + plutus_data_list_var: &Ident, +) -> Block { + data_with_unnamed_fields_from_list_of_plutus_data( + constructor, + fields_unnamed, + plutus_data_list_var, + ) +} + +fn variant_with_no_field_to_plutus_data(constructor: &Path, tag: usize) -> Arm { + parse_quote!( + #constructor => plutus_ledger_api::plutus_data::PlutusData::Constr(#tag.into(), vec![]) + ) +} + +fn variant_with_no_field_from_plutus_data_list( + constructor: &Path, + plutus_data_list_var: &Ident, +) -> Block { + data_with_no_fields_from_list_of_plutus_data(constructor, plutus_data_list_var) +} + +fn struct_with_named_fields_to_list_of_plutus_data(fields: &FieldsNamed) -> Block { + let field_accessors = fields + .named + .iter() + .map(|field| -> Expr { + let field_name = field.ident.as_ref().unwrap(); + + parse_quote!(self.#field_name) + }) + .collect::>(); + + data_fields_to_list_of_plutus_data(&field_accessors) +} + +fn struct_with_named_fields_from_list_of_plutus_data( + fields: &FieldsNamed, + plutus_data_list_var: &Ident, +) -> Block { + let constructor: Path = parse_quote!(Self); + + data_with_named_fields_from_list_of_plutus_data(&constructor, fields, &plutus_data_list_var) +} + +fn struct_with_named_fields_to_plutus_data_list(fields: &FieldsNamed) -> Block { + let to_list_of_plutus_data = struct_with_named_fields_to_list_of_plutus_data(fields); + + parse_quote!({ + plutus_ledger_api::plutus_data::PlutusData::List(#to_list_of_plutus_data) + }) +} + +fn struct_with_named_fields_from_plutus_data_list( + fields: &FieldsNamed, + plutus_data_input_var: &Ident, +) -> Block { + let list_of_plutus_data_var: Ident = parse_quote!(list_of_plutus_data); + + let from_list_of_plutus_data = + struct_with_named_fields_from_list_of_plutus_data(fields, &list_of_plutus_data_var); + + parse_quote!({ + let #list_of_plutus_data_var = plutus_ledger_api::plutus_data::parse_list(#plutus_data_input_var)?; + + #from_list_of_plutus_data + }) +} + +fn struct_with_named_fields_to_plutus_data_constr(fields: &FieldsNamed) -> Block { + let to_list_of_plutus_data = struct_with_named_fields_to_list_of_plutus_data(fields); + + parse_quote!({ + plutus_ledger_api::plutus_data::PlutusData::Constr(0.into(), #to_list_of_plutus_data) + }) +} + +fn struct_with_named_fields_from_plutus_data_constr( + fields: &FieldsNamed, + plutus_data_input_var: &Ident, +) -> Block { + let plutus_data_list_var: Ident = parse_quote!(plutus_data_list); + + let from_plutus_data_list = + struct_with_named_fields_from_list_of_plutus_data(fields, &plutus_data_list_var); + + parse_quote!({ + let #plutus_data_list_var = plutus_ledger_api::plutus_data::parse_constr_with_tag(#plutus_data_input_var, 0)?; + + #from_plutus_data_list + }) +} + +fn struct_with_unnamed_fields_to_list_of_plutus_data(fields: &FieldsUnnamed) -> Block { + let len = fields.unnamed.len(); + + let field_accessors = (0..len) + .into_iter() + .map(|idx| -> Expr { + let idx: Index = idx.into(); + + parse_quote!(self.#idx) + }) + .collect::>(); + + data_fields_to_list_of_plutus_data(&field_accessors) +} + +fn struct_with_unnamed_fields_from_list_of_plutus_data( + fields: &FieldsUnnamed, + plutus_data_list_var: &Ident, +) -> Block { + data_with_unnamed_fields_from_list_of_plutus_data( + &parse_quote!(Self), + fields, + plutus_data_list_var, + ) +} + +fn struct_with_unnamed_fields_to_plutus_data_list(fields: &FieldsUnnamed) -> Block { + let to_list_of_plutus_data = struct_with_unnamed_fields_to_list_of_plutus_data(fields); + + parse_quote!({ + plutus_ledger_api::plutus_data::PlutusData::List(#to_list_of_plutus_data) + }) +} + +fn struct_with_unnamed_fields_from_plutus_data_list( + fields: &FieldsUnnamed, + plutus_data_input_var: &Ident, +) -> Block { + let list_of_plutus_data_var: Ident = parse_quote!(list_of_plutus_data); + + let from_list_of_plutus_data = + struct_with_unnamed_fields_from_list_of_plutus_data(fields, &list_of_plutus_data_var); + + parse_quote!({ + let #list_of_plutus_data_var = plutus_ledger_api::plutus_data::parse_list(#plutus_data_input_var)?; + + #from_list_of_plutus_data + }) +} + +fn struct_with_unnamed_fields_to_plutus_data_constr(fields: &FieldsUnnamed) -> Block { + let to_list_of_plutus_data = struct_with_unnamed_fields_to_list_of_plutus_data(fields); + + parse_quote!({ + plutus_ledger_api::plutus_data::PlutusData::Constr(0.into(), #to_list_of_plutus_data) + }) +} + +fn struct_with_unnamed_fields_from_plutus_data_constr( + fields: &FieldsUnnamed, + plutus_data_input_var: &Ident, +) -> Block { + let fields_var: Ident = parse_quote!(fields); + + let from_fields = struct_with_unnamed_fields_from_list_of_plutus_data(fields, &fields_var); + + parse_quote!({ + let #fields_var = plutus_ledger_api::plutus_data::parse_constr_with_tag(#plutus_data_input_var, 0)?; + + #from_fields + }) +} + +fn struct_with_no_field_to_plutus_data_list() -> Block { + parse_quote!(plutus_data::PlutusData::Constr(0.into(), vec![])) +} + +fn struct_with_no_field_from_plutus_data_list(plutus_data_input_var: &Ident) -> Block { + let list_of_plutus_data_var: Ident = parse_quote!(list_of_plutus_data); + + let from_list_of_plutus_data = + data_with_no_fields_from_list_of_plutus_data(&parse_quote!(Self), &list_of_plutus_data_var); + + parse_quote!({ + let #list_of_plutus_data_var = plutus_ledger_api::plutus_data::parse_list(#plutus_data_input_var)?; + + #from_list_of_plutus_data + }) +} + +fn struct_with_no_field_to_plutus_data_constr() -> Block { + parse_quote!(plutus_data::PlutusData::Constr(0.into(), vec![])) +} + +fn struct_with_no_field_from_plutus_data_constr(plutus_data_input_var: &Ident) -> Block { + let fields_var: Ident = parse_quote!(fields); + + let from_fields = + data_with_no_fields_from_list_of_plutus_data(&parse_quote!(Self), &fields_var); + + parse_quote!({ + let #fields_var = plutus_ledger_api::plutus_data::parse_constr_with_tag(#plutus_data_input_var, 0)?; + + #from_fields + }) +} + +fn data_fields_to_list_of_plutus_data(field_accessors: &[Expr]) -> Block { + let fields_to_plutus_data = field_accessors + .iter() + .map(|a| -> Expr { parse_quote!(#a.to_plutus_data()) }); + + parse_quote!({ vec![ #(#fields_to_plutus_data),* ] }) +} + +fn data_with_named_fields_from_list_of_plutus_data( + constructor: &Path, + fields_named: &FieldsNamed, + plutus_data_list_var: &Ident, +) -> Block { + let field_count = fields_named.named.len(); + + let field_idents = fields_named + .named + .iter() + .map(|field| field.ident.as_ref().unwrap()); + + let unparsed_field_idents = field_idents + .clone() + .map(|field_ident| format_ident!("unparsed_{}", field_ident)); + + let field_decoded_stmts = field_idents.clone().zip(unparsed_field_idents.clone()).map( + |(field_ident, unparsed_field_ident)| -> Stmt { + parse_quote!( + let #field_ident = plutus_ledger_api::plutus_data::IsPlutusData::from_plutus_data(#unparsed_field_ident)?; + ) + }, + ); + + parse_quote!( + { + let [ #(#unparsed_field_idents),* ] = plutus_ledger_api::plutus_data::parse_fixed_len_constr_fields::<#field_count>(#plutus_data_list_var)?; + #(#field_decoded_stmts)* + Ok(#constructor{ #(#field_idents),* }) + } + ) +} + +fn data_with_unnamed_fields_from_list_of_plutus_data( + constructor: &Path, + fields_unnamed: &FieldsUnnamed, + plutus_data_list_var: &Ident, +) -> Block { + let field_count = fields_unnamed.unnamed.len(); + + let unparsed_field_idents = + (0..field_count).map(|field_index| format_ident!("unparsed_{}", field_index)); + + let parsed_field_idents = + (0..field_count).map(|field_index| format_ident!("parsed_{}", field_index)); + + let field_decoded_stmts = unparsed_field_idents + .clone() + .zip(parsed_field_idents.clone()) + .map(|(unparsed, parsed)| -> Stmt { + parse_quote!( + let #parsed = IsPlutusData::from_plutus_data(#unparsed)?; + ) + }); + + parse_quote!({ + let [ #(#unparsed_field_idents),* ] = plutus_ledger_api::plutus_data::parse_fixed_len_constr_fields::<#field_count>(#plutus_data_list_var)?; + #(#field_decoded_stmts)* + Ok(#constructor(#(#parsed_field_idents),*)) + }) +} + +fn data_with_no_fields_from_list_of_plutus_data( + constructor: &Path, + list_of_plutus_data_var: &Ident, +) -> Block { + parse_quote!({ + let [ ] = plutus_ledger_api::plutus_data::parse_fixed_len_constr_fields::<0>(#list_of_plutus_data_var)?; + Ok(#constructor) + }) +} diff --git a/is-plutus-data-derive/src/lib.rs b/is-plutus-data-derive/src/lib.rs new file mode 100644 index 0000000..2ba5263 --- /dev/null +++ b/is-plutus-data-derive/src/lib.rs @@ -0,0 +1,13 @@ +use quote::ToTokens; +use syn::{parse_macro_input, DeriveInput}; + +pub(crate) mod derive_impl; + +#[proc_macro_derive(IsPlutusData, attributes(is_plutus_data_derive_strategy))] +pub fn derive_is_plutus_data(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + derive_impl::get_is_plutus_data_instance(input) + .unwrap() + .into_token_stream() + .into() +} diff --git a/plutus-ledger-api/CHANGELOG.md b/plutus-ledger-api/CHANGELOG.md index e98308b..d40ac65 100644 --- a/plutus-ledger-api/CHANGELOG.md +++ b/plutus-ledger-api/CHANGELOG.md @@ -4,13 +4,23 @@ This changelog is based on [Keep A Changelog](https://keepachangelog.com/en/1.1.0). -## Unreleased +## v2.0.0 ### Added +- Added cardano-serialization-lib conversion traits (`ToCSL` and `FromCSL`) ([#55](https://github.com/mlabs-haskell/plutus-ledger-api-rust/pull/55)) +- Added v3 plutus ledger types ([#57](https://github.com/mlabs-haskell/plutus-ledger-api-rust/pull/57)) +- Added the ability to derive `IsPlutusData` instances ([#56](https://github.com/mlabs-haskell/plutus-ledger-api-rust/pull/56)) +- Added a few utility functions for Values ([#55](https://github.com/mlabs-haskell/plutus-ledger-api-rust/pull/55)) +- Added Display and Debug implementations for + CurrencySymbol, TokenName, Value, AddressWithExtraInfo and some other types ([#55](https://github.com/mlabs-haskell/plutus-ledger-api-rust/pull/55)) +- Added FromStr implementations for CurrencySymbol, TokenName, Value, Address + and some other types ([#55](https://github.com/mlabs-haskell/plutus-ledger-api-rust/pull/55)) + ### Changed -### Removed +- Fixed `serde` serialization of Plutus `Value`s +- Updated cardano-serialization-lib to Conway compatible 12.1.1 ## v1.0.0 diff --git a/plutus-ledger-api/Cargo.lock b/plutus-ledger-api/Cargo.lock index dbc4968..a4455ab 100644 --- a/plutus-ledger-api/Cargo.lock +++ b/plutus-ledger-api/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -19,15 +31,21 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bech32" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" [[package]] name = "bit-set" @@ -46,9 +64,24 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" -version = "2.5.0" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] [[package]] name = "bumpalo" @@ -56,11 +89,60 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cardano-serialization-lib" +version = "12.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a6b87191e22d30d2fd9cf52a8db1acd0611d119f432e3dc676650c76f67840" +dependencies = [ + "bech32", + "cbor_event", + "cfg-if", + "clear_on_drop", + "cryptoxide", + "digest", + "ed25519-bip32", + "getrandom", + "hashlink", + "hex", + "itertools", + "js-sys", + "noop_proc_macro", + "num", + "num-bigint", + "num-derive", + "num-integer", + "num-traits", + "rand", + "rand_os", + "schemars", + "serde", + "serde-wasm-bindgen", + "serde_json", + "sha2", + "wasm-bindgen", +] + +[[package]] +name = "cbor_event" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "089a0261d1bc59e54e8e11860031efd88593f0e61b921172c474f1f38c2f2d3c" + [[package]] name = "cc" -version = "1.0.99" +version = "1.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -82,11 +164,44 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "clear_on_drop" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" +dependencies = [ + "cc", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "cryptoxide" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "382ce8820a5bb815055d3553a610e8cb542b2d767bbacea99038afda96cd760d" [[package]] name = "data-encoding" @@ -100,12 +215,42 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "dissimilar" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ed25519-bip32" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb588f93c0d91b2f668849fd6d030cddb0b2e31f105963be189da5acdf492a21" +dependencies = [ + "cryptoxide", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "equivalent" version = "1.0.1" @@ -119,14 +264,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fnv" @@ -134,6 +279,22 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -141,8 +302,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -163,7 +326,7 @@ dependencies = [ "serde", "serde_json", "upon", - "yansi 1.0.1", + "yansi", ] [[package]] @@ -171,12 +334,36 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -203,12 +390,31 @@ checksum = "90f97a5f38dd3ccfbe7aa80f4a0c00930f21b922c74195be0201c51028f22dcf" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.1", +] + +[[package]] +name = "is-plutus-data-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "thiserror", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", ] [[package]] @@ -228,15 +434,15 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lbr-prelude" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2367a6b351bdc4e6df1989e5d2a276e8ae8e54d15dcbff6dfd21b5ff45096b" +checksum = "21471892874c4667636a067ed7a158b9691178988a4c5988585e1010e9b5817d" dependencies = [ "data-encoding", "lbr-prelude-derive", @@ -261,15 +467,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "linked-hash-map" @@ -285,9 +491,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" @@ -295,17 +501,73 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", "serde", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -315,6 +577,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -327,63 +611,75 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "plutus-ledger-api" -version = "1.0.0" +version = "2.0.0" dependencies = [ + "anyhow", + "cardano-serialization-lib", "chrono", "data-encoding", "goldie", "impl_ops", + "is-plutus-data-derive", "lbr-prelude", "linked-hash-map", + "nom", "num-bigint", "num-traits", "proptest", "serde", "serde_json", "thiserror", - "true", ] [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", - "yansi 0.5.1", + "yansi", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags", + "bitflags 2.6.0", "lazy_static", "num-traits", "rand", @@ -403,9 +699,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -418,7 +714,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -428,9 +724,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", ] +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -440,32 +751,56 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "wasm-bindgen", + "winapi", +] + [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", ] [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" dependencies = [ - "bitflags", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -486,20 +821,66 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schemars" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + [[package]] name = "serde" -version = "1.0.203" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", @@ -508,45 +889,72 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "syn" -version = "2.0.67" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "target-triple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" + [[package]] name = "tempfile" -version = "3.10.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -560,18 +968,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" dependencies = [ "proc-macro2", "quote", @@ -580,9 +988,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", @@ -592,18 +1000,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -612,27 +1020,28 @@ dependencies = [ "winnow", ] -[[package]] -name = "true" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "548ec159e98411c04bee9b458df4ccd2da2a0a5e50c08cbd7e90b00b154c5a9a" - [[package]] name = "trybuild" -version = "1.0.96" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a5f13f11071020bb12de7a16b925d2d58636175c20c11dc5f96cb64bb6c9b3" +checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4" dependencies = [ "dissimilar", "glob", "serde", "serde_derive", "serde_json", + "target-triple", "termcolor", "toml", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unarray" version = "0.1.4" @@ -641,15 +1050,15 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "upon" @@ -662,6 +1071,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -731,15 +1146,37 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.52.0" @@ -758,11 +1195,20 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -776,69 +1222,84 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] -name = "yansi" -version = "1.0.1" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/plutus-ledger-api/Cargo.toml b/plutus-ledger-api/Cargo.toml index db2417d..9164feb 100644 --- a/plutus-ledger-api/Cargo.toml +++ b/plutus-ledger-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plutus-ledger-api" -version = "1.0.0" +version = "2.0.0" edition = "2021" license = "Apache-2.0" description = "Plutus Ledger types and utilities implemented in Rust" @@ -11,22 +11,23 @@ repository = "https://github.com/mlabs-haskell/plutus-ledger-api-rust" [dependencies] proptest = "^1.3.1" lbr-prelude = { version = "0.1.1", optional = true } -serde_json = { version = "^1.0.107", features = [ - "arbitrary_precision", -], optional = true } +serde_json = { version = "1.0.128", optional = true } num-bigint = "~0.4" serde = { version = "^1.0.189", features = ["derive"], optional = true } -true = { version = "~0.1.0", optional = true } data-encoding = "^2.4.0" thiserror = "^1.0.50" linked-hash-map = "~0.5.6" num-traits = "~0.2.17" impl_ops = "0.1.1" chrono = { version = "0.4.34", optional = true } +cardano-serialization-lib = "12.1.1" +is-plutus-data-derive = { path = ".extras/is-plutus-data-derive-0" } +anyhow = "1.0.86" +nom = "7.1.3" [features] default = [] -serde = ["dep:serde", "num-bigint/serde"] +serde = ["dep:serde", "num-bigint/serde", "dep:serde_json"] lbf = ["dep:lbr-prelude", "dep:serde_json"] chrono = ["dep:chrono"] diff --git a/plutus-ledger-api/build.nix b/plutus-ledger-api/build.nix index d43c706..67602f5 100644 --- a/plutus-ledger-api/build.nix +++ b/plutus-ledger-api/build.nix @@ -4,13 +4,16 @@ rustFlake = inputs.flake-lang.lib.${system}.rustFlake { src = ./.; - version = "0"; + version = "2"; crateName = "plutus-ledger-api"; devShellHook = config.settings.shell.hook; cargoNextestExtraArgs = "--all-features"; extraSourceFilters = [ (path: _type: builtins.match ".*golden$" path != null) ]; + extraSources = [ + config.packages.is-plutus-data-derive-rust-src + ]; }; plutus-ledger-api-rust-github-pages = pkgs.stdenv.mkDerivation { diff --git a/plutus-ledger-api/src/aux.rs b/plutus-ledger-api/src/aux.rs new file mode 100644 index 0000000..7c854bd --- /dev/null +++ b/plutus-ledger-api/src/aux.rs @@ -0,0 +1,90 @@ +use std::{ + collections::BTreeMap, + iter::{empty, once}, + str::FromStr, +}; + +use nom::{ + branch::alt, + character::complete::{char, digit1}, + combinator::{map_res, opt, recognize}, + error::VerboseError, + multi::many1, + sequence::tuple, + IResult, +}; +use num_bigint::BigInt; + +use crate::error::ConversionError; + +/// Create a container C from one element. +pub fn singleton(value: T) -> C +where + C: FromIterator, +{ + once(value).collect() +} + +/// Create an empty container. +pub fn none() -> C +where + C: FromIterator, +{ + empty::().collect() +} + +/// Union two BTreeMaps, call f to resolve conflicts if duplicate keys are encountered. +pub fn union_btree_maps_with V>( + f: F, + l: BTreeMap, + r: BTreeMap, +) -> BTreeMap { + r.into_iter().fold(l.clone(), |mut acc, (k, vr)| { + let v = if let Some((_, vl)) = acc.remove_entry(&k) { + f(vl, vr) + } else { + vr + }; + acc.insert(k, v); + acc + }) +} + +pub fn union_b_tree_maps_with V>( + f: F, + maps: [&BTreeMap; N], +) -> BTreeMap { + maps.into_iter().fold(BTreeMap::new(), |acc, m| { + m.iter().fold(acc, |mut acc, (k, v)| { + acc.entry(k.clone()) + .and_modify(|va: &mut V| *va = f(va, v)) + .or_insert(v.clone()); + + acc + }) + }) +} + +/// Verify that a given bytestring has the expected length +pub(crate) fn guard_bytes( + ctx: &str, + bytes: Vec, + expected: usize, +) -> Result, ConversionError> { + if bytes.len() == expected { + Ok(bytes) + } else { + Err(ConversionError::invalid_bytestring_length( + ctx, expected, "equal to", &bytes, + )) + } +} + +/// Nom parser for BigInt +/// Expects an arbitrary length decimal integer, optionally signed +pub(crate) fn big_int(i: &str) -> IResult<&str, BigInt, VerboseError<&str>> { + map_res( + recognize(tuple((opt(alt((char('-'), char('+')))), many1(digit1)))), + |s: &str| BigInt::from_str(s), + )(i) +} diff --git a/plutus-ledger-api/src/csl/csl_to_pla.rs b/plutus-ledger-api/src/csl/csl_to_pla.rs new file mode 100644 index 0000000..6641090 --- /dev/null +++ b/plutus-ledger-api/src/csl/csl_to_pla.rs @@ -0,0 +1,82 @@ +use std::{ops::Neg, str::FromStr}; + +use cardano_serialization_lib as csl; +use num_bigint::{BigInt, ParseBigIntError}; + +#[derive(Debug, Clone, thiserror::Error)] +pub enum TryFromCSLError { + #[error("Unable to parse BigInt: {0}")] + InvalidBigInt(ParseBigIntError), + + #[error("Unable to represent CSL value in PLA: {0}")] + ImpossibleConversion(String), +} + +/// Convert a cardano-serialization-lib type to its plutus-ledger-api counterpart +pub trait FromCSL { + fn from_csl(value: &T) -> Self + where + Self: Sized; +} + +pub trait ToPLA { + fn to_pla(&self) -> T; +} + +impl ToPLA for T +where + U: FromCSL, +{ + fn to_pla(&self) -> U { + FromCSL::from_csl(self) + } +} + +/// Convert a cardano-serialization-lib type to its plutus-ledger-api counterpart +pub trait TryFromCSL { + fn try_from_csl(value: &T) -> Result + where + Self: Sized; +} + +pub trait TryToPLA { + fn try_to_pla(&self) -> Result; +} + +impl TryToPLA for T +where + U: TryFromCSL, +{ + fn try_to_pla(&self) -> Result { + TryFromCSL::try_from_csl(self) + } +} + +impl FromCSL for BigInt { + fn from_csl(value: &csl::BigNum) -> Self { + let x: u64 = From::from(*value); + BigInt::from(x) + } +} + +impl FromCSL for BigInt { + fn from_csl(value: &u32) -> Self { + BigInt::from(*value) + } +} + +impl TryFromCSL for BigInt { + fn try_from_csl(value: &csl::BigInt) -> Result { + BigInt::from_str(&value.to_str()).map_err(TryFromCSLError::InvalidBigInt) + } +} + +impl FromCSL for BigInt { + fn from_csl(value: &csl::Int) -> Self { + if value.is_positive() { + BigInt::from_csl(&value.as_positive().unwrap()) + } else { + BigInt::from_csl(&value.as_negative().unwrap()).neg() + } + } +} diff --git a/plutus-ledger-api/src/csl/mod.rs b/plutus-ledger-api/src/csl/mod.rs new file mode 100644 index 0000000..8af54b5 --- /dev/null +++ b/plutus-ledger-api/src/csl/mod.rs @@ -0,0 +1,2 @@ +pub mod csl_to_pla; +pub mod pla_to_csl; diff --git a/plutus-ledger-api/src/csl/pla_to_csl.rs b/plutus-ledger-api/src/csl/pla_to_csl.rs new file mode 100644 index 0000000..1742f60 --- /dev/null +++ b/plutus-ledger-api/src/csl/pla_to_csl.rs @@ -0,0 +1,109 @@ +use cardano_serialization_lib as csl; +use num_bigint::{BigInt, TryFromBigIntError}; +use num_traits::sign::Signed; + +#[derive(Debug, thiserror::Error)] +pub enum TryFromPLAError { + #[error("{0}")] + CSLDeserializeError(csl::DeserializeError), + + #[error("{0}")] + CSLJsError(csl::JsError), + + #[error("Unable to cast BigInt {0} into type {1}: value is out of bound")] + BigIntOutOfRange(BigInt, String), + + #[error("Unable to represent PLA value in CSL: ${0}")] + ImpossibleConversion(String), + + #[error("Invalid valid transaction time range: ${0:?}")] + InvalidTimeRange(crate::v2::transaction::POSIXTimeRange), + + #[error("Script is missing from context: {0:?}")] + MissingScript(crate::v2::script::ScriptHash), +} + +/// Convert a plutus-ledger-api type to its cardano-serialization-lib counterpart +/// `try_to_csl_with` accepts extra data where the PLA data itself is not enough +pub trait TryFromPLA { + fn try_from_pla(val: &T) -> Result + where + Self: Sized; +} + +/// Convert a plutus-ledger-api type to its cardano-serialization-lib counterpart +/// `try_to_csl_with` accepts extra data where the PLA data itself is not enough +/// +/// DO NOT IMPLEMENT THIS DIRECTLY. Implement `TryFromPLA` instead. +pub trait TryToCSL { + fn try_to_csl(&self) -> Result; +} + +impl TryToCSL for T +where + U: TryFromPLA, +{ + fn try_to_csl(&self) -> Result { + TryFromPLA::try_from_pla(self) + } +} + +impl TryFromPLA for csl::BigNum { + fn try_from_pla(val: &u64) -> Result { + // BigNum(s) are u64 under the hood. + + Ok(csl::BigNum::from(*val)) + } +} + +impl TryFromPLA for csl::BigNum { + fn try_from_pla(val: &BigInt) -> Result { + // BigNum(s) are u64 under the hood. + let x: u64 = val + .to_owned() + .try_into() + .map_err(|err: TryFromBigIntError| { + TryFromPLAError::BigIntOutOfRange(err.into_original(), "u64".into()) + })?; + + x.try_to_csl() + } +} + +impl TryFromPLA for csl::BigInt { + fn try_from_pla(val: &BigInt) -> Result { + Ok(val.to_owned().into()) + } +} + +impl TryFromPLA for csl::Int { + fn try_from_pla(val: &BigInt) -> Result { + if val.is_negative() { + Ok(csl::Int::new_negative(&(val.abs()).try_to_csl()?)) + } else { + Ok(csl::Int::new(&val.try_to_csl()?)) + } + } +} + +impl TryFromPLA for csl::Int { + fn try_from_pla(val: &i64) -> Result { + if val.is_negative() { + Ok(csl::Int::new_negative(&csl::BigNum::from( + val.unsigned_abs(), + ))) + } else { + Ok(csl::Int::new(&csl::BigNum::from(*val as u64))) + } + } +} + +impl TryFromPLA for u32 /* TransactionIndex */ { + fn try_from_pla(val: &BigInt) -> Result { + val.to_owned() + .try_into() + .map_err(|err: TryFromBigIntError| { + TryFromPLAError::BigIntOutOfRange(err.into_original(), "u32".into()) + }) + } +} diff --git a/plutus-ledger-api/src/error.rs b/plutus-ledger-api/src/error.rs new file mode 100644 index 0000000..ec2cb73 --- /dev/null +++ b/plutus-ledger-api/src/error.rs @@ -0,0 +1,47 @@ +use data_encoding::HEXLOWER; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ConversionError { + #[error("ByteString length must be {relation} {expected} but got {got} with value{value_hex}")] + InvalidByteStringLength { + ctx: String, + expected: usize, + got: usize, + value_hex: String, + relation: String, + }, + + #[error("String cannot be parsed as a hexadecimal value: {value_hex}")] + HexDecodeError { + value_hex: String, + source: data_encoding::DecodeError, + }, + + #[error(transparent)] + ParseError(anyhow::Error), +} + +impl ConversionError { + pub fn invalid_bytestring_length( + ctx: &str, + expected: usize, + relation: &str, + bytes: &[u8], + ) -> Self { + ConversionError::InvalidByteStringLength { + ctx: ctx.to_string(), + expected, + got: bytes.len(), + relation: relation.to_string(), + value_hex: HEXLOWER.encode(bytes), + } + } + + pub fn hex_decode_error(err: data_encoding::DecodeError, value_hex: &str) -> Self { + ConversionError::HexDecodeError { + source: err, + value_hex: value_hex.to_string(), + } + } +} diff --git a/plutus-ledger-api/src/generators/correct/mod.rs b/plutus-ledger-api/src/generators/correct/mod.rs index db042bf..6903246 100644 --- a/plutus-ledger-api/src/generators/correct/mod.rs +++ b/plutus-ledger-api/src/generators/correct/mod.rs @@ -2,3 +2,4 @@ pub mod primitive; pub mod v1; pub mod v2; +pub mod v3; diff --git a/plutus-ledger-api/src/generators/correct/primitive.rs b/plutus-ledger-api/src/generators/correct/primitive.rs index 8260313..6e6e6c7 100644 --- a/plutus-ledger-api/src/generators/correct/primitive.rs +++ b/plutus-ledger-api/src/generators/correct/primitive.rs @@ -51,7 +51,7 @@ pub fn arb_natural(n: usize) -> impl Strategy { /// Helper function to generate a well typed arbitrary natural number /// Generating `n` vectors of random u32 values, which gives a max bound of u32::MAX ^ n fn arb_biguint(n: usize) -> impl Strategy { - vec(any::(), n).prop_map(|value| BigUint::new(value)) + vec(any::(), n).prop_map(BigUint::new) } /// Strategy to generate an arbitrary character diff --git a/plutus-ledger-api/src/generators/correct/v1.rs b/plutus-ledger-api/src/generators/correct/v1.rs index 85b6c66..37d08a6 100644 --- a/plutus-ledger-api/src/generators/correct/v1.rs +++ b/plutus-ledger-api/src/generators/correct/v1.rs @@ -8,7 +8,7 @@ use crate::v1::address::{ Address, CertificateIndex, ChainPointer, Credential, Slot, StakingCredential, TransactionIndex, }; use crate::v1::assoc_map::AssocMap; -use crate::v1::crypto::{Ed25519PubKeyHash, LedgerBytes, PaymentPubKeyHash}; +use crate::v1::crypto::{Ed25519PubKeyHash, LedgerBytes, PaymentPubKeyHash, StakePubKeyHash}; use crate::v1::datum::{Datum, DatumHash}; use crate::v1::interval::{Extended, Interval, LowerBound, PlutusInterval, UpperBound}; use crate::v1::redeemer::{Redeemer, RedeemerHash}; @@ -17,6 +17,7 @@ use crate::v1::transaction::{ DCert, POSIXTime, ScriptContext, ScriptPurpose, TransactionHash, TransactionInfo, TransactionInput, TransactionOutput, TxInInfo, }; +use crate::v1::value::Lovelace; use crate::v2::value::{AssetClass, CurrencySymbol, TokenName, Value}; use num_bigint::BigInt; use proptest::collection::btree_map; @@ -402,3 +403,11 @@ pub fn arb_script_context() -> impl Strategy { (arb_script_purpose(), arb_transaction_info()) .prop_map(|(purpose, tx_info)| ScriptContext { purpose, tx_info }) } + +pub fn arb_lovelace() -> impl Strategy { + arb_natural(1).prop_map(Lovelace) +} + +pub fn arb_stake_pub_key_hash() -> impl Strategy { + arb_ed25519_pub_key_hash().prop_map(StakePubKeyHash) +} diff --git a/plutus-ledger-api/src/generators/correct/v3.rs b/plutus-ledger-api/src/generators/correct/v3.rs new file mode 100644 index 0000000..6e7f5bc --- /dev/null +++ b/plutus-ledger-api/src/generators/correct/v3.rs @@ -0,0 +1,293 @@ +//! Proptest strategies for Plutus V3 types +//! +//! These strategies always return valid values. + +use proptest::{ + collection::vec, + option, + prelude::{Just, Strategy}, + prop_oneof, +}; + +use crate::{ + generators::correct::{ + primitive::arb_integer, + v1::{ + arb_currency_symbol, arb_datum, arb_lovelace, arb_payment_pub_key_hash, + arb_stake_pub_key_hash, arb_staking_credential, arb_transaction_input, + }, + }, + v3::{ + ratio::Rational, + transaction::{ + ChangeParameters, ColdCommitteeCredential, Committee, Constitution, DRep, + DRepCredential, Delegatee, GovernanceAction, GovernanceActionId, + HotCommitteeCredential, ProtocolProcedure, ProtocolVersion, ScriptContext, ScriptInfo, + ScriptPurpose, TransactionInfo, TxCert, Vote, Voter, + }, + }, +}; + +use super::{ + primitive::arb_natural, + v1::{ + arb_assoc_map, arb_credential, arb_datum_hash, arb_plutus_data, + arb_plutus_interval_posix_time, arb_redeemer, arb_script_hash, arb_transaction_hash, + arb_value, + }, + v2::{arb_transaction_output, arb_tx_in_info}, +}; + +/// Strategy to generate cold committee credentials +pub fn arb_cold_committee_credential() -> impl Strategy { + arb_credential().prop_map(ColdCommitteeCredential) +} + +/// Strategy to generate hot committee credentials +pub fn arb_hot_committee_credential() -> impl Strategy { + arb_credential().prop_map(HotCommitteeCredential) +} + +/// Strategy to generate DRep credentials +pub fn arb_d_rep_credential() -> impl Strategy { + arb_credential().prop_map(DRepCredential) +} + +/// Strategy to generate DReps +pub fn arb_d_rep() -> impl Strategy { + prop_oneof![ + arb_d_rep_credential().prop_map(DRep::DRep), + Just(DRep::AlwaysAbstain), + Just(DRep::AlwaysNoConfidence) + ] +} + +/// Strategy to generate delegatees +pub fn arb_delegatee() -> impl Strategy { + prop_oneof![ + arb_stake_pub_key_hash().prop_map(Delegatee::Stake), + arb_d_rep().prop_map(Delegatee::Vote), + (arb_stake_pub_key_hash(), arb_d_rep()).prop_map(|(h, r)| Delegatee::StakeVote(h, r)) + ] +} + +/// Strategy to generate tx certs +pub fn arb_tx_cert() -> impl Strategy { + prop_oneof![ + (arb_staking_credential(), option::of(arb_lovelace())) + .prop_map(|(c, l)| TxCert::RegStaking(c, l)), + (arb_staking_credential(), option::of(arb_lovelace())) + .prop_map(|(c, l)| TxCert::UnRegStaking(c, l)), + (arb_staking_credential(), arb_delegatee()).prop_map(|(c, d)| TxCert::DelegStaking(c, d)), + (arb_staking_credential(), arb_delegatee(), arb_lovelace()) + .prop_map(|(c, d, l)| TxCert::RegDeleg(c, d, l)), + (arb_d_rep_credential(), arb_lovelace()).prop_map(|(d, l)| TxCert::RegDRep(d, l)), + arb_d_rep_credential().prop_map(TxCert::UpdateDRep), + (arb_d_rep_credential(), arb_lovelace()).prop_map(|(d, l)| TxCert::UnRegDRep(d, l)), + (arb_payment_pub_key_hash(), arb_payment_pub_key_hash()) + .prop_map(|(pkh1, pkh2)| TxCert::PoolRegister(pkh1, pkh2)), + (arb_payment_pub_key_hash(), arb_integer()).prop_map(|(pkh, i)| TxCert::PoolRetire(pkh, i)), + ( + arb_cold_committee_credential(), + arb_hot_committee_credential() + ) + .prop_map(|(c, h)| TxCert::AuthHotCommittee(c, h)), + arb_cold_committee_credential().prop_map(TxCert::ResignColdCommittee) + ] +} + +/// Strategy to generate voters +pub fn arb_voter() -> impl Strategy { + prop_oneof![ + arb_hot_committee_credential().prop_map(Voter::CommitteeVoter), + arb_d_rep_credential().prop_map(Voter::DRepVoter), + arb_payment_pub_key_hash().prop_map(Voter::StakePoolVoter) + ] +} + +/// Strategy to generate votes +pub fn arb_vote() -> impl Strategy { + prop_oneof![Just(Vote::VoteNo), Just(Vote::VoteYes), Just(Vote::Abstain)] +} + +/// Strategy to generate governance action ids +pub fn arb_governance_action_id() -> impl Strategy { + (arb_transaction_hash(), arb_integer()).prop_map(|(tx_id, gov_action_id)| GovernanceActionId { + tx_id, + gov_action_id, + }) +} + +/// Strategy to generate committees +pub fn arb_committee() -> impl Strategy { + ( + arb_assoc_map(arb_cold_committee_credential(), arb_integer()), + arb_rational(), + ) + .prop_map(|(members, quorum)| Committee { members, quorum }) +} + +/// Strategy to generate rationals +pub fn arb_rational() -> impl Strategy { + (arb_integer(), arb_integer()).prop_map(|(n, d)| Rational(n, d)) +} + +/// Strategy to generate constitutions +pub fn arb_constitution() -> impl Strategy { + option::of(arb_script_hash()).prop_map(|constitution_script| Constitution { + constitution_script, + }) +} + +/// Strategy to generate protocol versions +pub fn arb_protocol_version() -> impl Strategy { + (arb_natural(1), arb_natural(1)).prop_map(|(major, minor)| ProtocolVersion { major, minor }) +} + +/// Strategy to generate change parameters +pub fn arb_change_parameters() -> impl Strategy { + arb_plutus_data().prop_map(ChangeParameters) +} + +/// Strategy to generate governance actions +pub fn arb_governance_action() -> impl Strategy { + prop_oneof![ + ( + option::of(arb_governance_action_id()), + arb_change_parameters(), + option::of(arb_script_hash()) + ) + .prop_map(|(g, c, s)| GovernanceAction::ParameterChange(g, c, s)), + ( + option::of(arb_governance_action_id()), + arb_protocol_version() + ) + .prop_map(|(g, p)| GovernanceAction::HardForkInitiation(g, p)), + ( + arb_assoc_map(arb_credential(), arb_lovelace()), + option::of(arb_script_hash()) + ) + .prop_map(|(a, s)| GovernanceAction::TreasuryWithdrawals(a, s)), + option::of(arb_governance_action_id()).prop_map(GovernanceAction::NoConfidence), + ( + option::of(arb_governance_action_id()), + vec(arb_cold_committee_credential(), 5), + arb_assoc_map(arb_cold_committee_credential(), arb_integer()), + arb_rational() + ) + .prop_map(|(g, c, cm, q)| GovernanceAction::UpdateCommittee(g, c, cm, q)), + (option::of(arb_governance_action_id()), arb_constitution()) + .prop_map(|(g, c)| GovernanceAction::NewConstitution(g, c)), + Just(GovernanceAction::InfoAction) + ] +} + +/// Strategy to generate protocol procedures +pub fn arb_protocol_procedure() -> impl Strategy { + (arb_lovelace(), arb_credential(), arb_governance_action()).prop_map(|(l, c, g)| { + ProtocolProcedure { + deposit: l, + return_addr: c, + governance_action: g, + } + }) +} + +/// Strategy to generate script purposes +pub fn arb_script_purpose() -> impl Strategy { + prop_oneof![ + arb_currency_symbol().prop_map(ScriptPurpose::Minting), + arb_transaction_input().prop_map(ScriptPurpose::Spending), + arb_credential().prop_map(ScriptPurpose::Rewarding), + (arb_integer(), arb_tx_cert()).prop_map(|(i, c)| ScriptPurpose::Certifying(i, c)), + arb_voter().prop_map(ScriptPurpose::Voting), + (arb_integer(), arb_protocol_procedure()).prop_map(|(i, p)| ScriptPurpose::Proposing(i, p)) + ] +} + +/// Strategy to generate script info +pub fn arb_script_info() -> impl Strategy { + prop_oneof![ + arb_currency_symbol().prop_map(ScriptInfo::Minting), + (arb_transaction_input(), option::of(arb_datum())) + .prop_map(|(i, d)| ScriptInfo::Spending(i, d)), + arb_credential().prop_map(ScriptInfo::Rewarding), + (arb_integer(), arb_tx_cert()).prop_map(|(i, c)| ScriptInfo::Certifying(i, c)), + arb_voter().prop_map(ScriptInfo::Voting), + (arb_integer(), arb_protocol_procedure()).prop_map(|(i, p)| ScriptInfo::Proposing(i, p)) + ] +} + +/// Strategy to generate transaction info +pub fn arb_transaction_info() -> impl Strategy { + ( + vec(arb_tx_in_info(), 5), + vec(arb_tx_in_info(), 5), + vec(arb_transaction_output(), 5), + arb_lovelace(), + arb_value(), + vec(arb_tx_cert(), 5), + arb_assoc_map(arb_staking_credential(), arb_natural(1)), + arb_plutus_interval_posix_time(), + vec(arb_payment_pub_key_hash(), 5), + arb_assoc_map(arb_script_purpose(), arb_redeemer()), + arb_assoc_map(arb_datum_hash(), arb_datum()), + // HACK(chfanghr): Strategy is not implemented for longer tuples + ( + arb_transaction_hash(), + arb_assoc_map( + arb_voter(), + arb_assoc_map(arb_governance_action_id(), arb_vote()), + ), + vec(arb_protocol_procedure(), 5), + option::of(arb_lovelace()), + option::of(arb_lovelace()), + ), + ) + .prop_map( + |( + inputs, + reference_inputs, + outputs, + fee, + mint, + tx_certs, + wdrl, + valid_range, + signatories, + redeemers, + datums, + (id, votes, protocol_procedures, current_treasury_amount, treasury_donation), + )| { + TransactionInfo { + inputs, + reference_inputs, + outputs, + fee, + mint, + tx_certs, + wdrl, + valid_range, + signatories, + redeemers, + datums, + id, + votes, + protocol_procedures, + current_treasury_amount, + treasury_donation, + } + }, + ) +} + +/// Strategy to generate script contexts +pub fn arb_script_context() -> impl Strategy { + (arb_transaction_info(), arb_redeemer(), arb_script_info()).prop_map( + |(tx_info, redeemer, script_info)| ScriptContext { + tx_info, + redeemer, + script_info, + }, + ) +} diff --git a/plutus-ledger-api/src/goldens/v1.rs b/plutus-ledger-api/src/goldens/v1.rs index 09b24b0..5413c80 100644 --- a/plutus-ledger-api/src/goldens/v1.rs +++ b/plutus-ledger-api/src/goldens/v1.rs @@ -14,17 +14,20 @@ use crate::{ }, value::{AssetClass, CurrencySymbol, TokenName, Value}, }, + v2::address::{CertificateIndex, ChainPointer, Slot, TransactionIndex}, }; use num_bigint::BigInt; +pub fn sample_script_hash() -> ScriptHash { + ScriptHash(LedgerBytes([1].repeat(28).to_vec())) +} + pub fn sample_currency_symbol() -> CurrencySymbol { - CurrencySymbol::NativeToken(MintingPolicyHash(ScriptHash(LedgerBytes( - [0].repeat(28).to_vec(), - )))) + CurrencySymbol::NativeToken(MintingPolicyHash(sample_script_hash())) } pub fn sample_token_name() -> TokenName { - TokenName::from_string("Something") + TokenName::from_string("Something").unwrap() } pub fn sample_asset_class() -> AssetClass { @@ -39,7 +42,7 @@ pub fn sample_value() -> Value { &sample_currency_symbol(), &sample_token_name(), &BigInt::from(123), - ) + ) + Value::ada_value(&BigInt::from(234)) } pub fn sample_plutus_interval() -> PlutusInterval { @@ -50,10 +53,20 @@ pub fn sample_ed25519_pub_key_hash() -> Ed25519PubKeyHash { Ed25519PubKeyHash(LedgerBytes([0].repeat(28).to_vec())) } +pub fn sample_credential() -> Credential { + Credential::Script(ValidatorHash(sample_script_hash())) +} + pub fn sample_staking_credential() -> StakingCredential { - StakingCredential::Hash(Credential::Script(ValidatorHash(ScriptHash(LedgerBytes( - [1].repeat(28).to_vec(), - ))))) + StakingCredential::Hash(sample_credential()) +} + +pub fn sample_chain_pointer() -> ChainPointer { + ChainPointer { + slot_number: Slot(134561.into()), + transaction_index: TransactionIndex(4.into()), + certificate_index: CertificateIndex(10.into()), + } } pub fn sample_address() -> Address { @@ -78,11 +91,12 @@ pub fn sample_datum_hash() -> DatumHash { DatumHash(LedgerBytes([0].repeat(32).to_vec())) } +pub fn sample_plutus_data() -> PlutusData { + PlutusData::constr(1, vec![PlutusData::bytes("Something".as_bytes().to_vec())]) +} + pub fn sample_datum() -> Datum { - Datum(PlutusData::constr( - 1, - vec![PlutusData::bytes("Something".as_bytes().to_vec())], - )) + Datum(sample_plutus_data()) } pub fn sample_redeemer_hash() -> RedeemerHash { diff --git a/plutus-ledger-api/src/goldens/v2.rs b/plutus-ledger-api/src/goldens/v2.rs index e879296..5542f7c 100644 --- a/plutus-ledger-api/src/goldens/v2.rs +++ b/plutus-ledger-api/src/goldens/v2.rs @@ -1,4 +1,12 @@ //! Golden test data or Plutus V2 types +pub use super::v1::{ + sample_address, sample_asset_class, sample_chain_pointer, sample_credential, + sample_currency_symbol, sample_datum, sample_datum_hash, sample_dcert, + sample_ed25519_pub_key_hash, sample_payment_pub_key_hash, sample_plutus_data, + sample_plutus_interval, sample_redeemer, sample_redeemer_hash, sample_script_hash, + sample_script_purpose, sample_staking_credential, sample_token_name, sample_transaction_hash, + sample_transaction_input, sample_value, +}; use crate::v2::{ assoc_map::AssocMap, crypto::LedgerBytes, diff --git a/plutus-ledger-api/src/lamval.rs b/plutus-ledger-api/src/lamval.rs index 366e3b7..7a3459b 100644 --- a/plutus-ledger-api/src/lamval.rs +++ b/plutus-ledger-api/src/lamval.rs @@ -1,4 +1,6 @@ -use crate::plutus_data::{self, PlutusData, PlutusDataError}; +use crate::plutus_data::PlutusDataError; +use crate::plutus_data::{self, PlutusData}; + use num_bigint::BigInt; pub fn case_plutus_data<'a, T: 'a>( diff --git a/plutus-ledger-api/src/lib.rs b/plutus-ledger-api/src/lib.rs index 8232f0b..c65bb28 100644 --- a/plutus-ledger-api/src/lib.rs +++ b/plutus-ledger-api/src/lib.rs @@ -6,9 +6,12 @@ pub mod lamval; pub mod plutus_data; pub mod v1; pub mod v2; +pub mod v3; #[cfg(feature = "lbf")] pub use lbr_prelude::json; -pub mod utils; +pub mod aux; +pub mod csl; +pub mod error; #[macro_use] extern crate impl_ops; diff --git a/plutus-ledger-api/src/plutus_data.rs b/plutus-ledger-api/src/plutus_data.rs index 4607217..0be189b 100644 --- a/plutus-ledger-api/src/plutus_data.rs +++ b/plutus-ledger-api/src/plutus_data.rs @@ -1,4 +1,15 @@ //! Plutus Data related types and traits + +use std::collections::{BTreeMap, BTreeSet}; + +use cardano_serialization_lib as csl; +use num_bigint::BigInt; + +use crate::csl::csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA}; +use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; + +pub use is_plutus_data_derive::IsPlutusData; + #[cfg(feature = "lbf")] use data_encoding::HEXLOWER; #[cfg(feature = "lbf")] @@ -7,8 +18,6 @@ use lbr_prelude::error::Error; use lbr_prelude::json::{ case_json_constructor, case_json_object, json_constructor, json_object, Json, }; -use num_bigint::BigInt; -use std::collections::{BTreeMap, BTreeSet}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -24,6 +33,48 @@ pub enum PlutusData { Bytes(Vec), } +#[derive(Clone, Debug)] +pub enum PlutusType { + Constr, + Map, + List, + Integer, + Bytes, +} + +pub trait IsPlutusData { + fn to_plutus_data(&self) -> PlutusData; + + fn from_plutus_data(plutus_data: &PlutusData) -> Result + where + Self: Sized; +} + +// TODO(chfanghr): improve error reporting +#[derive(Clone, Debug, thiserror::Error)] +pub enum PlutusDataError { + #[error("Expected a PlutusData type {wanted:?}, but got {got:?}")] + UnexpectedPlutusType { got: PlutusType, wanted: PlutusType }, + #[error("Expected a PlutusData type as {wanted:?}, but got {got:?}")] + UnexpectedPlutusInvariant { got: String, wanted: String }, + #[error("Expected a Plutus List with {wanted:?} elements, but got {got:?} elements")] + UnexpectedListLength { got: usize, wanted: usize }, + #[error("Some internal error happened: {0}")] + InternalError(String), +} + +impl From<&PlutusData> for PlutusType { + fn from(plutus_data: &PlutusData) -> Self { + match plutus_data { + PlutusData::Constr(_, _) => PlutusType::Constr, + PlutusData::Map(_) => PlutusType::Map, + PlutusData::List(_) => PlutusType::List, + PlutusData::Integer(_) => PlutusType::Integer, + PlutusData::Bytes(_) => PlutusType::Bytes, + } + } +} + impl PlutusData { pub fn constr(tag: u32, fields: Vec) -> Self { PlutusData::Constr(BigInt::from(tag), fields) @@ -46,6 +97,35 @@ impl PlutusData { } } +impl IsPlutusData for PlutusData { + fn to_plutus_data(&self) -> PlutusData { + self.clone() + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + Ok(plutus_data.clone()) + } +} + +impl TryFromCSL for PlutusData { + fn try_from_csl(value: &csl::PlutusData) -> Result { + Ok(match value.kind() { + csl::PlutusDataKind::ConstrPlutusData => { + let constr_data = value.as_constr_plutus_data().unwrap(); + let tag = BigInt::from_csl(&constr_data.alternative()); + let args = constr_data.data().try_to_pla()?; + PlutusData::Constr(tag, args) + } + csl::PlutusDataKind::Map => PlutusData::Map(value.as_map().unwrap().try_to_pla()?), + csl::PlutusDataKind::List => PlutusData::List(value.as_list().unwrap().try_to_pla()?), + csl::PlutusDataKind::Integer => { + PlutusData::Integer(value.as_integer().unwrap().try_to_pla()?) + } + csl::PlutusDataKind::Bytes => PlutusData::Bytes(value.as_bytes().unwrap()), + }) + } +} + #[cfg(feature = "lbf")] impl Json for PlutusData { fn to_json(&self) -> serde_json::Value { @@ -162,62 +242,7 @@ impl Json for PlutusData { } } -/// Deserialise a Plutus data using parsers for each variant -pub fn case_plutus_data<'a, T>( - ctor_case: impl FnOnce(&'a BigInt) -> Box) -> T>, - list_case: impl FnOnce(&'a Vec) -> T, - int_case: impl FnOnce(&'a BigInt) -> T, - other_case: impl FnOnce(&'a PlutusData) -> T, - pd: &'a PlutusData, -) -> T { - match pd { - PlutusData::Constr(tag, args) => ctor_case(&tag)(&args), - PlutusData::List(args) => list_case(&args), - PlutusData::Integer(i) => int_case(&i), - other => other_case(&other), - } -} - -pub trait IsPlutusData { - fn to_plutus_data(&self) -> PlutusData; - - fn from_plutus_data(plutus_data: &PlutusData) -> Result - where - Self: Sized; -} - -#[derive(Clone, Debug)] -pub enum PlutusType { - Constr, - Map, - List, - Integer, - Bytes, -} - -impl From<&PlutusData> for PlutusType { - fn from(plutus_data: &PlutusData) -> Self { - match plutus_data { - PlutusData::Constr(_, _) => PlutusType::Constr, - PlutusData::Map(_) => PlutusType::Map, - PlutusData::List(_) => PlutusType::List, - PlutusData::Integer(_) => PlutusType::Integer, - PlutusData::Bytes(_) => PlutusType::Bytes, - } - } -} - -#[derive(Clone, Debug, thiserror::Error)] -pub enum PlutusDataError { - #[error("Expected a PlutusData type {wanted:?}, but got {got:?}")] - UnexpectedPlutusType { got: PlutusType, wanted: PlutusType }, - #[error("Expected a PlutusData type as {wanted:?}, but got {got:?}")] - UnexpectedPlutusInvariant { got: String, wanted: String }, - #[error("Expected a Plutus List with {wanted:?} elements, but got {got:?} elements")] - UnexpectedListLength { got: usize, wanted: usize }, - #[error("Some internal error happened: {0}")] - InternalError(String), -} +// MARK: Orphan IsPlutusData Instances impl IsPlutusData for BigInt { fn to_plutus_data(&self) -> PlutusData { @@ -235,72 +260,43 @@ impl IsPlutusData for BigInt { } } -impl IsPlutusData for bool { +impl IsPlutusData for Vec { fn to_plutus_data(&self) -> PlutusData { - if *self { - PlutusData::Constr(BigInt::from(1), Vec::with_capacity(0)) - } else { - PlutusData::Constr(BigInt::from(0), Vec::with_capacity(0)) - } + PlutusData::Bytes(self.clone()) } fn from_plutus_data(plutus_data: &PlutusData) -> Result { match plutus_data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 0)?; - Ok(false) - } - Ok(1) => { - verify_constr_fields(&fields, 0)?; - Ok(true) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field between 0 and 1".to_owned(), - got: flag.to_string(), - }), - }, - + PlutusData::Bytes(bytes) => Ok(bytes.clone()), _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, + wanted: PlutusType::Bytes, got: PlutusType::from(plutus_data), }), } } } -impl IsPlutusData for char { - fn to_plutus_data(&self) -> PlutusData { - String::from(*self).to_plutus_data() - } +const BOOL_FALSE_TAG: u32 = 0; +const BOOL_TRUE_TAG: u32 = 1; - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - String::from_plutus_data(plutus_data).and_then(|str| { - let mut chars = str.chars(); - let ch = chars.next(); - let rest = chars.next(); - match (ch, rest) { - (Some(ch), None) => Ok(ch), - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - got: "string".to_owned(), - wanted: "char".to_owned(), - }), - } - }) - } -} - -impl IsPlutusData for Vec { +impl IsPlutusData for bool { fn to_plutus_data(&self) -> PlutusData { - PlutusData::Bytes(self.clone()) + if *self { + PlutusData::Constr(BOOL_TRUE_TAG.into(), vec![]) + } else { + PlutusData::Constr(BOOL_FALSE_TAG.into(), vec![]) + } } fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Bytes(bytes) => Ok(bytes.clone()), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Bytes, - got: PlutusType::from(plutus_data), + let (tag, fields) = parse_constr(plutus_data)?; + let [] = parse_fixed_len_constr_fields::<0>(fields)?; + match tag { + BOOL_TRUE_TAG => Ok(true), + BOOL_FALSE_TAG => Ok(false), + _ => Err(PlutusDataError::UnexpectedPlutusInvariant { + wanted: format!("Constr with tag {BOOL_TRUE_TAG} or {BOOL_FALSE_TAG}"), + got: tag.to_string(), }), } } @@ -320,49 +316,71 @@ impl IsPlutusData for String { )) }), _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Integer, + wanted: PlutusType::Bytes, got: PlutusType::from(plutus_data), }), } } } +impl IsPlutusData for char { + fn to_plutus_data(&self) -> PlutusData { + String::from(*self).to_plutus_data() + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + String::from_plutus_data(plutus_data).and_then(|str| { + let mut chars = str.chars(); + let ch = chars.next(); + let rest = chars.next(); + match (ch, rest) { + (Some(ch), None) => Ok(ch), + _ => Err(PlutusDataError::UnexpectedPlutusInvariant { + got: "string".to_owned(), + wanted: "char".to_owned(), + }), + } + }) + } +} + +const OPTION_SOME_TAG: u32 = 0; +const OPTION_NONE_TAG: u32 = 1; + impl IsPlutusData for Option where T: IsPlutusData, { fn to_plutus_data(&self) -> PlutusData { match self { - Some(val) => PlutusData::Constr(BigInt::from(0), vec![val.to_plutus_data()]), - None => PlutusData::Constr(BigInt::from(1), Vec::with_capacity(0)), + Some(val) => PlutusData::Constr(OPTION_SOME_TAG.into(), vec![val.to_plutus_data()]), + None => PlutusData::Constr(OPTION_NONE_TAG.into(), vec![]), } } fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 1)?; - Ok(Some(T::from_plutus_data(&fields[0])?)) - } - Ok(1) => { - verify_constr_fields(&fields, 0)?; - Ok(None) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field between 0 and 1".to_owned(), - got: flag.to_string(), - }), - }, + let (tag, fields) = parse_constr(plutus_data)?; - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(plutus_data), + match tag { + OPTION_SOME_TAG => { + let [data] = parse_fixed_len_constr_fields::<1>(fields)?; + Ok(Some(T::from_plutus_data(data)?)) + } + OPTION_NONE_TAG => { + let [] = parse_fixed_len_constr_fields::<0>(fields)?; + Ok(None) + } + _ => Err(PlutusDataError::UnexpectedPlutusInvariant { + wanted: format!("Constr with tag {OPTION_SOME_TAG} or {OPTION_NONE_TAG}"), + got: tag.to_string(), }), } } } +const RESULT_ERR_TAG: u32 = 0; +const RESULT_OK_TAG: u32 = 1; + impl IsPlutusData for Result where T: IsPlutusData, @@ -370,31 +388,21 @@ where { fn to_plutus_data(&self) -> PlutusData { match self { - Err(val) => PlutusData::Constr(BigInt::from(0), vec![val.to_plutus_data()]), - Ok(val) => PlutusData::Constr(BigInt::from(1), vec![val.to_plutus_data()]), + Err(err) => PlutusData::Constr(RESULT_ERR_TAG.into(), vec![err.to_plutus_data()]), + Ok(val) => PlutusData::Constr(RESULT_OK_TAG.into(), vec![val.to_plutus_data()]), } } fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 1)?; - Ok(Err(E::from_plutus_data(&fields[0])?)) - } - Ok(1) => { - verify_constr_fields(&fields, 1)?; - Ok(Ok(T::from_plutus_data(&fields[0])?)) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field between 0 and 1".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(plutus_data), + let (tag, fields) = parse_constr(plutus_data)?; + let [field] = parse_fixed_len_constr_fields::<1>(fields)?; + + match tag { + RESULT_ERR_TAG => Ok(Err(E::from_plutus_data(field)?)), + RESULT_OK_TAG => Ok(Ok(T::from_plutus_data(field)?)), + _ => Err(PlutusDataError::UnexpectedPlutusInvariant { + wanted: format!("Constr with tag {RESULT_ERR_TAG} or {RESULT_OK_TAG}"), + got: tag.to_string(), }), } } @@ -414,16 +422,8 @@ where } fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::List(vec) => vec - .iter() - .map(|val| T::from_plutus_data(val)) - .collect::, PlutusDataError>>(), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::List, - got: PlutusType::from(plutus_data), - }), - } + let list = parse_list(plutus_data)?; + list.iter().map(T::from_plutus_data).collect() } } @@ -443,11 +443,11 @@ where fn from_plutus_data(plutus_data: &PlutusData) -> Result { match plutus_data { PlutusData::List(vec) => vec - .into_iter() + .iter() .map(|val| T::from_plutus_data(val)) .collect::>(), _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Map, + wanted: PlutusType::List, got: PlutusType::from(plutus_data), }), } @@ -471,7 +471,7 @@ where fn from_plutus_data(plutus_data: &PlutusData) -> Result { match plutus_data { PlutusData::Map(dict) => dict - .into_iter() + .iter() .map(|(key, val)| Ok((K::from_plutus_data(key)?, V::from_plutus_data(val)?))) .collect::>(), _ => Err(PlutusDataError::UnexpectedPlutusType { @@ -482,41 +482,21 @@ where } } +const UNIT_TAG: u32 = 0; + impl IsPlutusData for () { fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(BigInt::from(0), Vec::with_capacity(0)) + PlutusData::Constr(UNIT_TAG.into(), vec![]) } fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 0)?; - Ok(()) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(plutus_data), - }), - } + let fields = parse_constr_with_tag(plutus_data, UNIT_TAG)?; + let [] = parse_fixed_len_constr_fields::<0>(fields)?; + Ok(()) } } -impl IsPlutusData for PlutusData { - fn to_plutus_data(&self) -> PlutusData { - self.clone() - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - Ok(plutus_data.clone()) - } -} +const PAIR_TAG: u32 = 0; impl IsPlutusData for (A, B) where @@ -525,55 +505,111 @@ where { fn to_plutus_data(&self) -> PlutusData { PlutusData::Constr( - BigInt::from(0), + BigInt::from(PAIR_TAG), vec![self.0.to_plutus_data(), self.1.to_plutus_data()], ) } fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 2)?; - Ok(( - A::from_plutus_data(&fields[0])?, - B::from_plutus_data(&fields[1])?, - )) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, + let fields = parse_constr_with_tag(plutus_data, PAIR_TAG)?; + let [a, b] = parse_fixed_len_constr_fields::<2>(fields)?; + Ok((A::from_plutus_data(a)?, B::from_plutus_data(b)?)) + } +} - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(plutus_data), - }), - } +// MARK: Orphan TryFromCSL instances + +impl TryFromCSL for Vec { + fn try_from_csl(value: &csl::PlutusList) -> Result { + (0..value.len()) + .map(|idx| value.get(idx).try_to_pla()) + .collect() } } -/// Verify the number of fields contained in a PlutusData::Constr -pub fn verify_constr_fields( - fields: &Vec, - expected: usize, -) -> Result<(), PlutusDataError> { - if fields.len() != expected { - Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: format!("Constr with {} fields", expected), - got: format!("{:?}", fields), +impl TryFromCSL for Vec<(PlutusData, PlutusData)> { + fn try_from_csl(c_map: &csl::PlutusMap) -> Result { + let keys = c_map.keys(); + (0..keys.len()).try_fold(Vec::new(), |mut vector, idx| { + let key = keys.get(idx); + let values = c_map.get(&key).unwrap(); + + for value_idx in 0..values.len() { + vector.push(( + key.clone().try_to_pla()?, + values.get(value_idx).unwrap().try_to_pla()?, + )) + } + + Ok(vector) }) - } else { - Ok(()) + } +} + +impl TryFromPLA for csl::PlutusData { + fn try_from_pla(val: &PlutusData) -> Result { + match val { + PlutusData::Constr(tag, args) => Ok(csl::PlutusData::new_constr_plutus_data( + &csl::ConstrPlutusData::new(&tag.try_to_csl()?, &args.try_to_csl()?), + )), + PlutusData::Map(l) => Ok(csl::PlutusData::new_map(&l.try_to_csl()?)), + PlutusData::List(l) => Ok(csl::PlutusData::new_list(&l.try_to_csl()?)), + PlutusData::Integer(i) => Ok(csl::PlutusData::new_integer(&i.try_to_csl()?)), + PlutusData::Bytes(b) => Ok(csl::PlutusData::new_bytes(b.to_owned())), + } + } +} + +impl TryFromPLA> for csl::PlutusList { + fn try_from_pla(val: &Vec) -> Result { + val.iter() + // traverse + .map(|x| x.try_to_csl()) + .collect::, TryFromPLAError>>() + .map(|x| x.into()) + } +} + +impl TryFromPLA> for csl::PlutusMap { + fn try_from_pla(val: &Vec<(PlutusData, PlutusData)>) -> Result { + val.iter() + .try_fold(csl::PlutusMap::new(), |mut acc, (k, v)| { + let mut values = match acc.get(&k.try_to_csl()?) { + Some(existing_values) => existing_values, + None => csl::PlutusMapValues::new(), + }; + values.add(&v.try_to_csl()?); + acc.insert(&k.try_to_csl()?, &values); + Ok(acc) + }) + } +} + +// MARK: Aux functions + +/// Deserialise a Plutus data using parsers for each variant +pub fn case_plutus_data<'a, T>( + ctor_case: impl FnOnce(&'a BigInt) -> Box) -> T>, + list_case: impl FnOnce(&'a Vec) -> T, + int_case: impl FnOnce(&'a BigInt) -> T, + other_case: impl FnOnce(&'a PlutusData) -> T, + pd: &'a PlutusData, +) -> T { + match pd { + PlutusData::Constr(tag, args) => ctor_case(&tag)(&args), + PlutusData::List(args) => list_case(&args), + PlutusData::Integer(i) => int_case(&i), + other => other_case(&other), } } /// Given a vector of PlutusData, parse it as an array whose length is known at /// compile time. -pub fn parse_fixed_len_constr_fields<'a, const LEN: usize>( - v: &'a [PlutusData], -) -> Result<&'a [PlutusData; LEN], PlutusDataError> { +/// +/// This function is used by the derive macro. +pub fn parse_fixed_len_constr_fields( + v: &[PlutusData], +) -> Result<&[PlutusData; LEN], PlutusDataError> { v.try_into() .map_err(|_| PlutusDataError::UnexpectedListLength { got: v.len(), @@ -583,9 +619,9 @@ pub fn parse_fixed_len_constr_fields<'a, const LEN: usize>( /// Given a PlutusData, parse it as PlutusData::Constr and its tag as u32. Return /// the u32 tag and fields. -pub fn parse_constr<'a>( - data: &'a PlutusData, -) -> Result<(u32, &'a Vec), PlutusDataError> { +/// +/// This function is used by the derive macro. +pub fn parse_constr(data: &PlutusData) -> Result<(u32, &Vec), PlutusDataError> { match data { PlutusData::Constr(tag, fields) => u32::try_from(tag) .map_err(|err| PlutusDataError::UnexpectedPlutusInvariant { @@ -601,18 +637,33 @@ pub fn parse_constr<'a>( } /// Given a PlutusData, parse it as PlutusData::Constr and verify its tag. -pub fn parse_constr_with_tag<'a>( - data: &'a PlutusData, +/// +/// This function is used by the derive macro. +pub fn parse_constr_with_tag( + data: &PlutusData, expected_tag: u32, -) -> Result<&'a Vec, PlutusDataError> { +) -> Result<&Vec, PlutusDataError> { let (tag, fields) = parse_constr(data)?; if tag != expected_tag { Err(PlutusDataError::UnexpectedPlutusInvariant { got: tag.to_string(), - wanted: format!("Constr tag to be: {}", expected_tag), + wanted: format!("Constr with tag {}", expected_tag), }) } else { Ok(fields) } } + +/// Given a PlutusData, parse it as PlutusData::List. Return the plutus data list. +/// +/// This function is used by the derive macro. +pub fn parse_list(data: &PlutusData) -> Result<&Vec, PlutusDataError> { + match data { + PlutusData::List(list_of_plutus_data) => Ok(list_of_plutus_data), + _ => Err(PlutusDataError::UnexpectedPlutusType { + got: PlutusType::from(data), + wanted: PlutusType::List, + }), + } +} diff --git a/plutus-ledger-api/src/utils/mod.rs b/plutus-ledger-api/src/utils/mod.rs deleted file mode 100644 index b3ef267..0000000 --- a/plutus-ledger-api/src/utils/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::{ - collections::BTreeMap, - iter::{empty, once}, -}; - -/// Create a container C from one element. -pub fn singleton(value: T) -> C -where - C: FromIterator, -{ - once(value).collect() -} - -/// Create an empty container. -pub fn none() -> C -where - C: FromIterator, -{ - empty::().collect() -} - -/// Union two BTreeMaps, call f to resolve conflicts if duplicate keys are encountered. -pub fn union_btree_maps_with V>( - f: F, - l: BTreeMap, - r: BTreeMap, -) -> BTreeMap { - r.into_iter().fold(l.clone(), |mut acc, (k, vr)| { - let v = if let Some((_, vl)) = acc.remove_entry(&k) { - f(vl, vr) - } else { - vr - }; - acc.insert(k, v); - acc - }) -} diff --git a/plutus-ledger-api/src/v1/address.rs b/plutus-ledger-api/src/v1/address.rs index 41d2245..1da054c 100644 --- a/plutus-ledger-api/src/v1/address.rs +++ b/plutus-ledger-api/src/v1/address.rs @@ -1,22 +1,35 @@ //! Types related to Cardano addresses -use crate::plutus_data::{ - verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType, -}; -use crate::v1::crypto::Ed25519PubKeyHash; -use crate::v1::script::ValidatorHash; +use std::str::FromStr; + +use anyhow::anyhow; +use cardano_serialization_lib as csl; + #[cfg(feature = "lbf")] use lbr_prelude::json::{self, Error, Json}; use num_bigint::BigInt; - #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate as plutus_ledger_api; +use crate::csl::csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA}; +use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; +use crate::plutus_data::{ + parse_constr, parse_fixed_len_constr_fields, IsPlutusData, PlutusData, PlutusDataError, +}; +use crate::v1::crypto::Ed25519PubKeyHash; +use crate::v1::script::ValidatorHash; + +///////////// +// Address // +///////////// + /// Shelley Address for wallets or validators /// /// An address consists of a payment part (credential) and a delegation part (staking_credential). /// In order to serialize an address to `bech32`, the network kind must be known. /// For a better understanding of all the Cardano address types, read [CIP 19](https://cips.cardano.org/cips/cip19/) -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct Address { @@ -24,101 +37,113 @@ pub struct Address { pub staking_credential: Option, } -impl IsPlutusData for Address { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![ - self.credential.to_plutus_data(), - self.staking_credential.to_plutus_data(), - ], - ) +impl Address { + pub fn with_extra_info(&self, network_tag: u8) -> AddressWithExtraInfo { + AddressWithExtraInfo { + address: self, + network_tag, + } } +} - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 2)?; - Ok(Address { - credential: Credential::from_plutus_data(&fields[0])?, - staking_credential: >::from_plutus_data( - &fields[1], - )?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, +impl FromStr for Address { + type Err = anyhow::Error; - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), + fn from_str(s: &str) -> std::result::Result { + let csl_addr = csl::Address::from_bech32(s) + .map_err(|err| anyhow!("Couldn't parse bech32 address: {}", err))?; + csl_addr + .try_to_pla() + .map_err(|err| anyhow!("Couldn't convert address: {}", err)) + } +} + +impl TryFromCSL for Address { + fn try_from_csl(value: &csl::Address) -> Result { + if let Some(addr) = csl::BaseAddress::from_address(value) { + Ok(Address { + credential: Credential::from_csl(&addr.payment_cred()), + staking_credential: Some(StakingCredential::from_csl(&addr.stake_cred())), + }) + } else if let Some(addr) = csl::PointerAddress::from_address(value) { + Ok(Address { + credential: Credential::from_csl(&addr.payment_cred()), + staking_credential: Some(StakingCredential::from_csl(&addr.stake_pointer())), + }) + } else if let Some(addr) = csl::EnterpriseAddress::from_address(value) { + Ok(Address { + credential: Credential::from_csl(&addr.payment_cred()), + staking_credential: None, + }) + } else { + Err(TryFromCSLError::ImpossibleConversion(format!( + "Unable to represent address {:?}", + value + ))) } } } -/// A public key hash or validator hash credential (used as a payment or a staking credential) -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Credential { - PubKey(Ed25519PubKeyHash), - Script(ValidatorHash), +#[derive(Clone, Debug)] +pub struct AddressWithExtraInfo<'a> { + pub address: &'a Address, + pub network_tag: u8, } -impl IsPlutusData for Credential { - fn to_plutus_data(&self) -> PlutusData { - match self { - Credential::PubKey(pkh) => { - PlutusData::Constr(BigInt::from(0), vec![pkh.to_plutus_data()]) - } - Credential::Script(val_hash) => { - PlutusData::Constr(BigInt::from(1), vec![val_hash.to_plutus_data()]) - } - } - } +impl TryFromPLA> for csl::Address { + fn try_from_pla(val: &AddressWithExtraInfo<'_>) -> Result { + let payment = val.address.credential.try_to_csl()?; - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 1)?; - Ok(Credential::PubKey(Ed25519PubKeyHash::from_plutus_data( - &fields[0], - )?)) + Ok(match val.address.staking_credential { + None => csl::EnterpriseAddress::new(val.network_tag, &payment).to_address(), + Some(ref sc) => match sc { + StakingCredential::Hash(c) => { + csl::BaseAddress::new(val.network_tag, &payment, &c.try_to_csl()?).to_address() } - Ok(1) => { - verify_constr_fields(&fields, 1)?; - Ok(Credential::Script(ValidatorHash::from_plutus_data( - &fields[0], - )?)) + StakingCredential::Pointer(ptr) => { + csl::PointerAddress::new(val.network_tag, &payment, &ptr.try_to_csl()?) + .to_address() } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field between 0 and 1".to_owned(), - got: flag.to_string(), - }), }, + }) + } +} - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), +impl std::fmt::Display for AddressWithExtraInfo<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let bech32_addr: Option = self + .try_to_csl() + .ok() + .and_then(|csl_addr: csl::Address| csl_addr.to_bech32(None).ok()); + match bech32_addr { + Some(addr) => write!(f, "{}", addr), + None => write!(f, "INVALID ADDRESS {:?}", self), } } } +//////////////// +// Credential // +//////////////// + +/// A public key hash or validator hash credential (used as a payment or a staking credential) +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Credential { + PubKey(Ed25519PubKeyHash), + Script(ValidatorHash), +} + #[cfg(feature = "lbf")] impl Json for Credential { fn to_json(&self) -> serde_json::Value { match self { Credential::PubKey(pkh) => { - json::json_constructor("PubKeyCredential", &vec![pkh.to_json()]) + json::json_constructor("PubKeyCredential", vec![pkh.to_json()]) } Credential::Script(val_hash) => { - json::json_constructor("ScriptCredential", &vec![val_hash.to_json()]) + json::json_constructor("ScriptCredential", vec![val_hash.to_json()]) } } } @@ -155,6 +180,32 @@ impl Json for Credential { } } +impl FromCSL for Credential { + fn from_csl(value: &csl::Credential) -> Self { + match value.kind() { + csl::CredKind::Key => { + Credential::PubKey(Ed25519PubKeyHash::from_csl(&value.to_keyhash().unwrap())) + } + csl::CredKind::Script => { + Credential::Script(ValidatorHash::from_csl(&value.to_scripthash().unwrap())) + } + } + } +} + +impl TryFromPLA for csl::Credential { + fn try_from_pla(val: &Credential) -> Result { + match val { + Credential::PubKey(pkh) => Ok(csl::Credential::from_keyhash(&pkh.try_to_csl()?)), + Credential::Script(sh) => Ok(csl::Credential::from_scripthash(&sh.0.try_to_csl()?)), + } + } +} + +/////////////////////// +// StakingCredential // +/////////////////////// + /// Credential (public key hash or pointer) used for staking #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -163,6 +214,7 @@ pub enum StakingCredential { Pointer(ChainPointer), } +// NOTE(chfanghr): ChainPointer doesn't have a IsPlutusData instance so derive doesn't work here. impl IsPlutusData for StakingCredential { fn to_plutus_data(&self) -> PlutusData { match self { @@ -185,31 +237,23 @@ impl IsPlutusData for StakingCredential { } fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 1)?; - Ok(StakingCredential::Hash(Credential::from_plutus_data( - &fields[0], - )?)) - } - Ok(1) => { - verify_constr_fields(&fields, 3)?; - Ok(StakingCredential::Pointer(ChainPointer { - slot_number: Slot::from_plutus_data(&fields[0])?, - transaction_index: TransactionIndex::from_plutus_data(&fields[1])?, - certificate_index: CertificateIndex::from_plutus_data(&fields[2])?, - })) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field between 0 and 1".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), + let (tag, fields) = parse_constr(data)?; + match tag { + 0 => { + let [field] = parse_fixed_len_constr_fields::<1>(fields)?; + Ok(Self::Hash(Credential::from_plutus_data(field)?)) + } + 1 => { + let [field_0, field_1, field_2] = parse_fixed_len_constr_fields::<3>(fields)?; + Ok(Self::Pointer(ChainPointer { + slot_number: Slot::from_plutus_data(field_0)?, + transaction_index: TransactionIndex::from_plutus_data(field_1)?, + certificate_index: CertificateIndex::from_plutus_data(field_2)?, + })) + } + _ => Err(PlutusDataError::UnexpectedPlutusInvariant { + wanted: "Constr with tag 0 or 1".to_owned(), + got: tag.to_string(), }), } } @@ -220,10 +264,10 @@ impl Json for StakingCredential { fn to_json(&self) -> serde_json::Value { match self { StakingCredential::Hash(pkh) => { - json::json_constructor("StakingHash", &vec![pkh.to_json()]) + json::json_constructor("StakingHash", vec![pkh.to_json()]) } StakingCredential::Pointer(val_hash) => { - json::json_constructor("StakingPtr", &vec![val_hash.to_json()]) + json::json_constructor("StakingPtr", vec![val_hash.to_json()]) } } } @@ -246,7 +290,7 @@ impl Json for StakingCredential { ( "StakingPtr", Box::new(|ctor_fields| match &ctor_fields[..] { - [val_hash] => Ok(StakingCredential::Pointer(Json::from_json(&val_hash)?)), + [val_hash] => Ok(StakingCredential::Pointer(Json::from_json(val_hash)?)), _ => Err(Error::UnexpectedArrayLength { wanted: 1, got: ctor_fields.len(), @@ -260,6 +304,48 @@ impl Json for StakingCredential { } } +impl FromCSL for StakingCredential { + fn from_csl(value: &csl::Credential) -> Self { + StakingCredential::Hash(Credential::from_csl(value)) + } +} + +impl TryFromPLA for csl::Credential { + fn try_from_pla(val: &StakingCredential) -> Result { + match val { + StakingCredential::Hash(c) => c.try_to_csl(), + StakingCredential::Pointer(_) => Err(TryFromPLAError::ImpossibleConversion( + "cannot represent chain pointer".into(), + )), + } + } +} + +impl FromCSL for StakingCredential { + fn from_csl(value: &csl::Pointer) -> Self { + StakingCredential::Pointer(ChainPointer::from_csl(value)) + } +} + +#[derive(Clone, Debug)] +pub struct RewardAddressWithExtraInfo<'a> { + pub staking_credential: &'a StakingCredential, + pub network_tag: u8, +} + +impl TryFromPLA> for csl::RewardAddress { + fn try_from_pla(val: &RewardAddressWithExtraInfo<'_>) -> Result { + Ok(csl::RewardAddress::new( + val.network_tag, + &val.staking_credential.try_to_csl()?, + )) + } +} + +////////////////// +// ChainPointer // +////////////////// + /// In an address, a chain pointer refers to a point of the chain containing a stake key /// registration certificate. A point is identified by 3 coordinates: /// - An absolute slot number @@ -274,51 +360,92 @@ pub struct ChainPointer { pub certificate_index: CertificateIndex, } +impl FromCSL for ChainPointer { + fn from_csl(value: &csl::Pointer) -> Self { + ChainPointer { + slot_number: Slot::from_csl(&value.slot_bignum()), + transaction_index: TransactionIndex::from_csl(&value.tx_index_bignum()), + certificate_index: CertificateIndex::from_csl(&value.cert_index_bignum()), + } + } +} + +impl TryFromPLA for csl::Pointer { + fn try_from_pla(val: &ChainPointer) -> Result { + Ok(csl::Pointer::new_pointer( + &val.slot_number.try_to_csl()?, + &val.transaction_index.try_to_csl()?, + &val.certificate_index.try_to_csl()?, + )) + } +} + +////////// +// Slot // +////////// + /// Number of slots elapsed since genesis -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct Slot(pub BigInt); -impl IsPlutusData for Slot { - fn to_plutus_data(&self) -> PlutusData { - self.0.to_plutus_data() +impl FromCSL for Slot { + fn from_csl(value: &csl::BigNum) -> Self { + Slot(BigInt::from_csl(value)) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) +impl TryFromPLA for csl::BigNum { + fn try_from_pla(val: &Slot) -> Result { + val.0.try_to_csl() } } +////////////////////// +// CertificateIndex // +////////////////////// + /// Position of the certificate in a certain transaction -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct CertificateIndex(pub BigInt); -impl IsPlutusData for CertificateIndex { - fn to_plutus_data(&self) -> PlutusData { - self.0.to_plutus_data() +impl FromCSL for CertificateIndex { + fn from_csl(value: &csl::BigNum) -> Self { + CertificateIndex(BigInt::from_csl(value)) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) +impl TryFromPLA for csl::BigNum { + fn try_from_pla(val: &CertificateIndex) -> Result { + val.0.try_to_csl() } } +////////////////////// +// TransactionIndex // +////////////////////// + /// Position of a transaction in a given slot /// This is not identical to the index of a `TransactionInput` -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionIndex(pub BigInt); -impl IsPlutusData for TransactionIndex { - fn to_plutus_data(&self) -> PlutusData { - self.0.to_plutus_data() +impl FromCSL for TransactionIndex { + fn from_csl(value: &csl::BigNum) -> Self { + TransactionIndex(BigInt::from_csl(value)) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) +impl TryFromPLA for csl::BigNum { + fn try_from_pla(val: &TransactionIndex) -> Result { + val.0.try_to_csl() } } diff --git a/plutus-ledger-api/src/v1/assoc_map.rs b/plutus-ledger-api/src/v1/assoc_map.rs index 346933c..15760dd 100644 --- a/plutus-ledger-api/src/v1/assoc_map.rs +++ b/plutus-ledger-api/src/v1/assoc_map.rs @@ -8,7 +8,11 @@ use serde::{Deserialize, Serialize}; use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError, PlutusType}; -#[derive(Debug, PartialEq, Eq, Clone)] +////////////// +// AssocMap // +////////////// + +#[derive(Debug, PartialEq, Eq, Clone, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct AssocMap(pub Vec<(K, V)>); @@ -26,9 +30,7 @@ impl AssocMap { where K: PartialEq, { - let vec = &mut self.0; - - let old_value = vec.into_iter().find(|(k, _v)| k == &key); + let old_value = self.0.iter_mut().find(|(k, _v)| k == &key); match old_value { None => { self.0.push((key, value)); @@ -68,8 +70,8 @@ impl AssocMap { impl IsPlutusData for AssocMap { fn to_plutus_data(&self) -> PlutusData { PlutusData::Map( - (&self.0) - .into_iter() + self.0 + .iter() .map(|(k, v)| (k.to_plutus_data(), v.to_plutus_data())) .collect(), ) @@ -78,7 +80,7 @@ impl IsPlutusData for AssocMap { fn from_plutus_data(plutus_data: &PlutusData) -> Result { match plutus_data { PlutusData::Map(pairs) => pairs - .into_iter() + .iter() .map(|(k, v)| Ok((K::from_plutus_data(k)?, V::from_plutus_data(v)?))) .collect::, PlutusDataError>>() .map(Self), @@ -124,8 +126,8 @@ impl From> for AssocMap { impl Json for AssocMap { fn to_json(&self) -> serde_json::Value { json_array( - (&self.0) - .into_iter() + self.0 + .iter() .map(|(k, v)| json_array(vec![k.to_json(), v.to_json()])) .collect(), ) diff --git a/plutus-ledger-api/src/v1/crypto.rs b/plutus-ledger-api/src/v1/crypto.rs index ca3d743..25ed6bd 100644 --- a/plutus-ledger-api/src/v1/crypto.rs +++ b/plutus-ledger-api/src/v1/crypto.rs @@ -1,84 +1,84 @@ //! Types for cryptographic primitives, and other lower level building blocks -use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError, PlutusType}; +use cardano_serialization_lib as csl; use data_encoding::HEXLOWER; #[cfg(feature = "lbf")] use lbr_prelude::json::{Error, Json}; +use nom::{combinator::map_res, error::VerboseError, IResult}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate as plutus_ledger_api; +use crate::{ + csl::{ + csl_to_pla::FromCSL, + pla_to_csl::{TryFromPLA, TryFromPLAError}, + }, + plutus_data::IsPlutusData, +}; + +/////////////////////// +// Ed25519PubKeyHash // +/////////////////////// + /// ED25519 public key hash /// This is the standard cryptography in Cardano, commonly referred to as `PubKeyHash` in Plutus /// and other libraries -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct Ed25519PubKeyHash(pub LedgerBytes); -impl IsPlutusData for Ed25519PubKeyHash { - fn to_plutus_data(&self) -> PlutusData { - let Ed25519PubKeyHash(LedgerBytes(bytes)) = self; - PlutusData::Bytes(bytes.clone()) +impl FromCSL for Ed25519PubKeyHash { + fn from_csl(value: &csl::Ed25519KeyHash) -> Self { + Ed25519PubKeyHash(LedgerBytes(value.to_bytes())) + } +} + +impl TryFromPLA for csl::Ed25519KeyHash { + fn try_from_pla(val: &Ed25519PubKeyHash) -> Result { + csl::Ed25519KeyHash::from_bytes(val.0 .0.to_owned()) + .map_err(TryFromPLAError::CSLDeserializeError) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Bytes(bytes) => Ok(Self(LedgerBytes(bytes.clone()))), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Bytes, - got: PlutusType::from(data), - }), - } +impl FromCSL for Vec { + fn from_csl(value: &csl::RequiredSigners) -> Self { + (0..value.len()) + .map(|idx| Ed25519PubKeyHash::from_csl(&value.get(idx))) + .collect() } } +/////////////////////// +// PaymentPubKeyHash // +/////////////////////// + /// Standard public key hash used to verify a transaction witness -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct PaymentPubKeyHash(pub Ed25519PubKeyHash); -impl IsPlutusData for PaymentPubKeyHash { - fn to_plutus_data(&self) -> PlutusData { - let PaymentPubKeyHash(Ed25519PubKeyHash(LedgerBytes(bytes))) = self; - PlutusData::Bytes(bytes.clone()) - } - - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Bytes(bytes) => Ok(Self(Ed25519PubKeyHash(LedgerBytes(bytes.clone())))), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Bytes, - got: PlutusType::from(data), - }), - } - } -} +///////////////////// +// StakePubKeyHash // +///////////////////// /// Standard public key hash used to verify a staking -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct StakePubKeyHash(pub Ed25519PubKeyHash); -impl IsPlutusData for StakePubKeyHash { - fn to_plutus_data(&self) -> PlutusData { - let StakePubKeyHash(Ed25519PubKeyHash(LedgerBytes(bytes))) = self; - PlutusData::Bytes(bytes.clone()) - } - - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Bytes(bytes) => Ok(Self(Ed25519PubKeyHash(LedgerBytes(bytes.clone())))), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Bytes, - got: PlutusType::from(data), - }), - } - } -} +///////////////// +// LedgerBytes // +///////////////// /// A bytestring in the Cardano ledger context -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct LedgerBytes(pub Vec); @@ -88,20 +88,21 @@ impl std::fmt::Debug for LedgerBytes { } } -impl IsPlutusData for LedgerBytes { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Bytes(self.0.clone()) +impl std::fmt::Display for LedgerBytes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", HEXLOWER.encode(&self.0)) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Bytes(bytes) => Ok(Self(bytes.clone())), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Bytes, - got: PlutusType::from(data), - }), - } - } +/// Nom parser for LedgerBytes +/// Expects a hexadecimal string of arbitrary length (0 length is allowed) +/// E.g.: 00112233445566778899aabbcc +pub(crate) fn ledger_bytes(input: &str) -> IResult<&str, LedgerBytes, VerboseError<&str>> { + map_res(nom::character::complete::hex_digit0, |hex_bytes: &str| { + HEXLOWER + .decode(&hex_bytes.to_owned().to_ascii_lowercase().into_bytes()) + .map(LedgerBytes) + })(input) } #[cfg(feature = "lbf")] diff --git a/plutus-ledger-api/src/v1/datum.rs b/plutus-ledger-api/src/v1/datum.rs index bc3806f..c74fbbc 100644 --- a/plutus-ledger-api/src/v1/datum.rs +++ b/plutus-ledger-api/src/v1/datum.rs @@ -1,40 +1,53 @@ //! Types related to Plutus Datums -use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; + +use cardano_serialization_lib as csl; + +use crate as plutus_ledger_api; +use crate::csl::csl_to_pla::FromCSL; +use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; +use crate::plutus_data::{IsPlutusData, PlutusData}; use crate::v1::crypto::LedgerBytes; #[cfg(feature = "lbf")] use lbr_prelude::json::Json; - #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +/////////////// +// DatumHash // +/////////////// + /// blake2b-256 hash of a datum -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "lbf", derive(Json))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct DatumHash(pub LedgerBytes); -impl IsPlutusData for DatumHash { - fn to_plutus_data(&self) -> PlutusData { - self.0.to_plutus_data() +impl FromCSL for DatumHash { + fn from_csl(value: &csl::DataHash) -> Self { + DatumHash(LedgerBytes(value.to_bytes())) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) +impl TryFromPLA for csl::DataHash { + fn try_from_pla(val: &DatumHash) -> Result { + csl::DataHash::from_bytes(val.0 .0.to_owned()).map_err(TryFromPLAError::CSLDeserializeError) } } +/////////// +// Datum // +/////////// + /// Piece of information associated with a UTxO encoded into a PlutusData type. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "lbf", derive(Json))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Datum(pub PlutusData); -impl IsPlutusData for Datum { - fn to_plutus_data(&self) -> PlutusData { - self.0.clone() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) +impl TryFromPLA for csl::PlutusData { + fn try_from_pla(val: &Datum) -> Result { + val.0.try_to_csl() } } diff --git a/plutus-ledger-api/src/v1/interval.rs b/plutus-ledger-api/src/v1/interval.rs index 4f5bbd6..db827f8 100644 --- a/plutus-ledger-api/src/v1/interval.rs +++ b/plutus-ledger-api/src/v1/interval.rs @@ -1,7 +1,9 @@ //! Types related to PlutusInterval + use crate::feature_traits::FeatureTraits; use crate::plutus_data::{ - verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType, + parse_constr, parse_constr_with_tag, parse_fixed_len_constr_fields, IsPlutusData, PlutusData, + PlutusDataError, }; #[cfg(feature = "lbf")] use lbr_prelude::json::Json; @@ -10,6 +12,10 @@ use num_bigint::BigInt; use serde::{Deserialize, Serialize}; use std::cmp; +////////////// +// Interval // +////////////// + /// An abstraction over `PlutusInterval`, allowing valid values only #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -223,6 +229,10 @@ where } } +//////////////////// +// PlutusInterval // +//////////////////// + /// An interval of `T`s. /// /// The interval may be either closed or open at either end, meaning @@ -260,29 +270,19 @@ where } fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 2)?; - Ok(PlutusInterval { - from: >::from_plutus_data(&fields[0])?, - to: >::from_plutus_data(&fields[1])?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } + let fields = parse_constr_with_tag(data, 0)?; + let [field_0, field_1] = parse_fixed_len_constr_fields::<2>(fields)?; + Ok(Self { + from: IsPlutusData::from_plutus_data(field_0)?, + to: IsPlutusData::from_plutus_data(field_1)?, + }) } } +//////////////// +// UpperBound // +//////////////// + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "lbf", derive(Json))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -306,29 +306,19 @@ where } fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 2)?; - Ok(UpperBound { - bound: >::from_plutus_data(&fields[0])?, - closed: bool::from_plutus_data(&fields[1])?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } + let fields = parse_constr_with_tag(data, 0)?; + let [field_0, field_1] = parse_fixed_len_constr_fields::<2>(fields)?; + Ok(Self { + bound: IsPlutusData::from_plutus_data(field_0)?, + closed: IsPlutusData::from_plutus_data(field_1)?, + }) } } +//////////////// +// LowerBound // +//////////////// + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "lbf", derive(Json))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -352,29 +342,19 @@ where } fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 2)?; - Ok(LowerBound { - bound: >::from_plutus_data(&fields[0])?, - closed: bool::from_plutus_data(&fields[1])?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } + let fields = parse_constr_with_tag(data, 0)?; + let [field_0, field_1] = parse_fixed_len_constr_fields::<2>(fields)?; + Ok(Self { + bound: IsPlutusData::from_plutus_data(field_0)?, + closed: IsPlutusData::from_plutus_data(field_1)?, + }) } } +////////////// +// Extended // +////////////// + /// A set extended with a positive and negative infinity. #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "lbf", derive(Json))] @@ -445,31 +425,23 @@ where } fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 0)?; - Ok(Extended::NegInf) - } - Ok(1) => { - verify_constr_fields(&fields, 1)?; - Ok(Extended::Finite(IsPlutusData::from_plutus_data( - &fields[0], - )?)) - } - Ok(2) => { - verify_constr_fields(&fields, 0)?; - Ok(Extended::PosInf) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field between 0 and 1".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), + let (tag, fields) = parse_constr(data)?; + match tag { + 0 => { + let [] = parse_fixed_len_constr_fields::<0>(fields)?; + Ok(Extended::NegInf) + } + 1 => { + let [field] = parse_fixed_len_constr_fields::<1>(fields)?; + Ok(Extended::Finite(IsPlutusData::from_plutus_data(field)?)) + } + 2 => { + let [] = parse_fixed_len_constr_fields::<0>(fields)?; + Ok(Extended::PosInf) + } + _ => Err(PlutusDataError::UnexpectedPlutusInvariant { + wanted: "Constr with tag 0, 1 or 2".to_owned(), + got: tag.to_string(), }), } } diff --git a/plutus-ledger-api/src/v1/redeemer.rs b/plutus-ledger-api/src/v1/redeemer.rs index e5b9198..beeb616 100644 --- a/plutus-ledger-api/src/v1/redeemer.rs +++ b/plutus-ledger-api/src/v1/redeemer.rs @@ -1,40 +1,55 @@ //! Types related to Plutus Redeemers -use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; -use crate::v1::crypto::LedgerBytes; + +use cardano_serialization_lib as csl; + #[cfg(feature = "lbf")] use lbr_prelude::json::Json; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate as plutus_ledger_api; +use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; +use crate::plutus_data::{IsPlutusData, PlutusData}; +use crate::v1::crypto::LedgerBytes; + +////////////// +// Redeemer // +////////////// + /// Piece of information attached to a transaction when redeeming a value from a validator or a /// minting policy -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "lbf", derive(Json))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Redeemer(pub PlutusData); -impl IsPlutusData for Redeemer { - fn to_plutus_data(&self) -> PlutusData { - self.0.clone() - } +#[derive(Clone, Debug)] +pub struct RedeemerWithExtraInfo<'a> { + pub redeemer: &'a Redeemer, + pub tag: &'a csl::RedeemerTag, + pub index: u64, +} - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) +impl TryFromPLA> for csl::Redeemer { + fn try_from_pla<'a>(val: &RedeemerWithExtraInfo<'_>) -> Result { + let Redeemer(plutus_data) = val.redeemer; + Ok(csl::Redeemer::new( + val.tag, + &val.index.try_to_csl()?, + &plutus_data.try_to_csl()?, + &csl::ExUnits::new(&csl::BigNum::from(0u64), &csl::BigNum::from(0u64)), + )) } } +////////////////// +// RedeemerHash // +////////////////// + /// blake2b-256 hash of a datum -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "lbf", derive(Json))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RedeemerHash(pub LedgerBytes); - -impl IsPlutusData for RedeemerHash { - fn to_plutus_data(&self) -> PlutusData { - self.0.to_plutus_data() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) - } -} diff --git a/plutus-ledger-api/src/v1/script.rs b/plutus-ledger-api/src/v1/script.rs index 4b5b612..28dd358 100644 --- a/plutus-ledger-api/src/v1/script.rs +++ b/plutus-ledger-api/src/v1/script.rs @@ -1,55 +1,102 @@ //! Types related to Plutus scripts -use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; -use crate::v1::crypto::LedgerBytes; + +use cardano_serialization_lib as csl; + #[cfg(feature = "lbf")] use lbr_prelude::json::Json; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate as plutus_ledger_api; +use crate::aux::guard_bytes; +use crate::csl::csl_to_pla::FromCSL; +use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; +use crate::error::ConversionError; +use crate::plutus_data::IsPlutusData; +use crate::v1::crypto::LedgerBytes; + +/////////////////// +// ValidatorHash // +/////////////////// + /// Identifier of a validator script -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct ValidatorHash(pub ScriptHash); -impl IsPlutusData for ValidatorHash { - fn to_plutus_data(&self) -> PlutusData { - self.0.to_plutus_data() +impl FromCSL for ValidatorHash { + fn from_csl(value: &csl::ScriptHash) -> Self { + ValidatorHash(ScriptHash::from_csl(value)) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) +impl ValidatorHash { + pub fn from_bytes(bytes: Vec) -> Result { + Ok(ValidatorHash(ScriptHash::from_bytes(bytes)?)) } } +/////////////////////// +// MintingPolicyHash // +/////////////////////// + /// Hash of a minting policy script -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct MintingPolicyHash(pub ScriptHash); -impl IsPlutusData for MintingPolicyHash { - fn to_plutus_data(&self) -> PlutusData { - self.0.to_plutus_data() +impl MintingPolicyHash { + pub fn from_bytes(bytes: Vec) -> Result { + Ok(MintingPolicyHash(ScriptHash::from_bytes(bytes)?)) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) +impl FromCSL for MintingPolicyHash { + fn from_csl(value: &csl::PolicyID) -> Self { + MintingPolicyHash(ScriptHash(LedgerBytes(value.to_bytes()))) } } +impl TryFromPLA for csl::PolicyID { + fn try_from_pla(val: &MintingPolicyHash) -> Result { + val.0.try_to_csl() + } +} + +//////////////// +// ScriptHash // +//////////////// + /// Hash of a Plutus script -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct ScriptHash(pub LedgerBytes); -impl IsPlutusData for ScriptHash { - fn to_plutus_data(&self) -> PlutusData { - self.0.to_plutus_data() +impl ScriptHash { + pub fn from_bytes(bytes: Vec) -> Result { + Ok(ScriptHash(LedgerBytes(guard_bytes( + "ScriptHash", + bytes, + 28, + )?))) } +} + +impl FromCSL for ScriptHash { + fn from_csl(value: &csl::ScriptHash) -> Self { + ScriptHash(LedgerBytes(value.to_bytes())) + } +} - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) +impl TryFromPLA for csl::ScriptHash { + fn try_from_pla(val: &ScriptHash) -> Result { + csl::ScriptHash::from_bytes(val.0 .0.to_owned()) + .map_err(TryFromPLAError::CSLDeserializeError) } } diff --git a/plutus-ledger-api/src/v1/transaction.rs b/plutus-ledger-api/src/v1/transaction.rs index 1a90448..05f25f5 100644 --- a/plutus-ledger-api/src/v1/transaction.rs +++ b/plutus-ledger-api/src/v1/transaction.rs @@ -1,27 +1,52 @@ //! Types related to Cardano transactions. +use std::{fmt, str::FromStr}; + +use anyhow::anyhow; +use cardano_serialization_lib as csl; +#[cfg(feature = "lbf")] +use lbr_prelude::json::Json; +use nom::{ + character::complete::char, + combinator::{all_consuming, map, map_res}, + error::{context, VerboseError}, + sequence::{preceded, tuple}, + Finish, IResult, +}; +use num_bigint::BigInt; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + use super::{ address::{Address, StakingCredential}, - crypto::{LedgerBytes, PaymentPubKeyHash}, + crypto::{ledger_bytes, LedgerBytes, PaymentPubKeyHash}, datum::{Datum, DatumHash}, interval::PlutusInterval, value::{CurrencySymbol, Value}, }; -use crate::plutus_data::{ - parse_constr, parse_constr_with_tag, parse_fixed_len_constr_fields, verify_constr_fields, - IsPlutusData, PlutusData, PlutusDataError, PlutusType, + +use crate::{ + self as plutus_ledger_api, + aux::{big_int, guard_bytes}, +}; +use crate::{ + csl::pla_to_csl::{TryFromPLAError, TryToCSL}, + plutus_data::IsPlutusData, +}; +use crate::{ + csl::{csl_to_pla::FromCSL, pla_to_csl::TryFromPLA}, + error::ConversionError, }; -use crate::utils::{none, singleton}; -#[cfg(feature = "lbf")] -use lbr_prelude::json::Json; -use num_bigint::BigInt; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; + +////////////////////// +// TransactionInput // +////////////////////// /// An input of a transaction /// /// Also know as `TxOutRef` from Plutus, this identifies a UTxO by its transacton hash and index /// inside the transaction -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionInput { @@ -29,81 +54,162 @@ pub struct TransactionInput { pub index: BigInt, } -impl IsPlutusData for TransactionInput { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![ - self.transaction_id.to_plutus_data(), - self.index.to_plutus_data(), - ], - ) +impl fmt::Display for TransactionInput { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}#{}", self.transaction_id.0, self.index) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 2)?; - Ok(TransactionInput { - transaction_id: TransactionHash::from_plutus_data(&fields[0])?, - index: BigInt::from_plutus_data(&fields[1])?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field between 0 and 1".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), +impl FromCSL for TransactionInput { + fn from_csl(value: &csl::TransactionInput) -> Self { + TransactionInput { + transaction_id: TransactionHash::from_csl(&value.transaction_id()), + index: BigInt::from_csl(&value.index()), } } } +impl TryFromPLA for csl::TransactionInput { + fn try_from_pla(val: &TransactionInput) -> Result { + Ok(csl::TransactionInput::new( + &val.transaction_id.try_to_csl()?, + val.index.try_to_csl()?, + )) + } +} + +impl FromCSL for Vec { + fn from_csl(value: &csl::TransactionInputs) -> Self { + (0..value.len()) + .map(|idx| TransactionInput::from_csl(&value.get(idx))) + .collect() + } +} + +impl TryFromPLA> for csl::TransactionInputs { + fn try_from_pla(val: &Vec) -> Result { + val.iter() + .try_fold(csl::TransactionInputs::new(), |mut acc, input| { + acc.add(&input.try_to_csl()?); + Ok(acc) + }) + } +} + +/// Nom parser for TransactionInput +/// Expects a transaction hash of 32 bytes in hexadecimal followed by a # and an integer index +/// E.g.: 1122334455667788990011223344556677889900112233445566778899001122#1 +pub(crate) fn transaction_input( + input: &str, +) -> IResult<&str, TransactionInput, VerboseError<&str>> { + map( + tuple((transaction_hash, preceded(char('#'), big_int))), + |(transaction_id, index)| TransactionInput { + transaction_id, + index, + }, + )(input) +} + +impl FromStr for TransactionInput { + type Err = ConversionError; + + fn from_str(s: &str) -> Result { + all_consuming(transaction_input)(s) + .finish() + .map_err(|err| { + ConversionError::ParseError(anyhow!( + "Error while parsing TransactionInput '{}': {}", + s, + err + )) + }) + .map(|(_, cs)| cs) + } +} + +///////////////////// +// TransactionHash // +///////////////////// + /// 32-bytes blake2b256 hash of a transaction body. /// /// Also known as Transaction ID or `TxID`. /// Note: Plutus docs might incorrectly state that it uses SHA256. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionHash(pub LedgerBytes); -impl IsPlutusData for TransactionHash { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(BigInt::from(0), vec![self.0.to_plutus_data()]) +impl fmt::Display for TransactionHash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 1)?; - Ok(TransactionHash(IsPlutusData::from_plutus_data(&fields[0])?)) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } +impl TransactionHash { + pub fn from_bytes(bytes: Vec) -> Result { + Ok(TransactionHash(LedgerBytes(guard_bytes( + "ScriptHash", + bytes, + 32, + )?))) + } +} + +impl FromCSL for TransactionHash { + fn from_csl(value: &csl::TransactionHash) -> Self { + TransactionHash(LedgerBytes(value.to_bytes())) + } +} + +impl TryFromPLA for csl::TransactionHash { + fn try_from_pla(val: &TransactionHash) -> Result { + csl::TransactionHash::from_bytes(val.0 .0.to_owned()) + .map_err(TryFromPLAError::CSLDeserializeError) + } +} + +/// Nom parser for TransactionHash +/// Expects a hexadecimal string representation of 32 bytes +/// E.g.: 1122334455667788990011223344556677889900112233445566778899001122 +pub(crate) fn transaction_hash(input: &str) -> IResult<&str, TransactionHash, VerboseError<&str>> { + context( + "transaction_hash", + map_res(ledger_bytes, |LedgerBytes(bytes)| { + TransactionHash::from_bytes(bytes) + }), + )(input) +} + +impl FromStr for TransactionHash { + type Err = ConversionError; + + fn from_str(s: &str) -> Result { + all_consuming(transaction_hash)(s) + .finish() + .map_err(|err| { + ConversionError::ParseError(anyhow!( + "Error while parsing TransactionHash '{}': {}", + s, + err + )) + }) + .map(|(_, cs)| cs) } } +/////////////////////// +// TransactionOutput // +/////////////////////// + /// An output of a transaction /// /// This must include the target address, the hash of the datum attached, and the amount of output /// tokens -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionOutput { @@ -112,59 +218,17 @@ pub struct TransactionOutput { pub datum_hash: Option, } -impl IsPlutusData for TransactionOutput { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![ - self.address.to_plutus_data(), - self.value.to_plutus_data(), - self.datum_hash.to_plutus_data(), - ], - ) - } - - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 3)?; - Ok(TransactionOutput { - address: Address::from_plutus_data(&fields[0])?, - value: Value::from_plutus_data(&fields[1])?, - datum_hash: >::from_plutus_data(&fields[2])?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } - } -} +/////////////// +// POSIXTime // +/////////////// /// POSIX time is measured as the number of milliseconds since 1970-01-01T00:00:00Z -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct POSIXTime(pub BigInt); -impl IsPlutusData for POSIXTime { - fn to_plutus_data(&self) -> PlutusData { - self.0.to_plutus_data() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) - } -} - #[cfg(feature = "chrono")] #[derive(thiserror::Error, Debug)] pub enum POSIXTimeConversionError { @@ -187,17 +251,26 @@ impl TryFrom for chrono::DateTime { fn try_from(posix_time: POSIXTime) -> Result, Self::Error> { let POSIXTime(millis) = posix_time; - Ok(chrono::DateTime::from_timestamp_millis( + chrono::DateTime::from_timestamp_millis( ::try_from(millis).map_err(POSIXTimeConversionError::TryFromBigIntError)?, ) - .ok_or(POSIXTimeConversionError::OutOfBoundsError)?) + .ok_or(POSIXTimeConversionError::OutOfBoundsError) } } +//////////////////// +// POSIXTimeRange // +//////////////////// + pub type POSIXTimeRange = PlutusInterval; +////////////// +// TxInInfo // +////////////// + /// An input of a pending transaction. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TxInInfo { @@ -205,49 +278,19 @@ pub struct TxInInfo { pub output: TransactionOutput, } -impl IsPlutusData for TxInInfo { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![ - self.reference.to_plutus_data(), - self.output.to_plutus_data(), - ], - ) - } - - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 2)?; - Ok(TxInInfo { - reference: TransactionInput::from_plutus_data(&fields[0])?, - output: TransactionOutput::from_plutus_data(&fields[1])?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } - } -} - impl From<(TransactionInput, TransactionOutput)> for TxInInfo { fn from((reference, output): (TransactionInput, TransactionOutput)) -> TxInInfo { TxInInfo { reference, output } } } +/////////// +// DCert // +/////////// + /// Partial representation of digests of certificates on the ledger. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub enum DCert { @@ -275,74 +318,13 @@ pub enum DCert { Mir, } -impl IsPlutusData for DCert { - fn to_plutus_data(&self) -> PlutusData { - let (tag, fields) = match self { - DCert::DelegRegKey(c) => (0u32, singleton(c.to_plutus_data())), - DCert::DelegDeRegKey(c) => (1, singleton(c.to_plutus_data())), - DCert::DelegDelegate(c, pkh) => (2, vec![c.to_plutus_data(), pkh.to_plutus_data()]), - DCert::PoolRegister(pkh, pkh1) => { - (3, vec![pkh.to_plutus_data(), pkh1.to_plutus_data()]) - } - DCert::PoolRetire(pkh, i) => (4, vec![pkh.to_plutus_data(), i.to_plutus_data()]), - DCert::Genesis => (5, none()), - DCert::Mir => (6, none()), - }; - - PlutusData::Constr(BigInt::from(tag), fields) - } - - fn from_plutus_data(data: &PlutusData) -> Result { - let (tag, fields) = parse_constr(data)?; - - match tag { - 0 => { - let [field] = parse_fixed_len_constr_fields::<1>(fields)?; - IsPlutusData::from_plutus_data(field).map(Self::DelegRegKey) - } - 1 => { - let [field] = parse_fixed_len_constr_fields::<1>(fields)?; - IsPlutusData::from_plutus_data(field).map(Self::DelegDeRegKey) - } - 2 => { - let [field1, field2] = parse_fixed_len_constr_fields::<2>(fields)?; - Ok(Self::DelegDelegate( - IsPlutusData::from_plutus_data(field1)?, - IsPlutusData::from_plutus_data(field2)?, - )) - } - 3 => { - let [field1, field2] = parse_fixed_len_constr_fields::<2>(fields)?; - Ok(Self::PoolRegister( - IsPlutusData::from_plutus_data(field1)?, - IsPlutusData::from_plutus_data(field2)?, - )) - } - 4 => { - let [field1, field2] = parse_fixed_len_constr_fields::<2>(fields)?; - Ok(Self::PoolRetire( - IsPlutusData::from_plutus_data(field1)?, - IsPlutusData::from_plutus_data(field2)?, - )) - } - 5 => { - let [] = parse_fixed_len_constr_fields::<0>(fields)?; - Ok(Self::Genesis) - } - 6 => { - let [] = parse_fixed_len_constr_fields::<0>(fields)?; - Ok(Self::Mir) - } - bad_tag => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr tag to be 0, 1, 2, 3, 4, 5 or 6".to_owned(), - got: bad_tag.to_string(), - }), - } - } -} +/////////////////// +// ScriptPurpose // +/////////////////// /// The purpose of the script that's currently running. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub enum ScriptPurpose { @@ -352,37 +334,13 @@ pub enum ScriptPurpose { Certifying(DCert), } -impl IsPlutusData for ScriptPurpose { - fn to_plutus_data(&self) -> PlutusData { - let (tag, field) = match self { - ScriptPurpose::Minting(cs) => (0u32, cs.to_plutus_data()), - ScriptPurpose::Spending(i) => (1, i.to_plutus_data()), - ScriptPurpose::Rewarding(c) => (2, c.to_plutus_data()), - ScriptPurpose::Certifying(c) => (3, c.to_plutus_data()), - }; - - PlutusData::Constr(BigInt::from(tag), singleton(field)) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - let (tag, fields) = parse_constr(plutus_data)?; - let [field] = parse_fixed_len_constr_fields(fields)?; - - match tag { - 0 => IsPlutusData::from_plutus_data(field).map(Self::Minting), - 1 => IsPlutusData::from_plutus_data(field).map(Self::Spending), - 2 => IsPlutusData::from_plutus_data(field).map(Self::Rewarding), - 3 => IsPlutusData::from_plutus_data(field).map(Self::Certifying), - bad_tag => Err(PlutusDataError::UnexpectedPlutusInvariant { - got: bad_tag.to_string(), - wanted: format!("Constr tag to be 0, 1, 2 or 3"), - }), - } - } -} +///////////////////// +// TransactionInfo // +///////////////////// /// A pending transaction as seen by validator scripts, also known as TxInfo in Plutus -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionInfo { @@ -398,69 +356,16 @@ pub struct TransactionInfo { pub id: TransactionHash, } -impl IsPlutusData for TransactionInfo { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![ - self.inputs.to_plutus_data(), - self.outputs.to_plutus_data(), - self.fee.to_plutus_data(), - self.mint.to_plutus_data(), - self.d_cert.to_plutus_data(), - self.wdrl.to_plutus_data(), - self.valid_range.to_plutus_data(), - self.signatories.to_plutus_data(), - self.datums.to_plutus_data(), - self.id.to_plutus_data(), - ], - ) - } - - fn from_plutus_data(data: &PlutusData) -> Result { - let fields = parse_constr_with_tag(data, 0)?; - let [inputs, outputs, fee, mint, d_cert, wdrl, valid_range, signatories, datums, id] = - parse_fixed_len_constr_fields(fields)?; - - Ok(Self { - inputs: IsPlutusData::from_plutus_data(inputs)?, - outputs: IsPlutusData::from_plutus_data(outputs)?, - fee: IsPlutusData::from_plutus_data(fee)?, - mint: IsPlutusData::from_plutus_data(mint)?, - d_cert: IsPlutusData::from_plutus_data(d_cert)?, - wdrl: IsPlutusData::from_plutus_data(wdrl)?, - valid_range: IsPlutusData::from_plutus_data(valid_range)?, - signatories: IsPlutusData::from_plutus_data(signatories)?, - datums: IsPlutusData::from_plutus_data(datums)?, - id: IsPlutusData::from_plutus_data(id)?, - }) - } -} +/////////////////// +// ScriptContext // +/////////////////// /// The context that is presented to the currently-executing script. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct ScriptContext { pub tx_info: TransactionInfo, pub purpose: ScriptPurpose, } - -impl IsPlutusData for ScriptContext { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![self.tx_info.to_plutus_data(), self.purpose.to_plutus_data()], - ) - } - - fn from_plutus_data(data: &PlutusData) -> Result { - let fields = parse_constr_with_tag(data, 0)?; - let [tx_info, purpose] = parse_fixed_len_constr_fields(fields)?; - - Ok(Self { - tx_info: IsPlutusData::from_plutus_data(tx_info)?, - purpose: IsPlutusData::from_plutus_data(purpose)?, - }) - } -} diff --git a/plutus-ledger-api/src/v1/value.rs b/plutus-ledger-api/src/v1/value.rs index f3a3045..41c482f 100644 --- a/plutus-ledger-api/src/v1/value.rs +++ b/plutus-ledger-api/src/v1/value.rs @@ -1,25 +1,50 @@ //! Types related to Cardano values, such as Ada and native tokens. -use crate::plutus_data::{ - verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType, + +use std::str::FromStr; +use std::string::String; +use std::{ + collections::BTreeMap, + iter::Sum, + ops::{Add, Mul, Neg, Not, Sub}, }; -use crate::utils::{singleton, union_btree_maps_with}; -use crate::v1::crypto::LedgerBytes; -use crate::v1::script::{MintingPolicyHash, ScriptHash}; +use std::{fmt, ops}; + +use anyhow::anyhow; +use cardano_serialization_lib as csl; #[cfg(feature = "lbf")] use lbr_prelude::json::{Error, Json, JsonType}; +use nom::combinator::{map, opt}; +use nom::{ + branch::alt, + character::complete::{char, space0}, + combinator::{all_consuming, eof, map_res, success}, + error::{context, VerboseError}, + multi::separated_list0, + sequence::preceded, + sequence::tuple, + Finish, IResult, +}; use num_bigint::BigInt; use num_traits::Zero; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "lbf")] use serde_json; -use std::ops; -use std::string::String; -use std::{ - collections::BTreeMap, - iter::Sum, - ops::{Add, Mul, Neg, Not, Sub}, -}; + +use crate as plutus_ledger_api; +use crate::aux::{big_int, singleton, union_b_tree_maps_with, union_btree_maps_with}; +use crate::csl::csl_to_pla::FromCSL; +use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; +use crate::error::ConversionError; +use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; +use crate::v1::crypto::LedgerBytes; +use crate::v1::script::{MintingPolicyHash, ScriptHash}; + +use super::crypto::ledger_bytes; + +//////////////////// +// CurrencySymbol // +//////////////////// /// Identifier of a currency, which could be either Ada (or tAda), or a native token represented by /// it's minting policy hash. A currency may be associated with multiple `AssetClass`es. @@ -30,22 +55,71 @@ pub enum CurrencySymbol { NativeToken(MintingPolicyHash), } +impl CurrencySymbol { + pub fn from_bytes(bytes: Vec) -> Result { + if bytes.is_empty() { + Ok(CurrencySymbol::Ada) + } else { + Ok(CurrencySymbol::NativeToken(MintingPolicyHash::from_bytes( + bytes, + )?)) + } + } + + pub fn is_ada(&self) -> bool { + match self { + CurrencySymbol::Ada => true, + CurrencySymbol::NativeToken(_) => false, + } + } +} + +impl fmt::Display for CurrencySymbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CurrencySymbol::Ada => { + if f.alternate() { + write!(f, "lovelace") + } else { + write!(f, "") + } + } + CurrencySymbol::NativeToken(symbol) => write!(f, "{}", symbol.0 .0), + } + } +} + +impl FromStr for CurrencySymbol { + type Err = ConversionError; + + fn from_str(s: &str) -> Result { + all_consuming(currency_symbol)(s) + .finish() + .map_err(|err| { + ConversionError::ParseError(anyhow!( + "Error while parsing CurrencySymbol '{}': {}", + s, + err + )) + }) + .map(|(_, cs)| cs) + } +} + impl IsPlutusData for CurrencySymbol { fn to_plutus_data(&self) -> PlutusData { match self { - CurrencySymbol::Ada => String::from("").to_plutus_data(), CurrencySymbol::NativeToken(policy_hash) => policy_hash.to_plutus_data(), + CurrencySymbol::Ada => PlutusData::Bytes(Vec::with_capacity(0)), } } fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).and_then(|bytes: LedgerBytes| { + IsPlutusData::from_plutus_data(data).map(|bytes: LedgerBytes| { if bytes.0.is_empty() { - Ok(CurrencySymbol::Ada) + CurrencySymbol::Ada } else { - Ok(CurrencySymbol::NativeToken(MintingPolicyHash(ScriptHash( - bytes, - )))) + CurrencySymbol::NativeToken(MintingPolicyHash(ScriptHash(bytes))) } }) } @@ -78,12 +152,84 @@ impl Json for CurrencySymbol { } } +/// Nom parser for CurrencySymbol +/// Expects a hexadecimal string representation of 0 (Ada) or 28 bytes (NativeToken) +pub(crate) fn currency_symbol(input: &str) -> IResult<&str, CurrencySymbol, VerboseError<&str>> { + context( + "currency symbol", + map_res(ledger_bytes, |LedgerBytes(bytes)| { + CurrencySymbol::from_bytes(bytes) + }), + )(input) +} + +/////////// +// Value // +/////////// + /// A value that can contain multiple asset classes -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[cfg_attr(feature = "lbf", derive(Json))] pub struct Value(pub BTreeMap>); +#[cfg(feature = "serde")] +mod value_serde { + use std::collections::BTreeMap; + + use num_bigint::BigInt; + use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; + + use super::{CurrencySymbol, TokenName, Value}; + + struct Assets(BTreeMap); + + impl Serialize for Value { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_seq( + self.0 + .iter() + .map(|(cur_sym, assets)| (cur_sym, Assets(assets.to_owned()))), + ) + } + } + + impl<'de> Deserialize<'de> for Value { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let vec: Vec<(CurrencySymbol, Assets)> = Vec::deserialize(deserializer)?; + + Ok(Value( + vec.into_iter().map(|(cs, assets)| (cs, assets.0)).collect(), + )) + } + } + + impl Serialize for Assets { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_seq(self.0.iter()) + } + } + + impl<'de> Deserialize<'de> for Assets { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let vec: Vec<(TokenName, BigInt)> = Vec::deserialize(deserializer)?; + + Ok(Assets(vec.into_iter().collect())) + } + } +} + impl Value { pub fn new() -> Self { Value(BTreeMap::new()) @@ -105,7 +251,7 @@ impl Value { pub fn get_token_amount(&self, cs: &CurrencySymbol, tn: &TokenName) -> BigInt { self.0 .get(cs) - .and_then(|tn_map| tn_map.get(&tn)) + .and_then(|tn_map| tn_map.get(tn)) .map_or(BigInt::zero(), Clone::clone) } @@ -124,7 +270,7 @@ impl Value { tn_map .entry(tn.clone()) .and_modify(|old_a| { - *old_a = a.clone(); + old_a.clone_from(a); }) .or_insert_with(|| a.clone()); }) @@ -143,6 +289,18 @@ impl Value { self.filter(|_, _, a| a.is_zero().not()) } + pub fn is_subset(&self, b: &Value) -> bool { + (b - self) + .normalize() + // Has negative entries? + .filter(|_, _, amount| amount < &BigInt::from(0u32)) + .is_empty() + } + + pub fn is_pure_ada(&self) -> bool { + self.0.iter().all(|(cs, _)| cs == &CurrencySymbol::Ada) + } + /// Apply a function to each token of the value, and use its result as the new amount. pub fn map_amount(self, mut f: F) -> Self where @@ -186,11 +344,93 @@ impl Value { .collect(), ) } + + /// Create a vector with each distinct value + /// Warning: is the value is not normalized, the same asset class can appear twice + pub fn flatten(&self) -> Vec<(&CurrencySymbol, &TokenName, &BigInt)> { + self.0 + .iter() + .flat_map(|(currency_symbol, assets)| { + assets + .iter() + .map(move |(token_name, amount)| (currency_symbol, token_name, amount)) + }) + .collect() + } + + pub fn unflatten(list: &[(CurrencySymbol, TokenName, BigInt)]) -> Self { + list.iter() + .fold(Value::new(), |v, (cs, tn, am)| v.insert_token(cs, tn, am)) + } } -impl Default for Value { - fn default() -> Self { - Self(BTreeMap::new()) +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut it = self + .0 + .iter() + .flat_map(|(currency_symbol, assets)| { + assets + .iter() + .map(move |(token_name, amount)| (currency_symbol, token_name, amount)) + }) + .peekable(); + while let Some((cur_sym, tn, amount)) = it.next() { + if cur_sym.is_ada() { + write!(f, "{}", amount)?; + } else if tn.is_empty() { + write!(f, "{} {}", amount, cur_sym)?; + } else { + write!(f, "{} {}.{}", amount, cur_sym, tn)?; + } + if it.peek().is_some() { + write!(f, "+")?; + } + } + + Ok(()) + } +} + +/// Nom parser for a single entry in a Value +/// Expects an integer quantity, followed by an asset class after a space character +/// (space is not required for Ada) +/// E.g.: 12 11223344556677889900112233445566778899001122334455667788.001122aabbcc +pub(crate) fn flat_value( + input: &str, +) -> IResult<&str, (CurrencySymbol, TokenName, BigInt), VerboseError<&str>> { + map( + tuple((big_int, opt(preceded(char(' '), asset_class)))), + |(amount, asset_class)| match asset_class { + None => (CurrencySymbol::Ada, TokenName::ada(), amount), + Some(AssetClass { + currency_symbol, + token_name, + }) => (currency_symbol, token_name, amount), + }, + )(input) +} + +/// Nom parser for Value +/// Expects flat Value entries divided by a `+` sign +/// E.g.: 123+12 11223344556677889900112233445566778899001122334455667788.001122aabbcc +pub(crate) fn value(input: &str) -> IResult<&str, Value, VerboseError<&str>> { + map( + separated_list0(tuple((space0, char('+'))), flat_value), + |flat_values| Value::unflatten(&flat_values), + )(input) +} + +impl FromStr for Value { + type Err = ConversionError; + + fn from_str(s: &str) -> Result { + all_consuming(value)(s) + .finish() + .map_err(|err| { + ConversionError::ParseError(anyhow!("Error while parsing Value '{}': {}", s, err)) + }) + .map(|(_, cs)| cs) } } @@ -268,15 +508,12 @@ impl Mul<&BigInt> for &Value { fn mul(self, rhs: &BigInt) -> Self::Output { Value( - (&self.0) - .into_iter() + self.0 + .iter() .map(|(cs, tn_map)| { ( cs.clone(), - tn_map - .into_iter() - .map(|(tn, q)| (tn.clone(), q * rhs)) - .collect(), + tn_map.iter().map(|(tn, q)| (tn.clone(), q * rhs)).collect(), ) }) .collect(), @@ -306,6 +543,144 @@ impl IsPlutusData for Value { } } +impl FromCSL for BTreeMap { + fn from_csl(value: &csl::Assets) -> Self { + let keys = value.keys(); + (0..keys.len()).fold(BTreeMap::new(), |mut acc, idx| { + let asset_name = keys.get(idx); + if let Some(quantity) = value.get(&asset_name) { + acc.insert( + TokenName::from_csl(&asset_name), + BigInt::from_csl(&quantity), + ); + } + acc + }) + } +} + +impl TryFromPLA> for csl::Assets { + fn try_from_pla(val: &BTreeMap) -> Result { + val.iter().try_fold(csl::Assets::new(), |mut acc, (k, v)| { + acc.insert(&k.try_to_csl()?, &v.try_to_csl()?); + Ok(acc) + }) + } +} + +impl FromCSL for Value { + fn from_csl(value: &csl::MultiAsset) -> Self { + let keys = value.keys(); + Value((0..keys.len()).fold(BTreeMap::new(), |mut acc, idx| { + let script_hash = keys.get(idx); + if let Some(assets) = value.get(&script_hash) { + let assets = BTreeMap::from_csl(&assets); + acc.insert( + CurrencySymbol::NativeToken(MintingPolicyHash::from_csl(&script_hash)), + assets, + ); + } + acc + })) + } +} + +impl FromCSL for Value { + fn from_csl(value: &csl::Value) -> Self { + let lovelaces = BigInt::from_csl(&value.coin()); + let mut pla_value = Value::ada_value(&lovelaces); + if let Some(multi_asset) = value.multiasset() { + pla_value = &pla_value + &Value::from_csl(&multi_asset) + } + pla_value + } +} + +impl TryFromPLA for csl::Value { + fn try_from_pla(val: &Value) -> Result { + let coin: csl::Coin = val + .0 + .get(&CurrencySymbol::Ada) + .and_then(|m| m.get(&TokenName::ada())) + .map_or(Ok(csl::BigNum::zero()), TryToCSL::try_to_csl)?; + + let m_ass = val + .0 + .iter() + .filter_map(|(cs, tn_map)| match &cs { + CurrencySymbol::Ada => None, + CurrencySymbol::NativeToken(h) => Some((h, tn_map)), + }) + .try_fold(csl::MultiAsset::new(), |mut acc, (cs, ass)| { + acc.insert(&cs.try_to_csl()?, &ass.try_to_csl()?); + Ok(acc) + })?; + + let mut v = csl::Value::new(&coin); + + v.set_multiasset(&m_ass); + + Ok(v) + } +} + +impl FromCSL for BTreeMap { + fn from_csl(m_ass: &csl::MintAssets) -> Self { + let keys = m_ass.keys(); + (0..keys.len()) + .map(|idx| { + let key = keys.get(idx); + let value = m_ass.get(&key).unwrap(); + (TokenName::from_csl(&key), BigInt::from_csl(&value)) + }) + .collect() + } +} + +impl FromCSL for BTreeMap { + fn from_csl(value: &csl::MintsAssets) -> Self { + (0..value.len()) + .map(|idx| value.get(idx).unwrap()) + .fold(BTreeMap::new(), |acc, m| { + let ass = BTreeMap::from_csl(&m); + union_b_tree_maps_with(|l, r| l + r, [&acc, &ass]) + }) + } +} + +impl TryFromPLA> for csl::MintAssets { + fn try_from_pla(val: &BTreeMap) -> Result { + val.iter() + .try_fold(csl::MintAssets::new(), |mut acc, (k, v)| { + acc.insert(&k.try_to_csl()?, &v.try_to_csl()?) + .map_err(TryFromPLAError::CSLJsError)?; + Ok(acc) + }) + } +} + +impl FromCSL for Value { + fn from_csl(mint: &csl::Mint) -> Self { + let keys = mint.keys(); + Value( + (0..keys.len()) + .map(|idx| { + let sh = keys.get(idx); + let ass = mint.get(&sh).unwrap_or(csl::MintsAssets::new()); + ( + CurrencySymbol::NativeToken(MintingPolicyHash::from_csl(&sh)), + BTreeMap::from_csl(&ass), + ) + }) + .collect::>>(), + ) + } +} + +/////////////// +// TokenName // +/////////////// + /// Name of a token. This can be any arbitrary bytearray #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -313,25 +688,64 @@ impl IsPlutusData for Value { pub struct TokenName(pub LedgerBytes); impl TokenName { + /// Ada tokenname (empty bytestring) pub fn ada() -> TokenName { TokenName(LedgerBytes(Vec::with_capacity(0))) } -} -impl TokenName { - pub fn from_bytes(bytes: Vec) -> Self { - TokenName(LedgerBytes(bytes)) + pub fn is_empty(&self) -> bool { + self.0 .0.is_empty() + } + + pub fn from_bytes(bytes: Vec) -> Result { + if bytes.len() <= 32 { + Ok(TokenName(LedgerBytes(bytes))) + } else { + Err(ConversionError::invalid_bytestring_length( + "TokenName", + 32, + "less than or equal to", + &bytes, + )) + } } - pub fn from_string(str: &str) -> Self { - TokenName(LedgerBytes(String::from(str).into_bytes())) + /// Convert a UTF8 string into a TokenName (use from_str to convert from a hexadecimal string) + pub fn from_string(str: &str) -> Result { + TokenName::from_bytes(String::from(str).into_bytes()) } + /// Convert TokenName to string if it is a valid UTF8 bytestring pub fn try_into_string(self) -> Result { String::from_utf8(self.0 .0) } } +impl FromStr for TokenName { + type Err = ConversionError; + + fn from_str(s: &str) -> Result { + all_consuming(token_name)(s) + .finish() + .map_err(|err| { + ConversionError::ParseError(anyhow!( + "Error while parsing TokenName '{}': {}", + s, + err + )) + }) + .map(|(_, cs)| cs) + } +} + +/// Nom parser for TokenName +/// Expects a hexadecimal string representation of up to 32 +pub(crate) fn token_name(input: &str) -> IResult<&str, TokenName, VerboseError<&str>> { + map_res(ledger_bytes, |LedgerBytes(bytes)| { + TokenName::from_bytes(bytes) + })(input) +} + impl IsPlutusData for TokenName { fn to_plutus_data(&self) -> PlutusData { self.0.to_plutus_data() @@ -342,8 +756,39 @@ impl IsPlutusData for TokenName { } } +impl fmt::Display for TokenName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + let utf8_str = std::str::from_utf8(&self.0 .0); + + match utf8_str { + Ok(str) => write!(f, "{}", str), + Err(_) => write!(f, "0x{}", self.0), + } + } else { + write!(f, "{}", self.0) + } + } +} + +impl FromCSL for TokenName { + fn from_csl(value: &csl::AssetName) -> Self { + TokenName(LedgerBytes(value.name())) + } +} + +impl TryFromPLA for csl::AssetName { + fn try_from_pla(val: &TokenName) -> Result { + csl::AssetName::new(val.0 .0.to_owned()).map_err(TryFromPLAError::CSLJsError) + } +} + +//////////////// +// AssetClass // +//////////////// + /// AssetClass is uniquely identifying a specific asset -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct AssetClass { @@ -351,50 +796,62 @@ pub struct AssetClass { pub token_name: TokenName, } -impl IsPlutusData for AssetClass { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![ - self.currency_symbol.to_plutus_data(), - self.token_name.to_plutus_data(), - ], - ) +impl fmt::Display for AssetClass { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.token_name.is_empty() { + write!(f, "{}", self.currency_symbol) + } else { + write!(f, "{}.{}", self.currency_symbol, self.token_name) + } } +} - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 2)?; - Ok(AssetClass { - currency_symbol: CurrencySymbol::from_plutus_data(&fields[0])?, - token_name: TokenName::from_plutus_data(&fields[1])?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field between 0 and 1".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } +impl FromStr for AssetClass { + type Err = ConversionError; + + fn from_str(s: &str) -> Result { + all_consuming(asset_class)(s) + .finish() + .map_err(|err| { + ConversionError::ParseError(anyhow!( + "Error while parsing AssetClass '{}': {}", + s, + err + )) + }) + .map(|(_, cs)| cs) } } -#[cfg(test)] -mod test { - use super::*; +/// Nom parser for AssetClass +/// Expects a currency symbol and token name both in hexadecimal format, divided by a `.` +/// In case the token name is empty, the divider is not required +/// E.g.: +/// - 11223344556677889900112233445566778899001122334455667788.001122aabbcc +/// - 11223344556677889900112233445566778899001122334455667788 +pub(crate) fn asset_class(input: &str) -> IResult<&str, AssetClass, VerboseError<&str>> { + let (input, cs) = currency_symbol(input)?; + + let (input, tn) = alt(( + preceded(eof, success(TokenName::ada())), + preceded(char('.'), token_name), + ))(input)?; + + Ok(( + input, + AssetClass { + currency_symbol: cs, + token_name: tn, + }, + )) +} - #[test] - fn to_from_string_token_name() { - let name = "Hello"; - let token_name = TokenName::from_string(name); +//////////////// +// Lovelace // +//////////////// - assert_eq!(token_name.try_into_string().unwrap(), name); - } -} +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct Lovelace(pub BigInt); diff --git a/plutus-ledger-api/src/v2/datum.rs b/plutus-ledger-api/src/v2/datum.rs index 41af4d3..2afce83 100644 --- a/plutus-ledger-api/src/v2/datum.rs +++ b/plutus-ledger-api/src/v2/datum.rs @@ -1,21 +1,34 @@ //! Types related to Plutus Datums -use crate::plutus_data::{ - verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType, -}; -pub use crate::v1::datum::{Datum, DatumHash}; + +use cardano_serialization_lib as csl; #[cfg(feature = "lbf")] use lbr_prelude::json::{self, Error, Json}; -use num_bigint::BigInt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate as plutus_ledger_api; +pub use crate::v1::datum::{Datum, DatumHash}; +use crate::{ + csl::{ + csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA}, + pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}, + }, + plutus_data::IsPlutusData, +}; + +// use crate as plutus_data/ +///////////////// +// OutputDatum // +///////////////// + /// Optional datum of a transaction /// /// In case an inline datum is used, the data is embedded inside the transaction body, so it can be /// directly retrieved. In case of a datum hash, an off-chain indexer is required to find the /// associated datum by its hash. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum OutputDatum { None, @@ -23,62 +36,16 @@ pub enum OutputDatum { InlineDatum(Datum), } -impl IsPlutusData for OutputDatum { - fn to_plutus_data(&self) -> PlutusData { - match self { - OutputDatum::None => PlutusData::Constr(BigInt::from(0), vec![]), - OutputDatum::DatumHash(dat_hash) => { - PlutusData::Constr(BigInt::from(1), vec![dat_hash.to_plutus_data()]) - } - OutputDatum::InlineDatum(datum) => { - PlutusData::Constr(BigInt::from(2), vec![datum.to_plutus_data()]) - } - } - } - - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 0)?; - Ok(OutputDatum::None) - } - Ok(1) => { - verify_constr_fields(&fields, 1)?; - Ok(OutputDatum::DatumHash(DatumHash::from_plutus_data( - &fields[0], - )?)) - } - Ok(2) => { - verify_constr_fields(&fields, 1)?; - Ok(OutputDatum::InlineDatum(Datum::from_plutus_data( - &fields[0], - )?)) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be between 0..2".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } - } -} - #[cfg(feature = "lbf")] impl Json for OutputDatum { fn to_json(&self) -> serde_json::Value { match self { - OutputDatum::None => json::json_constructor("NoOutputDatum", &Vec::with_capacity(0)), + OutputDatum::None => json::json_constructor("NoOutputDatum", Vec::with_capacity(0)), OutputDatum::DatumHash(dat_hash) => { - json::json_constructor("OutputDatumHash", &vec![dat_hash.to_json()]) + json::json_constructor("OutputDatumHash", vec![dat_hash.to_json()]) } OutputDatum::InlineDatum(datum) => { - json::json_constructor("OutputDatum", &vec![datum.to_json()]) + json::json_constructor("OutputDatum", vec![datum.to_json()]) } } } @@ -101,7 +68,7 @@ impl Json for OutputDatum { ( "OutputDatumHash", Box::new(|ctor_fields| match &ctor_fields[..] { - [dat_hash] => Ok(OutputDatum::DatumHash(Json::from_json(&dat_hash)?)), + [dat_hash] => Ok(OutputDatum::DatumHash(Json::from_json(dat_hash)?)), _ => Err(Error::UnexpectedArrayLength { wanted: 1, got: ctor_fields.len(), @@ -125,3 +92,29 @@ impl Json for OutputDatum { ) } } + +impl TryFromCSL for OutputDatum { + fn try_from_csl(value: &csl::OutputDatum) -> Result { + Ok(if let Some(d) = value.data() { + OutputDatum::InlineDatum(Datum(d.try_to_pla()?)) + } else if let Some(h) = value.data_hash() { + OutputDatum::DatumHash(DatumHash::from_csl(&h)) + } else { + OutputDatum::None + }) + } +} + +impl TryFromPLA for Option { + fn try_from_pla( + pla_output_datum: &OutputDatum, + ) -> Result, TryFromPLAError> { + Ok(match pla_output_datum { + OutputDatum::None => None, + OutputDatum::InlineDatum(Datum(d)) => { + Some(csl::OutputDatum::new_data(&d.try_to_csl()?)) + } + OutputDatum::DatumHash(dh) => Some(csl::OutputDatum::new_data_hash(&dh.try_to_csl()?)), + }) + } +} diff --git a/plutus-ledger-api/src/v2/transaction.rs b/plutus-ledger-api/src/v2/transaction.rs index 6dc3d43..45c1c5e 100644 --- a/plutus-ledger-api/src/v2/transaction.rs +++ b/plutus-ledger-api/src/v2/transaction.rs @@ -1,22 +1,27 @@ //! Types related to Cardano transactions. -use crate::plutus_data::{parse_constr_with_tag, parse_fixed_len_constr_fields}; -use crate::plutus_data::{ - verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType, -}; -#[cfg(feature = "chrono")] -pub use crate::v1::transaction::POSIXTimeConversionError; -pub use crate::v1::transaction::{ - DCert, POSIXTime, POSIXTimeRange, ScriptPurpose, TransactionHash, TransactionInput, -}; + +use std::collections::BTreeMap; + +use cardano_serialization_lib as csl; #[cfg(feature = "lbf")] use lbr_prelude::json::Json; use num_bigint::BigInt; - #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate as plutus_ledger_api; +use crate::csl::csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA}; +use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; +use crate::plutus_data::IsPlutusData; +#[cfg(feature = "chrono")] +pub use crate::v1::transaction::POSIXTimeConversionError; +pub use crate::v1::transaction::{ + DCert, POSIXTime, POSIXTimeRange, ScriptPurpose, TransactionHash, TransactionInput, +}; + +use super::address::AddressWithExtraInfo; use super::{ - address::{Address, StakingCredential}, + address::{Address, RewardAddressWithExtraInfo, StakingCredential}, assoc_map::AssocMap, crypto::PaymentPubKeyHash, datum::{Datum, DatumHash, OutputDatum}, @@ -25,11 +30,16 @@ use super::{ value::Value, }; +/////////////////////// +// TransactionOutput // +/////////////////////// + /// An output of a transaction /// /// This must include the target address, an optional datum, an optional reference script, and the /// amount of output tokens -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionOutput { @@ -39,47 +49,124 @@ pub struct TransactionOutput { pub reference_script: Option, } -impl IsPlutusData for TransactionOutput { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![ - self.address.to_plutus_data(), - self.value.to_plutus_data(), - self.datum.to_plutus_data(), - self.reference_script.to_plutus_data(), - ], - ) +impl TryFromCSL for TransactionOutput { + fn try_from_csl(value: &csl::TransactionOutput) -> Result { + Ok(TransactionOutput { + address: value.address().try_to_pla()?, + datum: if value.has_data_hash() { + OutputDatum::DatumHash(DatumHash::from_csl(&value.data_hash().unwrap())) + } else if value.has_plutus_data() { + OutputDatum::InlineDatum(Datum(value.plutus_data().unwrap().try_to_pla()?)) + } else { + OutputDatum::None + }, + reference_script: if value.has_script_ref() { + let script_ref = value.script_ref().unwrap(); + let script_hash = if script_ref.is_native_script() { + script_ref.native_script().unwrap().hash() + } else { + script_ref.plutus_script().unwrap().hash() + }; + Some(ScriptHash::from_csl(&script_hash)) + } else { + None + }, + value: Value::from_csl(&value.amount()), + }) } +} - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 4)?; - Ok(TransactionOutput { - address: Address::from_plutus_data(&fields[0])?, - value: Value::from_plutus_data(&fields[1])?, - datum: OutputDatum::from_plutus_data(&fields[2])?, - reference_script: >::from_plutus_data(&fields[3])?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, +impl TryFromCSL for Vec { + fn try_from_csl(value: &csl::TransactionOutputs) -> Result { + (0..value.len()) + .map(|idx| TransactionOutput::try_from_csl(&value.get(idx))) + .collect() + } +} - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), +#[derive(Clone, Debug)] +pub struct TransactionOutputWithExtraInfo<'a> { + pub transaction_output: &'a TransactionOutput, + pub scripts: &'a BTreeMap, + pub network_id: u8, + pub data_cost: &'a csl::DataCost, +} + +impl TryFromPLA> for csl::TransactionOutput { + fn try_from_pla(val: &TransactionOutputWithExtraInfo<'_>) -> Result { + let mut output_builder = csl::TransactionOutputBuilder::new().with_address( + &AddressWithExtraInfo { + address: &val.transaction_output.address, + network_tag: val.network_id, + } + .try_to_csl()?, + ); + + output_builder = match &val.transaction_output.datum { + OutputDatum::None => output_builder, + OutputDatum::InlineDatum(Datum(d)) => output_builder.with_plutus_data(&d.try_to_csl()?), + OutputDatum::DatumHash(dh) => output_builder.with_data_hash(&dh.try_to_csl()?), + }; + + let script_ref = val + .transaction_output + .reference_script + .clone() + .map(|script_hash| -> Result<_, TryFromPLAError> { + let script = val + .scripts + .get(&script_hash) + .ok_or(TryFromPLAError::MissingScript(script_hash))?; + Ok(csl::ScriptRef::new_plutus_script(script)) + }) + .transpose()?; + + if let Some(script_ref) = &script_ref { + output_builder = output_builder.with_script_ref(script_ref); + }; + + let value_without_min_utxo = val.transaction_output.value.try_to_csl()?; + + let mut calc = csl::MinOutputAdaCalculator::new_empty(val.data_cost) + .map_err(TryFromPLAError::CSLJsError)?; + calc.set_amount(&value_without_min_utxo); + match &val.transaction_output.datum { + OutputDatum::None => {} + OutputDatum::InlineDatum(Datum(d)) => { + calc.set_plutus_data(&d.try_to_csl()?); + } + OutputDatum::DatumHash(dh) => { + calc.set_data_hash(&dh.try_to_csl()?); + } + }; + if let Some(script_ref) = script_ref { + calc.set_script_ref(&script_ref); } + + let required_coin = calc.calculate_ada().map_err(TryFromPLAError::CSLJsError)?; + let coin = std::cmp::max(value_without_min_utxo.coin(), required_coin); + + let value = match value_without_min_utxo.multiasset() { + Some(multiasset) => csl::Value::new_with_assets(&coin, &multiasset), + None => csl::Value::new(&coin), + }; + + output_builder + .next() + .map_err(TryFromPLAError::CSLJsError)? + .with_value(&value) + .build() + .map_err(TryFromPLAError::CSLJsError) } } +////////////// +// TxInInfo // +////////////// + /// An input of a pending transaction. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TxInInfo { @@ -93,43 +180,12 @@ impl From<(TransactionInput, TransactionOutput)> for TxInInfo { } } -impl IsPlutusData for TxInInfo { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![ - self.reference.to_plutus_data(), - self.output.to_plutus_data(), - ], - ) - } - - fn from_plutus_data(data: &PlutusData) -> Result { - match data { - PlutusData::Constr(flag, fields) => match u32::try_from(flag) { - Ok(0) => { - verify_constr_fields(&fields, 2)?; - Ok(TxInInfo { - reference: TransactionInput::from_plutus_data(&fields[0])?, - output: TransactionOutput::from_plutus_data(&fields[1])?, - }) - } - _ => Err(PlutusDataError::UnexpectedPlutusInvariant { - wanted: "Constr field to be 0".to_owned(), - got: flag.to_string(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } - } -} +// TransactionInfo // +///////////////////// /// A pending transaction as seen by validator scripts, also known as TxInfo in Plutus -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionInfo { @@ -147,73 +203,41 @@ pub struct TransactionInfo { pub id: TransactionHash, } -impl IsPlutusData for TransactionInfo { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![ - self.inputs.to_plutus_data(), - self.reference_inputs.to_plutus_data(), - self.outputs.to_plutus_data(), - self.fee.to_plutus_data(), - self.mint.to_plutus_data(), - self.d_cert.to_plutus_data(), - self.wdrl.to_plutus_data(), - self.valid_range.to_plutus_data(), - self.signatories.to_plutus_data(), - self.redeemers.to_plutus_data(), - self.datums.to_plutus_data(), - self.id.to_plutus_data(), - ], - ) - } +#[derive(Clone, Debug)] +pub struct WithdrawalsWithExtraInfo<'a> { + pub withdrawals: &'a AssocMap, + pub network_tag: u8, +} - fn from_plutus_data(data: &PlutusData) -> Result { - let fields = parse_constr_with_tag(data, 0)?; - let [inputs, reference_inputs, outputs, fee, mint, d_cert, wdrl, valid_range, signatories, redeemers, datums, id] = - parse_fixed_len_constr_fields(fields)?; - - Ok(Self { - inputs: IsPlutusData::from_plutus_data(inputs)?, - reference_inputs: IsPlutusData::from_plutus_data(reference_inputs)?, - outputs: IsPlutusData::from_plutus_data(outputs)?, - fee: IsPlutusData::from_plutus_data(fee)?, - mint: IsPlutusData::from_plutus_data(mint)?, - d_cert: IsPlutusData::from_plutus_data(d_cert)?, - wdrl: IsPlutusData::from_plutus_data(wdrl)?, - valid_range: IsPlutusData::from_plutus_data(valid_range)?, - signatories: IsPlutusData::from_plutus_data(signatories)?, - redeemers: IsPlutusData::from_plutus_data(redeemers)?, - datums: IsPlutusData::from_plutus_data(datums)?, - id: IsPlutusData::from_plutus_data(id)?, - }) +impl TryFromPLA> for csl::Withdrawals { + fn try_from_pla(val: &WithdrawalsWithExtraInfo<'_>) -> Result { + val.withdrawals + .0 + .iter() + .try_fold(csl::Withdrawals::new(), |mut acc, (s, q)| { + acc.insert( + &RewardAddressWithExtraInfo { + staking_credential: s, + network_tag: val.network_tag, + } + .try_to_csl()?, + &q.try_to_csl()?, + ); + Ok(acc) + }) } } +/////////////////// +// ScriptContext // +/////////////////// + /// The context that is presented to the currently-executing script. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct ScriptContext { pub tx_info: TransactionInfo, pub purpose: ScriptPurpose, } - -impl IsPlutusData for ScriptContext { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - vec![self.tx_info.to_plutus_data(), self.purpose.to_plutus_data()], - ) - } - - fn from_plutus_data(data: &PlutusData) -> Result { - let fields = parse_constr_with_tag(data, 0)?; - let [tx_info, purpose] = parse_fixed_len_constr_fields(fields)?; - - Ok(Self { - tx_info: IsPlutusData::from_plutus_data(tx_info)?, - purpose: IsPlutusData::from_plutus_data(purpose)?, - }) - } -} diff --git a/plutus-ledger-api/src/v3/mod.rs b/plutus-ledger-api/src/v3/mod.rs new file mode 100644 index 0000000..6350950 --- /dev/null +++ b/plutus-ledger-api/src/v3/mod.rs @@ -0,0 +1,15 @@ +//! Plutus types and utilities for Plutus V3 +//! +//! Types and utilities unchanged in the new version are re-exported from the v2 module. +pub mod ratio; +pub mod transaction; + +// Inherited from v2 +pub use crate::v2::address; +pub use crate::v2::assoc_map; +pub use crate::v2::crypto; +pub use crate::v2::datum; +pub use crate::v2::interval; +pub use crate::v2::redeemer; +pub use crate::v2::script; +pub use crate::v2::value; diff --git a/plutus-ledger-api/src/v3/ratio.rs b/plutus-ledger-api/src/v3/ratio.rs new file mode 100644 index 0000000..c134719 --- /dev/null +++ b/plutus-ledger-api/src/v3/ratio.rs @@ -0,0 +1,36 @@ +//! Types related to rational values + +#[cfg(feature = "lbf")] +use lbr_prelude::json::Json; +use num_bigint::BigInt; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; + +// TODO(chfanghr): maintain the invariants mentioned here: https://github.com/IntersectMBO/plutus/blob/master/plutus-tx/src/PlutusTx/Ratio.hs#L65-L68 +/// Represents an arbitrary-precision ratio. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct Rational( + /// numerator + pub BigInt, + /// denominator + pub BigInt, +); + +impl IsPlutusData for Rational { + fn to_plutus_data(&self) -> PlutusData { + (self.0.clone(), self.1.clone()).to_plutus_data() + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result + where + Self: Sized, + { + let (n, d) = IsPlutusData::from_plutus_data(plutus_data)?; + + Ok(Self(n, d)) + } +} diff --git a/plutus-ledger-api/src/v3/transaction.rs b/plutus-ledger-api/src/v3/transaction.rs new file mode 100644 index 0000000..124a0d5 --- /dev/null +++ b/plutus-ledger-api/src/v3/transaction.rs @@ -0,0 +1,279 @@ +//! Types related to Cardano transactions. + +#[cfg(feature = "lbf")] +use lbr_prelude::json::Json; +use num_bigint::BigInt; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +pub use crate::v2::transaction::{ + DCert, POSIXTime, POSIXTimeRange, TransactionHash, TransactionInput, TransactionOutput, + TxInInfo, +}; +use crate::{ + self as plutus_ledger_api, + plutus_data::{IsPlutusData, PlutusData}, + v2::{ + address::{Credential, StakingCredential}, + assoc_map::AssocMap, + crypto::{PaymentPubKeyHash, StakePubKeyHash}, + datum::{Datum, DatumHash}, + redeemer::Redeemer, + script::ScriptHash, + value::{CurrencySymbol, Lovelace, Value}, + }, +}; + +use super::ratio::Rational; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct ColdCommitteeCredential(pub Credential); + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct HotCommitteeCredential(pub Credential); + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct DRepCredential(pub Credential); + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub enum DRep { + DRep(DRepCredential), + AlwaysAbstain, + AlwaysNoConfidence, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub enum Delegatee { + Stake(StakePubKeyHash), + Vote(DRep), + StakeVote(StakePubKeyHash, DRep), +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub enum TxCert { + /// Register staking credential with an optional deposit amount + RegStaking(StakingCredential, Option), + /// Un-Register staking credential with an optional refund amount + UnRegStaking(StakingCredential, Option), + /// Delegate staking credential to a Delegatee + DelegStaking(StakingCredential, Delegatee), + /// Register and delegate staking credential to a Delegatee in one certificate. Note that deposit is mandatory. + RegDeleg(StakingCredential, Delegatee, Lovelace), + /// Register a DRep with a deposit value. The optional anchor is omitted. + RegDRep(DRepCredential, Lovelace), + /// Update a DRep. The optional anchor is omitted. + UpdateDRep(DRepCredential), + /// UnRegister a DRep with mandatory refund value + UnRegDRep(DRepCredential, Lovelace), + /// A digest of the PoolParams + PoolRegister( + /// pool id + PaymentPubKeyHash, + // pool vrf + PaymentPubKeyHash, + ), + /// The retirement certificate and the Epoch in which the retirement will take place + PoolRetire(PaymentPubKeyHash, BigInt), + /// Authorize a Hot credential for a specific Committee member's cold credential + AuthHotCommittee(ColdCommitteeCredential, HotCommitteeCredential), + ResignColdCommittee(ColdCommitteeCredential), +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub enum Voter { + CommitteeVoter(HotCommitteeCredential), + DRepVoter(DRepCredential), + StakePoolVoter(PaymentPubKeyHash), +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub enum Vote { + VoteNo, + VoteYes, + Abstain, +} + +/// Similar to TransactionInput, but for GovernanceAction. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct GovernanceActionId { + pub tx_id: TransactionHash, + pub gov_action_id: BigInt, +} + +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct Committee { + /// Committee members with epoch number when each of them expires + pub members: AssocMap, + /// Quorum of the committee that is necessary for a successful vote + pub quorum: Rational, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct Constitution { + /// Optional guardrail script + pub constitution_script: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct ProtocolVersion { + pub major: BigInt, + pub minor: BigInt, +} + +// TODO(chfanghr): check invariant according to https://github.com/IntersectMBO/plutus/blob/bb33f082d26f8b6576d3f0d423be53eddfb6abd8/plutus-ledger-api/src/PlutusLedgerApi/V3/Contexts.hs#L338-L364 +/// A Plutus Data object containing proposed parameter changes. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Newtype"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct ChangeParameters(pub PlutusData); + +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub enum GovernanceAction { + /// Propose to change the protocol parameters + ParameterChange( + Option, + ChangeParameters, + // The hash of the constitution script + Option, + ), + /// Propose to update protocol version + HardForkInitiation(Option, ProtocolVersion), + /// Propose to withdraw from the cardano treasury + TreasuryWithdrawals( + AssocMap, + // The hash of the constitution script + Option, + ), + /// Propose to create a state of no-confidence in the current constitutional committee + NoConfidence(Option), + /// Propose to update the members of the constitutional committee and/or its signature threshold and/or terms + UpdateCommittee( + Option, + /// Committee members to be removed + Vec, + /// Committee members to be added + AssocMap, + /// New quorum + Rational, + ), + /// Propose to modify the constitution or its guardrail script + NewConstitution(Option, Constitution), + InfoAction, +} + +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct ProtocolProcedure { + pub deposit: Lovelace, + pub return_addr: Credential, + pub governance_action: GovernanceAction, +} + +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub enum ScriptPurpose { + Minting(CurrencySymbol), + Spending(TransactionInput), + Rewarding(Credential), + Certifying( + /// 0-based index of the given `TxCert` in `the `tx_certs` field of the `TransactionInfo` + BigInt, + TxCert, + ), + Voting(Voter), + Proposing( + /// 0-based index of the given `ProposalProcedure` in `protocol_procedures` field of the `TransactionInfo` + BigInt, + ProtocolProcedure, + ), +} + +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub enum ScriptInfo { + Minting(CurrencySymbol), + Spending(TransactionInput, Option), + Rewarding(Credential), + Certifying(BigInt, TxCert), + Voting(Voter), + Proposing(BigInt, ProtocolProcedure), +} + +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct TransactionInfo { + pub inputs: Vec, + pub reference_inputs: Vec, + pub outputs: Vec, + pub fee: Lovelace, + pub mint: Value, + pub tx_certs: Vec, + pub wdrl: AssocMap, + pub valid_range: POSIXTimeRange, + pub signatories: Vec, + pub redeemers: AssocMap, + pub datums: AssocMap, + pub id: TransactionHash, + pub votes: AssocMap>, + pub protocol_procedures: Vec, + pub current_treasury_amount: Option, + pub treasury_donation: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "lbf", derive(Json))] +pub struct ScriptContext { + pub tx_info: TransactionInfo, + pub redeemer: Redeemer, + pub script_info: ScriptInfo, +} diff --git a/plutus-ledger-api/tests/csl.rs b/plutus-ledger-api/tests/csl.rs new file mode 100644 index 0000000..78a7fa2 --- /dev/null +++ b/plutus-ledger-api/tests/csl.rs @@ -0,0 +1,132 @@ +#[cfg(test)] +mod csl_pla_roundtrip_tests { + use cardano_serialization_lib as csl; + use num_bigint::BigInt; + use plutus_ledger_api::{ + csl::{ + csl_to_pla::{FromCSL, TryFromCSL}, + pla_to_csl::TryToCSL, + }, + generators::correct::v1::{ + arb_address, arb_certificate_index, arb_chain_pointer, arb_credential, arb_datum_hash, + arb_ed25519_pub_key_hash, arb_minting_policy_hash, arb_plutus_data, arb_script_hash, + arb_slot, arb_token_name, arb_transaction_hash, arb_transaction_index, + arb_transaction_input, arb_value, + }, + v1::{ + address::{Address, StakingCredential}, + value::Value, + }, + }; + use proptest::{prop_assert_eq, proptest, strategy::Strategy, test_runner::TestCaseError}; + + fn try_to_from_prop + FromCSL + PartialEq + std::fmt::Debug>( + v: A, + ) -> Result<(), TestCaseError> { + Ok(prop_assert_eq!( + A::from_csl(&>::try_to_csl(&v)?), + v + )) + } + + fn try_to_try_from_prop + TryFromCSL + PartialEq + std::fmt::Debug>( + v: A, + ) -> Result<(), TestCaseError> { + Ok(prop_assert_eq!( + A::try_from_csl(&>::try_to_csl(&v)?)?, + v + )) + } + + proptest! { + #[test] + fn test_token_name(val in arb_token_name()) { + try_to_from_prop(val)? + } + + #[test] + fn test_minting_policy_hash(val in arb_minting_policy_hash()) { + try_to_from_prop(val)? + } + + // This is special because the CSL machinery always puts in at least a zero Ada in the value + // But the arbitrary generated value by PLA does not. + #[test] + fn test_value(val in arb_value()) { + let csl_val: csl::Value = val.try_to_csl()?; + prop_assert_eq!( + Value::from_csl(&csl_val), + // Add a zero ada value. + Value::ada_value(&BigInt::from(0)) + val + ) + } + + #[test] + fn test_transaction_hash(val in arb_transaction_hash()) { + try_to_from_prop(val)? + } + + #[test] + fn test_transaction_input(val in arb_transaction_input()) { + try_to_from_prop(val)? + } + + #[test] + fn test_ed25519_pub_key_hash(val in arb_ed25519_pub_key_hash()) { + try_to_from_prop::(val)? + } + + #[test] + fn test_script_hash(val in arb_script_hash()) { + try_to_from_prop(val)? + } + + #[test] + fn test_staking_credential(val in arb_credential().prop_map(StakingCredential::Hash)) { + try_to_from_prop::(val)? + } + + #[test] + fn test_credential(val in arb_credential()) { + try_to_from_prop(val)? + } + + #[test] + fn test_slot(val in arb_slot()) { + try_to_from_prop(val)? + } + + #[test] + fn test_transaction_index(val in arb_transaction_index()) { + try_to_from_prop(val)? + } + + #[test] + fn test_certificate_index(val in arb_certificate_index()) { + try_to_from_prop(val)? + } + + #[test] + fn test_chain_pointer(val in arb_chain_pointer()) { + try_to_from_prop(val)? + } + + #[test] + fn test_plutus_data(val in arb_plutus_data()) { + try_to_try_from_prop(val)? + } + + #[test] + fn test_datum_hash(val in arb_datum_hash()) { + try_to_from_prop(val)? + } + + #[test] + fn test_address(val in arb_address()) { + prop_assert_eq!( + Address::try_from_csl(&val.with_extra_info(1).try_to_csl()?)?, + val + ) + } + } +} diff --git a/plutus-ledger-api/tests/display.rs b/plutus-ledger-api/tests/display.rs new file mode 100644 index 0000000..c0318aa --- /dev/null +++ b/plutus-ledger-api/tests/display.rs @@ -0,0 +1,143 @@ +#[cfg(test)] +mod display_serialisation_tests { + mod golden_v1 { + use plutus_ledger_api::{ + goldens::v1::{ + sample_address, sample_asset_class, sample_currency_symbol, + sample_transaction_input, sample_value, + }, + v1::value::CurrencySymbol, + v3::value::TokenName, + }; + + #[test] + fn v1_currency_symbol_display_1() { + goldie::assert!(format!("{}", sample_currency_symbol())) + } + + #[test] + fn v1_currency_symbol_display_2() { + goldie::assert!(format!("{}", CurrencySymbol::Ada)) + } + + #[test] + fn v1_currency_symbol_display_3() { + goldie::assert!(format!("{:#}", CurrencySymbol::Ada)) + } + + #[test] + fn v1_token_name_display_1() { + goldie::assert!(format!( + "{}", + TokenName::from_bytes(vec![255, 244, 233, 222]).unwrap() + )) + } + + #[test] + fn v1_token_name_display_2() { + goldie::assert!(format!("{}", TokenName::from_string("TestToken").unwrap())) + } + + #[test] + fn v1_token_name_display_3() { + goldie::assert!(format!( + "{:#}", + TokenName::from_bytes(vec![255, 244, 233, 222]).unwrap() + )) + } + + #[test] + fn v1_token_name_display_4() { + goldie::assert!(format!( + "{:#}", + TokenName::from_string("TestToken").unwrap() + )) + } + + #[test] + fn v1_asset_class_display() { + goldie::assert!(format!("{}", sample_asset_class())) + } + + #[test] + fn v1_value_display_1() { + goldie::assert!(format!("{}", sample_value())) + } + + #[test] + fn v1_value_display_2() { + goldie::assert!(format!("{:#}", sample_value())) + } + + #[test] + fn v1_address_display_1() { + goldie::assert!(format!("{}", sample_address().with_extra_info(0))) + } + + #[test] + fn v1_address_display_2() { + goldie::assert!(format!("{}", sample_address().with_extra_info(1))) + } + + #[test] + fn v1_transaction_input_display() { + goldie::assert!(format!("{}", sample_transaction_input())) + } + } + + mod props_v1 { + use std::{fmt::Display, str::FromStr}; + + use plutus_ledger_api::{ + generators::correct::v1::{ + arb_address, arb_asset_class, arb_currency_symbol, arb_transaction_input, arb_value, + }, + v1::{address::Address, value::TokenName}, + }; + use proptest::{prelude::*, string::string_regex}; + + fn from_to_string(val: &T) -> Result + where + T: FromStr + Display + PartialEq, + { + T::from_str(&val.to_string()) + } + + proptest! { + + #[test] + fn currency_symbol(val in arb_currency_symbol()) { + assert_eq!(val, from_to_string(&val)?); + } + + #[test] + fn token_name(val in string_regex("[a-zA-Z0-9]{0,32}").unwrap()) { + let token_name = TokenName::from_string(&val).unwrap(); + + assert_eq!(token_name.try_into_string().unwrap(), val); + } + + #[test] + fn asset_class(val in arb_asset_class()) { + assert_eq!(val, from_to_string(&val)?); + } + + #[test] + fn value(val in arb_value()) { + assert_eq!(val, from_to_string(&val)?); + } + + #[test] + fn address(val in arb_address()) { + let roundtripped = Address::from_str(&val.with_extra_info(0).to_string()).unwrap(); + + assert_eq!(val, roundtripped); + } + + #[test] + fn transaction_input(val in arb_transaction_input()) { + assert_eq!(val, from_to_string(&val)?); + } + } + } +} diff --git a/plutus-ledger-api/tests/json.rs b/plutus-ledger-api/tests/lbf.rs similarity index 57% rename from plutus-ledger-api/tests/json.rs rename to plutus-ledger-api/tests/lbf.rs index c918724..36e0410 100644 --- a/plutus-ledger-api/tests/json.rs +++ b/plutus-ledger-api/tests/lbf.rs @@ -1,6 +1,6 @@ #[cfg(test)] #[cfg(feature = "lbf")] -mod json_roundtrip_tests { +mod lb_json_roundtrip_tests { use lbr_prelude::json::{Error, Json}; fn from_to_json(val: &T) -> Result where @@ -159,4 +159,108 @@ mod json_roundtrip_tests { } } } + + mod v3 { + use super::from_to_json; + use plutus_ledger_api::generators::correct::v3::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn test_cold_committee_credential(val in arb_cold_committee_credential()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_hot_committee_credential(val in arb_hot_committee_credential()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_d_rep_credential(val in arb_d_rep_credential()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_delegatee(val in arb_delegatee()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_tx_cert(val in arb_tx_cert()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_voter(val in arb_voter()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_vote(val in arb_vote()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_governance_action_id(val in arb_governance_action_id()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_committee(val in arb_committee()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_rational(val in arb_rational()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_constitution(val in arb_constitution()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_protocol_version(val in arb_protocol_version()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_change_parameters(val in arb_change_parameters()) { + assert_eq!(val, from_to_json(&val)?) + } + + + #[test] + fn test_governance_action(val in arb_governance_action()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_protocol_procedure(val in arb_protocol_procedure()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_script_purpose(val in arb_script_purpose()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_script_info(val in arb_script_info()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_transaction_info(val in arb_transaction_info()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_script_context(val in arb_script_context()) { + assert_eq!(val, from_to_json(&val)?) + } + } + } } diff --git a/plutus-ledger-api/tests/plutus_data.rs b/plutus-ledger-api/tests/plutus_data.rs index 5d53f42..6626089 100644 --- a/plutus-ledger-api/tests/plutus_data.rs +++ b/plutus-ledger-api/tests/plutus_data.rs @@ -338,6 +338,12 @@ mod plutusdata_roundtrip_tests { fn v1_script_context(val in arb_script_context()) { assert_eq!(val, from_to_plutus_data(&val)?) } + + + #[test] + fn v1_lovelace(val in arb_lovelace()) { + assert_eq!(val, from_to_plutus_data(&val)?) + } } } mod golden_v2 { @@ -406,4 +412,122 @@ mod plutusdata_roundtrip_tests { } } } + + mod prop_v3 { + use super::from_to_plutus_data; + use plutus_ledger_api::generators::correct::v3::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn v3_cold_committee_credential(val in arb_cold_committee_credential()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + #[test] + fn v3_hot_committee_credential(val in arb_hot_committee_credential()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + #[test] + fn v3_d_rep_credential(val in arb_d_rep_credential()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_d_rep(val in arb_d_rep()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_delegatee(val in arb_delegatee()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_tx_cert(val in arb_tx_cert()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_voter(val in arb_voter()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_vote(val in arb_vote()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_governance_action_id(val in arb_governance_action_id()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + #[test] + fn v3_committee(val in arb_committee()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + #[test] + fn v3_rational(val in arb_rational()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + #[test] + fn v3_constitution(val in arb_constitution()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_protocol_version(val in arb_protocol_version()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_change_parameters(val in arb_change_parameters()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_governance_action(val in arb_governance_action()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + #[test] + fn v3_protocol_procedure(val in arb_protocol_procedure()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + #[test] + fn v3_script_purpose(val in arb_script_purpose()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + #[test] + fn v3_script_info(val in arb_script_info()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + + #[test] + fn v3_transaction_info(val in arb_transaction_info()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + + #[test] + fn v3_script_context(val in arb_script_context()) { + assert_eq!(val, from_to_plutus_data(&val)?); + } + } + } } diff --git a/plutus-ledger-api/tests/serde.rs b/plutus-ledger-api/tests/serde.rs new file mode 100644 index 0000000..cc583b9 --- /dev/null +++ b/plutus-ledger-api/tests/serde.rs @@ -0,0 +1,264 @@ +#[cfg(test)] +#[cfg(feature = "serde")] +mod serde_roundtrip_tests { + fn from_to_json(val: &T) -> Result + where + T: serde::Serialize + for<'a> serde::Deserialize<'a> + PartialEq, + { + serde_json::from_str(&serde_json::to_string(&val)?) + } + + mod v1 { + use super::from_to_json; + use plutus_ledger_api::generators::correct::{primitive::arb_integer, v1::*}; + use proptest::prelude::*; + + proptest! { + #[test] + fn test_asset_class(val in arb_asset_class()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_value(val in arb_value()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_plutus_data(val in arb_plutus_data()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_plutus_interval(val in arb_plutus_interval_posix_time()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_address(val in arb_address()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_transaction_input(val in arb_transaction_input()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_transaction_output(val in arb_transaction_output()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_tx_in_info(val in arb_tx_in_info()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_redeemeer(val in arb_redeemer()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_redeemeer_hash(val in arb_redeemer_hash()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_bigint_assoc_map(val in arb_assoc_map(arb_integer(), arb_integer())) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_d_cert(val in arb_d_cert()) { + assert_eq!(val, from_to_json(&val)?) + } + } + + proptest! { + #[test] + fn test_script_purpose(val in arb_script_purpose()) { + assert_eq!(val, from_to_json(&val)?) + } + } + + proptest! { + #[test] + fn test_transaction_info(val in arb_transaction_info()) { + assert_eq!(val, from_to_json(&val)?) + } + } + + proptest! { + #[test] + fn test_script_context(val in arb_script_context()) { + assert_eq!(val, from_to_json(&val)?) + } + } + } + mod v2 { + use super::from_to_json; + use plutus_ledger_api::generators::correct::v2::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn test_transaction_output(val in arb_transaction_output()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_tx_in_info(val in arb_tx_in_info()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_output_datum(val in arb_output_datum()) { + assert_eq!(val, from_to_json(&val)?); + } + } + + proptest! { + #[test] + fn test_transaction_info(val in arb_transaction_info()) { + assert_eq!(val, from_to_json(&val)?) + } + } + + proptest! { + #[test] + fn test_script_context(val in arb_script_context()) { + assert_eq!(val, from_to_json(&val)?) + } + } + } + mod v3 { + use super::from_to_json; + use plutus_ledger_api::generators::correct::v3::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn test_cold_committee_credential(val in arb_cold_committee_credential()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_hot_committee_credential(val in arb_hot_committee_credential()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_d_rep_credential(val in arb_d_rep_credential()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_delegatee(val in arb_delegatee()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_tx_cert(val in arb_tx_cert()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_voter(val in arb_voter()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_vote(val in arb_vote()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_governance_action_id(val in arb_governance_action_id()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_committee(val in arb_committee()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_rational(val in arb_rational()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_constitution(val in arb_constitution()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_protocol_version(val in arb_protocol_version()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_change_parameters(val in arb_change_parameters()) { + assert_eq!(val, from_to_json(&val)?) + } + + + #[test] + fn test_governance_action(val in arb_governance_action()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_protocol_procedure(val in arb_protocol_procedure()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_script_purpose(val in arb_script_purpose()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_script_info(val in arb_script_info()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_transaction_info(val in arb_transaction_info()) { + assert_eq!(val, from_to_json(&val)?) + } + + #[test] + fn test_script_context(val in arb_script_context()) { + assert_eq!(val, from_to_json(&val)?) + } + } + } +} diff --git a/plutus-ledger-api/tests/testdata/v1_address_display.golden b/plutus-ledger-api/tests/testdata/v1_address_display.golden new file mode 100644 index 0000000..f8310f0 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_address_display.golden @@ -0,0 +1 @@ +addr_test1yqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsjen49f \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_address_display_1.golden b/plutus-ledger-api/tests/testdata/v1_address_display_1.golden new file mode 100644 index 0000000..f8310f0 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_address_display_1.golden @@ -0,0 +1 @@ +addr_test1yqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsjen49f \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_address_display_2.golden b/plutus-ledger-api/tests/testdata/v1_address_display_2.golden new file mode 100644 index 0000000..fe6e8e1 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_address_display_2.golden @@ -0,0 +1 @@ +addr1yyqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs30w4fk \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_asset_class.golden b/plutus-ledger-api/tests/testdata/v1_asset_class.golden index 2fe461a..9e193eb 100644 --- a/plutus-ledger-api/tests/testdata/v1_asset_class.golden +++ b/plutus-ledger-api/tests/testdata/v1_asset_class.golden @@ -3,34 +3,34 @@ Constr( [ Bytes( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Bytes( diff --git a/plutus-ledger-api/tests/testdata/v1_asset_class_display.golden b/plutus-ledger-api/tests/testdata/v1_asset_class_display.golden new file mode 100644 index 0000000..d77aa17 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_asset_class_display.golden @@ -0,0 +1 @@ +01010101010101010101010101010101010101010101010101010101.536f6d657468696e67 \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_currency_symbol_display_1.golden b/plutus-ledger-api/tests/testdata/v1_currency_symbol_display_1.golden new file mode 100644 index 0000000..45e1797 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_currency_symbol_display_1.golden @@ -0,0 +1 @@ +01010101010101010101010101010101010101010101010101010101 \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_currency_symbol_display_2.golden b/plutus-ledger-api/tests/testdata/v1_currency_symbol_display_2.golden new file mode 100644 index 0000000..e69de29 diff --git a/plutus-ledger-api/tests/testdata/v1_currency_symbol_display_3.golden b/plutus-ledger-api/tests/testdata/v1_currency_symbol_display_3.golden new file mode 100644 index 0000000..cf33dc1 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_currency_symbol_display_3.golden @@ -0,0 +1 @@ +lovelace \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_script_context.golden b/plutus-ledger-api/tests/testdata/v1_script_context.golden index b3be0f2..8ed1257 100644 --- a/plutus-ledger-api/tests/testdata/v1_script_context.golden +++ b/plutus-ledger-api/tests/testdata/v1_script_context.golden @@ -154,35 +154,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -355,35 +372,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -460,35 +494,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -520,35 +571,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -895,34 +963,34 @@ Constr( [ Bytes( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), ], diff --git a/plutus-ledger-api/tests/testdata/v1_script_purpose.golden b/plutus-ledger-api/tests/testdata/v1_script_purpose.golden index af1c3c3..e396f0e 100644 --- a/plutus-ledger-api/tests/testdata/v1_script_purpose.golden +++ b/plutus-ledger-api/tests/testdata/v1_script_purpose.golden @@ -3,34 +3,34 @@ Constr( [ Bytes( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), ], diff --git a/plutus-ledger-api/tests/testdata/v1_token_name_display_1.golden b/plutus-ledger-api/tests/testdata/v1_token_name_display_1.golden new file mode 100644 index 0000000..488c931 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_token_name_display_1.golden @@ -0,0 +1 @@ +fff4e9de \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_token_name_display_2.golden b/plutus-ledger-api/tests/testdata/v1_token_name_display_2.golden new file mode 100644 index 0000000..820efda --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_token_name_display_2.golden @@ -0,0 +1 @@ +54657374546f6b656e \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_token_name_display_3.golden b/plutus-ledger-api/tests/testdata/v1_token_name_display_3.golden new file mode 100644 index 0000000..9c98efe --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_token_name_display_3.golden @@ -0,0 +1 @@ +0xfff4e9de \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_token_name_display_4.golden b/plutus-ledger-api/tests/testdata/v1_token_name_display_4.golden new file mode 100644 index 0000000..74dc4fb --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_token_name_display_4.golden @@ -0,0 +1 @@ +TestToken \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_transaction_info.golden b/plutus-ledger-api/tests/testdata/v1_transaction_info.golden index 4d6d21b..e696a1a 100644 --- a/plutus-ledger-api/tests/testdata/v1_transaction_info.golden +++ b/plutus-ledger-api/tests/testdata/v1_transaction_info.golden @@ -151,35 +151,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -352,35 +369,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -457,35 +491,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -517,35 +568,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( diff --git a/plutus-ledger-api/tests/testdata/v1_transaction_input_display.golden b/plutus-ledger-api/tests/testdata/v1_transaction_input_display.golden new file mode 100644 index 0000000..a7af387 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_transaction_input_display.golden @@ -0,0 +1 @@ +0000000000000000000000000000000000000000000000000000000000000000#3 \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_transaction_output.golden b/plutus-ledger-api/tests/testdata/v1_transaction_output.golden index 0a98a9d..f1a2de9 100644 --- a/plutus-ledger-api/tests/testdata/v1_transaction_output.golden +++ b/plutus-ledger-api/tests/testdata/v1_transaction_output.golden @@ -94,35 +94,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( diff --git a/plutus-ledger-api/tests/testdata/v1_tx_in_info.golden b/plutus-ledger-api/tests/testdata/v1_tx_in_info.golden index a394d41..3d01853 100644 --- a/plutus-ledger-api/tests/testdata/v1_tx_in_info.golden +++ b/plutus-ledger-api/tests/testdata/v1_tx_in_info.golden @@ -146,35 +146,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( diff --git a/plutus-ledger-api/tests/testdata/v1_value.golden b/plutus-ledger-api/tests/testdata/v1_value.golden index ac756e7..4fa6506 100644 --- a/plutus-ledger-api/tests/testdata/v1_value.golden +++ b/plutus-ledger-api/tests/testdata/v1_value.golden @@ -1,36 +1,53 @@ Map( [ + ( + Bytes( + [], + ), + Map( + [ + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), ( Bytes( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( diff --git a/plutus-ledger-api/tests/testdata/v1_value_display_1.golden b/plutus-ledger-api/tests/testdata/v1_value_display_1.golden new file mode 100644 index 0000000..c7d2241 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_value_display_1.golden @@ -0,0 +1 @@ +234+123 01010101010101010101010101010101010101010101010101010101.536f6d657468696e67 \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v1_value_display_2.golden b/plutus-ledger-api/tests/testdata/v1_value_display_2.golden new file mode 100644 index 0000000..c7d2241 --- /dev/null +++ b/plutus-ledger-api/tests/testdata/v1_value_display_2.golden @@ -0,0 +1 @@ +234+123 01010101010101010101010101010101010101010101010101010101.536f6d657468696e67 \ No newline at end of file diff --git a/plutus-ledger-api/tests/testdata/v2_script_context.golden b/plutus-ledger-api/tests/testdata/v2_script_context.golden index f44208a..2febd78 100644 --- a/plutus-ledger-api/tests/testdata/v2_script_context.golden +++ b/plutus-ledger-api/tests/testdata/v2_script_context.golden @@ -154,35 +154,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -426,35 +443,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -646,35 +680,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -770,35 +821,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -830,35 +898,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -1099,34 +1184,34 @@ Constr( [ Bytes( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), ], @@ -1245,34 +1330,34 @@ Constr( [ Bytes( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), ], diff --git a/plutus-ledger-api/tests/testdata/v2_transaction_info.golden b/plutus-ledger-api/tests/testdata/v2_transaction_info.golden index 9ac751d..dce881b 100644 --- a/plutus-ledger-api/tests/testdata/v2_transaction_info.golden +++ b/plutus-ledger-api/tests/testdata/v2_transaction_info.golden @@ -151,35 +151,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -423,35 +440,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -643,35 +677,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -767,35 +818,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -827,35 +895,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( @@ -1096,34 +1181,34 @@ Constr( [ Bytes( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), ], diff --git a/plutus-ledger-api/tests/testdata/v2_transaction_output.golden b/plutus-ledger-api/tests/testdata/v2_transaction_output.golden index e7110a7..a0461a7 100644 --- a/plutus-ledger-api/tests/testdata/v2_transaction_output.golden +++ b/plutus-ledger-api/tests/testdata/v2_transaction_output.golden @@ -94,35 +94,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map( diff --git a/plutus-ledger-api/tests/testdata/v2_tx_in_info.golden b/plutus-ledger-api/tests/testdata/v2_tx_in_info.golden index d07191e..149bf1d 100644 --- a/plutus-ledger-api/tests/testdata/v2_tx_in_info.golden +++ b/plutus-ledger-api/tests/testdata/v2_tx_in_info.golden @@ -146,35 +146,52 @@ Constr( [ ( Bytes( + [], + ), + Map( [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + ( + Bytes( + [], + ), + Integer( + 234, + ), + ), + ], + ), + ), + ( + Bytes( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, ], ), Map(