From 87e07ef86e372e147de990e2cc014b3ac1c90453 Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Mon, 14 Oct 2024 20:28:44 +0800 Subject: [PATCH 01/12] implement procedural derive macro for `IsPlutusData` --- .gitignore | 1 + flake.nix | 1 + is-plutus-data-derive/Cargo.lock | 68 +++ is-plutus-data-derive/Cargo.toml | 13 + is-plutus-data-derive/build.nix | 18 + is-plutus-data-derive/src/derive_impl.rs | 706 +++++++++++++++++++++++ is-plutus-data-derive/src/lib.rs | 13 + 7 files changed, 820 insertions(+) create mode 100644 is-plutus-data-derive/Cargo.lock create mode 100644 is-plutus-data-derive/Cargo.toml create mode 100644 is-plutus-data-derive/build.nix create mode 100644 is-plutus-data-derive/src/derive_impl.rs create mode 100644 is-plutus-data-derive/src/lib.rs diff --git a/.gitignore b/.gitignore index 1a1f6a6..0871c3a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ result .direnv .pre-commit-config.yaml .DS_Store +target 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/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..acaf07f --- /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.87" +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..d287406 --- /dev/null +++ b/is-plutus-data-derive/src/derive_impl.rs @@ -0,0 +1,706 @@ +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, Fields, + FieldsNamed, FieldsUnnamed, Ident, Index, ItemImpl, 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? + generics.type_params_mut().for_each(|param| { + param + .bounds + .push(parse_quote!(plutus_ledger_api::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 an Ident.")] + 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("plutus_data_derive_strategy") + .then_some(&name_value.value), + _ => None, + }?; + + Some(match &value { + Expr::Path(path) => (|| -> Result { + let ident = path.path.require_ident()?; + DeriveStrategy::from_str(&ident.to_string()) + .map_err(|err| Error::new(ident.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 plutus_data_list_var: Ident = parse_quote!(plutus_data_list); + + let from_list_of_plutus_data = + struct_with_unnamed_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_list_of_plutus_data + }) +} + +fn struct_with_no_field_to_plutus_data_list() -> Block { + parse_quote!(plutus_ledger_api::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_ledger_api::plutus_data::PlutusData::Constr( + 0.into(), + vec![] + )) +} + +fn struct_with_no_field_from_plutus_data_constr(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_constr_with_tag(#plutus_data_input_var, 0)?; + + #from_list_of_plutus_data + }) +} + +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),* ] = parse_fixed_len_plutus_data_list::<#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),* ] = parse_fixed_len_plutus_data_list::<#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 [ ] = parse_fixed_len_plutus_data_list::<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..4e54429 --- /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(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() +} From 21efc52e04be49b59884e61774482d45f8d6d86b Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Thu, 17 Oct 2024 23:46:35 +0800 Subject: [PATCH 02/12] separate `PlutusData` and `IsPlutusData` from plutus-ledger-api --- .gitignore | 1 + flake.nix | 1 + is-plutus-data-derive/src/derive_impl.rs | 80 +-- plutus-data/.envrc | 1 + plutus-data/Cargo.lock | 657 ++++++++++++++++++ plutus-data/Cargo.toml | 19 + plutus-data/build.nix | 19 + plutus-data/src/is_plutus_data/aux.rs | 66 ++ plutus-data/src/is_plutus_data/instances.rs | 292 ++++++++ .../src/is_plutus_data/is_plutus_data.rs | 22 + plutus-data/src/is_plutus_data/mod.rs | 3 + plutus-data/src/lib.rs | 8 + plutus-data/src/plutus_data.rs | 183 +++++ 13 files changed, 1306 insertions(+), 46 deletions(-) create mode 100644 plutus-data/.envrc create mode 100644 plutus-data/Cargo.lock create mode 100644 plutus-data/Cargo.toml create mode 100644 plutus-data/build.nix create mode 100644 plutus-data/src/is_plutus_data/aux.rs create mode 100644 plutus-data/src/is_plutus_data/instances.rs create mode 100644 plutus-data/src/is_plutus_data/is_plutus_data.rs create mode 100644 plutus-data/src/is_plutus_data/mod.rs create mode 100644 plutus-data/src/lib.rs create mode 100644 plutus-data/src/plutus_data.rs diff --git a/.gitignore b/.gitignore index 0871c3a..1f902d2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ result .pre-commit-config.yaml .DS_Store target +.extras diff --git a/flake.nix b/flake.nix index 8d68e4d..29f46ea 100644 --- a/flake.nix +++ b/flake.nix @@ -24,6 +24,7 @@ ./plutus-ledger-api/build.nix ./is-plutus-data-derive/build.nix + ./plutus-data/build.nix ]; debug = true; systems = [ "x86_64-linux" "x86_64-darwin" ]; diff --git a/is-plutus-data-derive/src/derive_impl.rs b/is-plutus-data-derive/src/derive_impl.rs index d287406..35fc5e5 100644 --- a/is-plutus-data-derive/src/derive_impl.rs +++ b/is-plutus-data-derive/src/derive_impl.rs @@ -24,22 +24,20 @@ pub(crate) fn get_is_plutus_data_instance(input: DeriveInput) -> Result plutus_ledger_api::plutus_data::PlutusData { + impl #impl_generics plutus_data::IsPlutusData for #type_name #type_generics #where_clause { + fn to_plutus_data(&self) -> plutus_data::PlutusData { #encoder } - fn from_plutus_data(plutus_data: &plutus_ledger_api::plutus_data::PlutusData) -> Result + fn from_plutus_data(plutus_data: &plutus_data::PlutusData) -> Result where Self: Sized { #decoder } @@ -171,15 +169,15 @@ fn get_newtype_encoder_decoder(input: &DeriveInput) -> Result<(Block, Block)> { Some(field_name) => { parse_quote!({ Ok(Self { - #field_name: plutus_ledger_api::plutus_data::IsPlutusData::from_plutus_data(plutus_data)? + #field_name: 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(Self(plutus_data::IsPlutusData::from_plutus_data( + plutus_data, + )?)) }) } }; @@ -311,11 +309,11 @@ fn enum_from_plutus_data_constr(e: &DataEnum, plutus_data_input_var: &Ident) -> parse_quote!( { - let (tag, #plutus_data_list_var) = plutus_ledger_api::plutus_data::parse_constr(#plutus_data_input_var)?; + let (tag, #plutus_data_list_var) = plutus_data::is_plutus_data::aux::parse_constr(#plutus_data_input_var)?; match tag { #(#arms),* - tag => Err(plutus_ledger_api::plutus_data::PlutusDataError::UnexpectedPlutusInvariant { + tag => Err(plutus_data::PlutusDataError::UnexpectedPlutusInvariant { wanted: format!(#expected_tags_str), got: tag.to_string(), }), @@ -381,7 +379,7 @@ fn variant_with_named_fields_to_plutus_data( 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) + #constructor{ #(#field_names),* } => plutus_data::PlutusData::Constr(#tag.into(), #plutus_data_list) ) } @@ -408,7 +406,7 @@ fn variant_with_unnamed_field_to_plutus_data( 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) + #constructor(#(#field_names),*) => plutus_data::PlutusData::Constr(#tag.into(), #plutus_data_list) ) } @@ -426,7 +424,7 @@ fn variant_with_unnamed_fields_from_plutus_data_list( 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![]) + #constructor => plutus_data::PlutusData::Constr(#tag.into(), vec![]) ) } @@ -464,7 +462,7 @@ 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) + plutus_data::PlutusData::List(#to_list_of_plutus_data) }) } @@ -478,7 +476,7 @@ fn struct_with_named_fields_from_plutus_data_list( 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)?; + let #list_of_plutus_data_var = plutus_data::is_plutus_data::aux::parse_list(#plutus_data_input_var)?; #from_list_of_plutus_data }) @@ -488,7 +486,7 @@ 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) + plutus_data::PlutusData::Constr(0.into(), #to_list_of_plutus_data) }) } @@ -502,7 +500,7 @@ fn struct_with_named_fields_from_plutus_data_constr( 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)?; + let #plutus_data_list_var = plutus_data::is_plutus_data::aux::parse_constr_with_tag(#plutus_data_input_var, 0)?; #from_plutus_data_list }) @@ -538,7 +536,7 @@ fn struct_with_unnamed_fields_to_plutus_data_list(fields: &FieldsUnnamed) -> Blo 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) + plutus_data::PlutusData::List(#to_list_of_plutus_data) }) } @@ -552,7 +550,7 @@ fn struct_with_unnamed_fields_from_plutus_data_list( 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)?; + let #list_of_plutus_data_var = plutus_data::is_plutus_data::aux::parse_list(#plutus_data_input_var)?; #from_list_of_plutus_data }) @@ -562,7 +560,7 @@ fn struct_with_unnamed_fields_to_plutus_data_constr(fields: &FieldsUnnamed) -> B 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) + plutus_data::PlutusData::Constr(0.into(), #to_list_of_plutus_data) }) } @@ -570,23 +568,19 @@ fn struct_with_unnamed_fields_from_plutus_data_constr( fields: &FieldsUnnamed, plutus_data_input_var: &Ident, ) -> Block { - let plutus_data_list_var: Ident = parse_quote!(plutus_data_list); + let fields_var: Ident = parse_quote!(fields); - let from_list_of_plutus_data = - struct_with_unnamed_fields_from_list_of_plutus_data(fields, &plutus_data_list_var); + let from_fields = struct_with_unnamed_fields_from_list_of_plutus_data(fields, &fields_var); parse_quote!({ - let #plutus_data_list_var = plutus_ledger_api::plutus_data::parse_constr_with_tag(#plutus_data_input_var, 0)?; + let #fields_var = plutus_data::is_plutus_data::aux::parse_constr_with_tag(#plutus_data_input_var, 0)?; - #from_list_of_plutus_data + #from_fields }) } fn struct_with_no_field_to_plutus_data_list() -> Block { - parse_quote!(plutus_ledger_api::plutus_data::PlutusData::Constr( - 0.into(), - vec![] - )) + parse_quote!(plutus_data::PlutusData::Constr(0.into(), vec![])) } fn struct_with_no_field_from_plutus_data_list(plutus_data_input_var: &Ident) -> Block { @@ -596,32 +590,26 @@ fn struct_with_no_field_from_plutus_data_list(plutus_data_input_var: &Ident) -> 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)?; - - - + let #list_of_plutus_data_var = plutus_data::is_plutus_data::aux::parse_list(#plutus_data_input_var)?; #from_list_of_plutus_data }) } fn struct_with_no_field_to_plutus_data_constr() -> Block { - parse_quote!(plutus_ledger_api::plutus_data::PlutusData::Constr( - 0.into(), - vec![] - )) + 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 list_of_plutus_data_var: Ident = parse_quote!(list_of_plutus_data); + let fields_var: Ident = parse_quote!(fields); - let from_list_of_plutus_data = - data_with_no_fields_from_list_of_plutus_data(&parse_quote!(Self), &list_of_plutus_data_var); + let from_fields = + data_with_no_fields_from_list_of_plutus_data(&parse_quote!(Self), &fields_var); parse_quote!({ - let #list_of_plutus_data_var = plutus_ledger_api::plutus_data::parse_constr_with_tag(#plutus_data_input_var, 0)?; + let #fields_var = plutus_data::is_plutus_data::aux::parse_constr_with_tag(#plutus_data_input_var, 0)?; - #from_list_of_plutus_data + #from_fields }) } @@ -652,7 +640,7 @@ fn data_with_named_fields_from_list_of_plutus_data( 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)?; + let #field_ident = plutus_data::IsPlutusData::from_plutus_data(#unparsed_field_ident)?; ) }, ); diff --git a/plutus-data/.envrc b/plutus-data/.envrc new file mode 100644 index 0000000..6841e87 --- /dev/null +++ b/plutus-data/.envrc @@ -0,0 +1 @@ +use flake .#dev-plutus-data-rust diff --git a/plutus-data/Cargo.lock b/plutus-data/Cargo.lock new file mode 100644 index 0000000..690b993 --- /dev/null +++ b/plutus-data/Cargo.lock @@ -0,0 +1,657 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "dissimilar" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-plutus-data-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "thiserror", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lbr-prelude" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21471892874c4667636a067ed7a158b9691178988a4c5988585e1010e9b5817d" +dependencies = [ + "data-encoding", + "lbr-prelude-derive", + "num-bigint", + "proptest", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "lbr-prelude-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "370d0aa0809ee84ccf7c6d84ae2d6ac97b84cb1337b3d145101bcf6a7319deac" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "trybuild", +] + +[[package]] +name = "libc" +version = "0.2.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "plutus-data" +version = "0.1.0" +dependencies = [ + "data-encoding", + "is-plutus-data-derive", + "lbr-prelude", + "num-bigint", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[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 = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[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 = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "trybuild" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8923cde76a6329058a86f04d033f0945a2c6df8b94093512e4ab188b3e3a8950" +dependencies = [ + "dissimilar", + "glob", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +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-data/Cargo.toml b/plutus-data/Cargo.toml new file mode 100644 index 0000000..ade6890 --- /dev/null +++ b/plutus-data/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "plutus-data" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "^1.0.189", features = ["derive"], optional = true } +serde_json = { version = "1.0.128", optional = true } +num-bigint = "~0.4" +is-plutus-data-derive = { path = ".extras/is-plutus-data-derive-0", optional = true } +lbr-prelude = { version = "0.1.1", optional = true } +data-encoding = { version = "2.6.0", optional = true } +thiserror = "1.0.64" + +[features] +default = [] +serde = ["dep:serde", "num-bigint/serde", "dep:serde_json"] +lbf = ["dep:lbr-prelude", "dep:serde_json", "dep:data-encoding"] +derive = ["dep:is-plutus-data-derive"] diff --git a/plutus-data/build.nix b/plutus-data/build.nix new file mode 100644 index 0000000..33b0093 --- /dev/null +++ b/plutus-data/build.nix @@ -0,0 +1,19 @@ +{ inputs, ... }: { + perSystem = { config, system, ... }: + let + rustFlake = + inputs.flake-lang.lib.${system}.rustFlake { + src = ./.; + version = "0"; + crateName = "plutus-data"; + devShellHook = config.settings.shell.hook; + cargoNextestExtraArgs = "--all-features"; + extraSources = [ + config.packages.is-plutus-data-derive-rust-src + ]; + }; + in + { + inherit (rustFlake) packages checks devShells; + }; +} diff --git a/plutus-data/src/is_plutus_data/aux.rs b/plutus-data/src/is_plutus_data/aux.rs new file mode 100644 index 0000000..b793f7f --- /dev/null +++ b/plutus-data/src/is_plutus_data/aux.rs @@ -0,0 +1,66 @@ +use crate::{PlutusData, PlutusDataError, PlutusType}; + +/// Given a vector of PlutusData, parse it as an array whose length is known at +/// compile time. +/// +/// 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(), + wanted: LEN, + }) +} + +/// Given a PlutusData, parse it as PlutusData::Constr and its tag as u32. Return +/// the u32 tag and fields. +/// +/// 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 { + got: err.to_string(), + wanted: "Constr bigint tag within u32 range".into(), + }) + .map(|tag| (tag, fields)), + _ => Err(PlutusDataError::UnexpectedPlutusType { + wanted: PlutusType::Constr, + got: PlutusType::from(data), + }), + } +} + +/// Given a PlutusData, parse it as PlutusData::Constr and verify its tag. +/// +/// This function is used by the derive macro. +pub fn parse_constr_with_tag( + data: &PlutusData, + expected_tag: u32, +) -> Result<&Vec, PlutusDataError> { + let (tag, fields) = parse_constr(data)?; + + if tag != expected_tag { + Err(PlutusDataError::UnexpectedPlutusInvariant { + got: tag.to_string(), + 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-data/src/is_plutus_data/instances.rs b/plutus-data/src/is_plutus_data/instances.rs new file mode 100644 index 0000000..5c7838a --- /dev/null +++ b/plutus-data/src/is_plutus_data/instances.rs @@ -0,0 +1,292 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use num_bigint::BigInt; + +use crate::{IsPlutusData, PlutusData, PlutusDataError, PlutusType}; + +use super::aux::{parse_constr, parse_constr_with_tag, parse_fixed_len_constr_fields, parse_list}; + +impl IsPlutusData for PlutusData { + fn to_plutus_data(&self) -> PlutusData { + self.clone() + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + Ok(plutus_data.clone()) + } +} + +// MARK: Orphan Instances + +impl IsPlutusData for BigInt { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Integer(self.clone()) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + match plutus_data { + PlutusData::Integer(int) => Ok(int.clone()), + _ => Err(PlutusDataError::UnexpectedPlutusType { + wanted: PlutusType::Integer, + got: PlutusType::from(plutus_data), + }), + } + } +} + +impl IsPlutusData for Vec { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Bytes(self.clone()) + } + + 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), + }), + } + } +} + +const BOOL_FALSE_TAG: u32 = 0; +const BOOL_TRUE_TAG: u32 = 1; + +impl IsPlutusData for bool { + fn to_plutus_data(&self) -> PlutusData { + 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 { + 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(), + }), + } + } +} + +impl IsPlutusData for String { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Bytes(self.as_bytes().into()) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + match plutus_data { + PlutusData::Bytes(bytes) => String::from_utf8(bytes.clone()).map_err(|err| { + PlutusDataError::InternalError(format!( + "Couldn't convert Plutus bytes to String: {:?}", + err + )) + }), + _ => Err(PlutusDataError::UnexpectedPlutusType { + 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(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 { + let (tag, fields) = parse_constr(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, + E: IsPlutusData, +{ + fn to_plutus_data(&self) -> PlutusData { + match self { + 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 { + 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(), + }), + } + } +} + +impl IsPlutusData for Vec +where + T: IsPlutusData, +{ + fn to_plutus_data(&self) -> PlutusData { + let values = self + .iter() + .map(|val| val.to_plutus_data()) + .collect::>(); + + PlutusData::List(values) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + let list = parse_list(plutus_data)?; + list.iter().map(T::from_plutus_data).collect() + } +} + +impl IsPlutusData for BTreeSet +where + T: IsPlutusData + Eq + Ord, +{ + fn to_plutus_data(&self) -> PlutusData { + let set = self + .iter() + .map(|val| val.to_plutus_data()) + .collect::>(); + + PlutusData::List(set) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + match plutus_data { + PlutusData::List(vec) => vec + .iter() + .map(|val| T::from_plutus_data(val)) + .collect::>(), + _ => Err(PlutusDataError::UnexpectedPlutusType { + wanted: PlutusType::List, + got: PlutusType::from(plutus_data), + }), + } + } +} + +impl IsPlutusData for BTreeMap +where + K: IsPlutusData + Eq + Ord, + V: IsPlutusData, +{ + fn to_plutus_data(&self) -> PlutusData { + let assoc_map = self + .iter() + .map(|(key, val)| (key.to_plutus_data(), val.to_plutus_data())) + .collect::>(); + + PlutusData::Map(assoc_map) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + match plutus_data { + PlutusData::Map(dict) => dict + .iter() + .map(|(key, val)| Ok((K::from_plutus_data(key)?, V::from_plutus_data(val)?))) + .collect::>(), + _ => Err(PlutusDataError::UnexpectedPlutusType { + wanted: PlutusType::Map, + got: PlutusType::from(plutus_data), + }), + } + } +} + +const UNIT_TAG: u32 = 0; + +impl IsPlutusData for () { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Constr(UNIT_TAG.into(), vec![]) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + let fields = parse_constr_with_tag(plutus_data, UNIT_TAG)?; + let [] = parse_fixed_len_constr_fields::<0>(fields)?; + Ok(()) + } +} + +const PAIR_TAG: u32 = 0; + +impl IsPlutusData for (A, B) +where + A: IsPlutusData, + B: IsPlutusData, +{ + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Constr( + BigInt::from(PAIR_TAG), + vec![self.0.to_plutus_data(), self.1.to_plutus_data()], + ) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + 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)?)) + } +} diff --git a/plutus-data/src/is_plutus_data/is_plutus_data.rs b/plutus-data/src/is_plutus_data/is_plutus_data.rs new file mode 100644 index 0000000..4f73c15 --- /dev/null +++ b/plutus-data/src/is_plutus_data/is_plutus_data.rs @@ -0,0 +1,22 @@ +use crate::{PlutusData, PlutusType}; + +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), +} diff --git a/plutus-data/src/is_plutus_data/mod.rs b/plutus-data/src/is_plutus_data/mod.rs new file mode 100644 index 0000000..526bebf --- /dev/null +++ b/plutus-data/src/is_plutus_data/mod.rs @@ -0,0 +1,3 @@ +pub mod aux; +mod instances; +pub mod is_plutus_data; diff --git a/plutus-data/src/lib.rs b/plutus-data/src/lib.rs new file mode 100644 index 0000000..c2c607a --- /dev/null +++ b/plutus-data/src/lib.rs @@ -0,0 +1,8 @@ +pub mod is_plutus_data; +pub mod plutus_data; + +#[cfg(feature = "derive")] +pub use is_plutus_data_derive::IsPlutusData; + +pub use is_plutus_data::is_plutus_data::{IsPlutusData, PlutusDataError}; +pub use plutus_data::{PlutusData, PlutusType}; diff --git a/plutus-data/src/plutus_data.rs b/plutus-data/src/plutus_data.rs new file mode 100644 index 0000000..9c3cbfc --- /dev/null +++ b/plutus-data/src/plutus_data.rs @@ -0,0 +1,183 @@ +use num_bigint::BigInt; + +#[cfg(feature = "lbf")] +use data_encoding::HEXLOWER; +#[cfg(feature = "lbf")] +use lbr_prelude::error::Error; +#[cfg(feature = "lbf")] +use lbr_prelude::json::{ + case_json_constructor, case_json_object, json_constructor, json_object, Json, +}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Data representation of on-chain data such as Datums and Redeemers +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum PlutusData { + Constr(BigInt, Vec), + Map(Vec<(PlutusData, PlutusData)>), + List(Vec), + Integer(BigInt), + Bytes(Vec), +} + +#[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, + } + } +} + +impl PlutusData { + pub fn constr(tag: u32, fields: Vec) -> Self { + PlutusData::Constr(BigInt::from(tag), fields) + } + + pub fn map(fields: Vec<(PlutusData, PlutusData)>) -> Self { + PlutusData::Map(fields) + } + + pub fn list(fields: Vec) -> Self { + PlutusData::List(fields) + } + + pub fn integer(value: u32) -> Self { + PlutusData::Integer(BigInt::from(value)) + } + + pub fn bytes(value: Vec) -> Self { + PlutusData::Bytes(value) + } +} + +#[cfg(feature = "lbf")] +impl Json for PlutusData { + fn to_json(&self) -> serde_json::Value { + match self { + PlutusData::Constr(index, fields) => json_constructor( + "Constr", + vec![json_object(vec![ + ("index".to_string(), index.to_json()), + ("fields".to_string(), fields.to_json()), + ])], + ), + PlutusData::Map(map) => json_constructor("Map", vec![map.to_json()]), + PlutusData::List(list) => json_constructor("List", vec![list.to_json()]), + PlutusData::Integer(int) => json_constructor("Integer", vec![int.to_json()]), + PlutusData::Bytes(bytes) => { + json_constructor("Bytes", vec![String::to_json(&HEXLOWER.encode(bytes))]) + } + } + } + + fn from_json(value: &serde_json::Value) -> Result { + case_json_constructor( + "PlutusV1.PlutusData", + vec![ + ( + "Constr", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => case_json_object( + |obj| { + let index = obj.get("index").ok_or(Error::UnexpectedFieldName { + wanted: "index".to_owned(), + got: obj.keys().cloned().collect(), + parser: "PlutusV1.PlutusData".to_owned(), + })?; + + let fields = + obj.get("fields").ok_or(Error::UnexpectedFieldName { + wanted: "fields".to_owned(), + got: obj.keys().cloned().collect(), + parser: "PlutusV1.PlutusData".to_owned(), + })?; + Ok(PlutusData::Constr( + BigInt::from_json(index)?, + >::from_json(fields)?, + )) + }, + val, + ), + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ( + "Map", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => Ok(PlutusData::Map(Json::from_json(val)?)), + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ( + "List", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => Ok(PlutusData::List(Json::from_json(val)?)), + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ( + "Integer", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => Ok(PlutusData::Integer(Json::from_json(val)?)), + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ( + "Bytes", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => { + let bytes = String::from_json(val).and_then(|str| { + HEXLOWER.decode(&str.into_bytes()).map_err(|_| { + Error::UnexpectedJsonInvariant { + wanted: "base16 string".to_owned(), + got: "unexpected string".to_owned(), + parser: "Plutus.V1.Bytes".to_owned(), + } + }) + })?; + Ok(PlutusData::Bytes(bytes)) + } + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ], + value, + ) + } +} From 1a7c261b20bc83595921b0a564e27cdbf96ce7c2 Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Thu, 17 Oct 2024 23:58:01 +0800 Subject: [PATCH 03/12] lower version requirement of proc-macro2 --- is-plutus-data-derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/is-plutus-data-derive/Cargo.toml b/is-plutus-data-derive/Cargo.toml index acaf07f..770f519 100644 --- a/is-plutus-data-derive/Cargo.toml +++ b/is-plutus-data-derive/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -proc-macro2 = "1.0.87" +proc-macro2 = "^1.0.66" quote = "1.0.37" syn = { version = "2.0.79", features = ["full", "extra-traits"]} thiserror = "1.0.64" From 167395f67add825645763208fece00bfb545e4d4 Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Fri, 18 Oct 2024 00:02:29 +0800 Subject: [PATCH 04/12] `plutus_data_derive_strategy` -> `is_plutus_data_derive_strategy` --- is-plutus-data-derive/src/derive_impl.rs | 2 +- is-plutus-data-derive/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/is-plutus-data-derive/src/derive_impl.rs b/is-plutus-data-derive/src/derive_impl.rs index 35fc5e5..b40bc6f 100644 --- a/is-plutus-data-derive/src/derive_impl.rs +++ b/is-plutus-data-derive/src/derive_impl.rs @@ -96,7 +96,7 @@ fn try_parse_derive_strategy(attr: &Attribute) -> Option> let value = match &attr.meta { Meta::NameValue(name_value) => name_value .path - .is_ident("plutus_data_derive_strategy") + .is_ident("is_plutus_data_derive_strategy") .then_some(&name_value.value), _ => None, }?; diff --git a/is-plutus-data-derive/src/lib.rs b/is-plutus-data-derive/src/lib.rs index 4e54429..2ba5263 100644 --- a/is-plutus-data-derive/src/lib.rs +++ b/is-plutus-data-derive/src/lib.rs @@ -3,7 +3,7 @@ use syn::{parse_macro_input, DeriveInput}; pub(crate) mod derive_impl; -#[proc_macro_derive(IsPlutusData, attributes(plutus_data_derive_strategy))] +#[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) From 4d5a08b2b41e7cc96d863b7368aa3fd9e1bde053 Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Fri, 18 Oct 2024 00:03:58 +0800 Subject: [PATCH 05/12] fix `parse_fixed_len_plutus_data_list` not in scope --- is-plutus-data-derive/src/derive_impl.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/is-plutus-data-derive/src/derive_impl.rs b/is-plutus-data-derive/src/derive_impl.rs index b40bc6f..51f483f 100644 --- a/is-plutus-data-derive/src/derive_impl.rs +++ b/is-plutus-data-derive/src/derive_impl.rs @@ -647,7 +647,7 @@ fn data_with_named_fields_from_list_of_plutus_data( parse_quote!( { - let [ #(#unparsed_field_idents),* ] = parse_fixed_len_plutus_data_list::<#field_count>(#plutus_data_list_var)?; + let [ #(#unparsed_field_idents),* ] = plutus_data::is_plutus_data::aux::parse_fixed_len_constr_fields::<#field_count>(#plutus_data_list_var)?; #(#field_decoded_stmts)* Ok(#constructor{ #(#field_idents),* }) } @@ -677,7 +677,7 @@ fn data_with_unnamed_fields_from_list_of_plutus_data( }); parse_quote!({ - let [ #(#unparsed_field_idents),* ] = parse_fixed_len_plutus_data_list::<#field_count>(#plutus_data_list_var)?; + let [ #(#unparsed_field_idents),* ] = plutus_data::is_plutus_data::aux::parse_fixed_len_constr_fields::<#field_count>(#plutus_data_list_var)?; #(#field_decoded_stmts)* Ok(#constructor(#(#parsed_field_idents),*)) }) @@ -688,7 +688,7 @@ fn data_with_no_fields_from_list_of_plutus_data( list_of_plutus_data_var: &Ident, ) -> Block { parse_quote!({ - let [ ] = parse_fixed_len_plutus_data_list::<0>(#list_of_plutus_data_var)?; + let [ ] = plutus_data::is_plutus_data::aux::parse_fixed_len_constr_fields::<0>(#list_of_plutus_data_var)?; Ok(#constructor) }) } From 001c5588fe19d36e3bc89d4e52b388cf25626973 Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Fri, 18 Oct 2024 00:35:51 +0800 Subject: [PATCH 06/12] attribute value can only be literal --- is-plutus-data-derive/src/derive_impl.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/is-plutus-data-derive/src/derive_impl.rs b/is-plutus-data-derive/src/derive_impl.rs index 51f483f..0b0886c 100644 --- a/is-plutus-data-derive/src/derive_impl.rs +++ b/is-plutus-data-derive/src/derive_impl.rs @@ -5,8 +5,8 @@ use syn::{ parse::{Parse, ParseStream}, parse_quote, spanned::Spanned, - Arm, Attribute, Block, Data, DataEnum, DataStruct, DeriveInput, Error, Expr, Fields, - FieldsNamed, FieldsUnnamed, Ident, Index, ItemImpl, Meta, Path, Result, Stmt, + 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 { @@ -56,7 +56,7 @@ enum DeriveStrategy { enum DeriveStrategyError { #[error("Unknown strategy {0}. Should be one of Newtype, List and Constr.")] UnknownStrategy(String), - #[error("Unable to parse strategy. Should be an Ident.")] + #[error("Unable to parse strategy. Should be a string literal Newtype, Constr or List.")] UnexpectedToken, #[error("More than one strategies specified.")] MoreThanOneSpecified, @@ -102,10 +102,11 @@ fn try_parse_derive_strategy(attr: &Attribute) -> Option> }?; Some(match &value { - Expr::Path(path) => (|| -> Result { - let ident = path.path.require_ident()?; - DeriveStrategy::from_str(&ident.to_string()) - .map_err(|err| Error::new(ident.span(), err)) + 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(), From c7812dd13805183e5b9b733712514a175add7d26 Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Fri, 18 Oct 2024 01:03:38 +0800 Subject: [PATCH 07/12] derive the shit out of it --- plutus-ledger-api/Cargo.lock | 24 + plutus-ledger-api/Cargo.toml | 5 +- plutus-ledger-api/build.nix | 4 + plutus-ledger-api/src/plutus_data.rs | 608 +----------------------- plutus-ledger-api/src/v1/address.rs | 169 ++----- plutus-ledger-api/src/v1/crypto.rs | 89 +--- plutus-ledger-api/src/v1/datum.rs | 28 +- plutus-ledger-api/src/v1/interval.rs | 127 ++--- plutus-ledger-api/src/v1/redeemer.rs | 28 +- plutus-ledger-api/src/v1/script.rs | 41 +- plutus-ledger-api/src/v1/transaction.rs | 325 +------------ plutus-ledger-api/src/v1/value.rs | 41 +- plutus-ledger-api/src/v2/datum.rs | 51 +- plutus-ledger-api/src/v2/transaction.rs | 150 +----- 14 files changed, 155 insertions(+), 1535 deletions(-) diff --git a/plutus-ledger-api/Cargo.lock b/plutus-ledger-api/Cargo.lock index 17e1f90..e2f7fa2 100644 --- a/plutus-ledger-api/Cargo.lock +++ b/plutus-ledger-api/Cargo.lock @@ -398,6 +398,16 @@ dependencies = [ "hashbrown 0.15.0", ] +[[package]] +name = "is-plutus-data-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "thiserror", +] + [[package]] name = "itertools" version = "0.10.5" @@ -598,6 +608,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "plutus-data" +version = "0.1.0" +dependencies = [ + "data-encoding", + "is-plutus-data-derive", + "lbr-prelude", + "num-bigint", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "plutus-ledger-api" version = "2.0.0-beta.1" @@ -612,6 +635,7 @@ dependencies = [ "linked-hash-map", "num-bigint", "num-traits", + "plutus-data", "proptest", "serde", "serde_json", diff --git a/plutus-ledger-api/Cargo.toml b/plutus-ledger-api/Cargo.toml index 66411b0..d09b07f 100644 --- a/plutus-ledger-api/Cargo.toml +++ b/plutus-ledger-api/Cargo.toml @@ -23,11 +23,12 @@ impl_ops = "0.1.1" chrono = { version = "0.4.34", optional = true } cardano-serialization-lib = "12.1.0" anyhow = "1.0.86" +plutus-data = { path = ".extras/plutus-data-0", features = [ "derive" ]} [features] default = [] -serde = ["dep:serde", "num-bigint/serde", "dep:serde_json"] -lbf = ["dep:lbr-prelude", "dep:serde_json"] +serde = ["dep:serde", "num-bigint/serde", "dep:serde_json", "plutus-data/serde"] +lbf = ["dep:lbr-prelude", "dep:serde_json", "plutus-data/lbf"] chrono = ["dep:chrono"] [dev-dependencies] diff --git a/plutus-ledger-api/build.nix b/plutus-ledger-api/build.nix index c4ad5db..4fa2517 100644 --- a/plutus-ledger-api/build.nix +++ b/plutus-ledger-api/build.nix @@ -11,6 +11,10 @@ extraSourceFilters = [ (path: _type: builtins.match ".*golden$" path != null) ]; + extraSources = [ + config.packages.is-plutus-data-derive-rust-src + config.packages.plutus-data-rust-src + ]; }; plutus-ledger-api-rust-github-pages = pkgs.stdenv.mkDerivation { diff --git a/plutus-ledger-api/src/plutus_data.rs b/plutus-ledger-api/src/plutus_data.rs index 8329704..e446ed7 100644 --- a/plutus-ledger-api/src/plutus_data.rs +++ b/plutus-ledger-api/src/plutus_data.rs @@ -1,170 +1,13 @@ //! Plutus Data related types and traits use cardano_serialization_lib as csl; -#[cfg(feature = "lbf")] -use data_encoding::HEXLOWER; -#[cfg(feature = "lbf")] -use lbr_prelude::error::Error; -#[cfg(feature = "lbf")] -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}; use crate::csl::csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA}; use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; -/// Data representation of on-chain data such as Datums and Redeemers -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum PlutusData { - Constr(BigInt, Vec), - Map(Vec<(PlutusData, PlutusData)>), - List(Vec), - Integer(BigInt), - Bytes(Vec), -} - -impl PlutusData { - pub fn constr(tag: u32, fields: Vec) -> Self { - PlutusData::Constr(BigInt::from(tag), fields) - } - - pub fn map(fields: Vec<(PlutusData, PlutusData)>) -> Self { - PlutusData::Map(fields) - } - - pub fn list(fields: Vec) -> Self { - PlutusData::List(fields) - } - - pub fn integer(value: u32) -> Self { - PlutusData::Integer(BigInt::from(value)) - } - - pub fn bytes(value: Vec) -> Self { - PlutusData::Bytes(value) - } -} - -#[cfg(feature = "lbf")] -impl Json for PlutusData { - fn to_json(&self) -> serde_json::Value { - match self { - PlutusData::Constr(index, fields) => json_constructor( - "Constr", - vec![json_object(vec![ - ("index".to_string(), index.to_json()), - ("fields".to_string(), fields.to_json()), - ])], - ), - PlutusData::Map(map) => json_constructor("Map", vec![map.to_json()]), - PlutusData::List(list) => json_constructor("List", vec![list.to_json()]), - PlutusData::Integer(int) => json_constructor("Integer", vec![int.to_json()]), - PlutusData::Bytes(bytes) => { - json_constructor("Bytes", vec![String::to_json(&HEXLOWER.encode(bytes))]) - } - } - } - - fn from_json(value: &serde_json::Value) -> Result { - case_json_constructor( - "PlutusV1.PlutusData", - vec![ - ( - "Constr", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => case_json_object( - |obj| { - let index = obj.get("index").ok_or(Error::UnexpectedFieldName { - wanted: "index".to_owned(), - got: obj.keys().cloned().collect(), - parser: "PlutusV1.PlutusData".to_owned(), - })?; - - let fields = - obj.get("fields").ok_or(Error::UnexpectedFieldName { - wanted: "fields".to_owned(), - got: obj.keys().cloned().collect(), - parser: "PlutusV1.PlutusData".to_owned(), - })?; - Ok(PlutusData::Constr( - BigInt::from_json(index)?, - >::from_json(fields)?, - )) - }, - val, - ), - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ( - "Map", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => Ok(PlutusData::Map(Json::from_json(val)?)), - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ( - "List", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => Ok(PlutusData::List(Json::from_json(val)?)), - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ( - "Integer", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => Ok(PlutusData::Integer(Json::from_json(val)?)), - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ( - "Bytes", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => { - let bytes = String::from_json(val).and_then(|str| { - HEXLOWER.decode(&str.into_bytes()).map_err(|_| { - Error::UnexpectedJsonInvariant { - wanted: "base16 string".to_owned(), - got: "unexpected string".to_owned(), - parser: "Plutus.V1.Bytes".to_owned(), - } - }) - })?; - Ok(PlutusData::Bytes(bytes)) - } - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ], - value, - ) - } -} +pub use plutus_data::{ + is_plutus_data::aux::*, IsPlutusData, PlutusData, PlutusDataError, PlutusType, +}; /// Deserialise a Plutus data using parsers for each variant pub fn case_plutus_data<'a, T>( @@ -175,386 +18,10 @@ pub fn case_plutus_data<'a, 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), -} - -impl IsPlutusData for BigInt { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Integer(self.clone()) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Integer(int) => Ok(int.clone()), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Integer, - got: PlutusType::from(plutus_data), - }), - } - } -} - -impl IsPlutusData for bool { - 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)) - } - } - - 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(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - 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(), - }), - } - }) - } -} - -impl IsPlutusData for Vec { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Bytes(self.clone()) - } - - 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), - }), - } - } -} - -impl IsPlutusData for String { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Bytes(self.as_bytes().into()) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Bytes(bytes) => String::from_utf8(bytes.clone()).map_err(|err| { - PlutusDataError::InternalError(format!( - "Couldn't convert Plutus bytes to String: {:?}", - err - )) - }), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Integer, - got: PlutusType::from(plutus_data), - }), - } - } -} - -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)), - } - } - - 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(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(plutus_data), - }), - } - } -} - -impl IsPlutusData for Result -where - T: IsPlutusData, - E: IsPlutusData, -{ - 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()]), - } - } - - 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), - }), - } - } -} - -impl IsPlutusData for Vec -where - T: IsPlutusData, -{ - fn to_plutus_data(&self) -> PlutusData { - let values = self - .iter() - .map(|val| val.to_plutus_data()) - .collect::>(); - - PlutusData::List(values) - } - - 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), - }), - } - } -} - -impl IsPlutusData for BTreeSet -where - T: IsPlutusData + Eq + Ord, -{ - fn to_plutus_data(&self) -> PlutusData { - let set = self - .iter() - .map(|val| val.to_plutus_data()) - .collect::>(); - - PlutusData::List(set) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::List(vec) => vec - .iter() - .map(|val| T::from_plutus_data(val)) - .collect::>(), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Map, - got: PlutusType::from(plutus_data), - }), - } - } -} - -impl IsPlutusData for BTreeMap -where - K: IsPlutusData + Eq + Ord, - V: IsPlutusData, -{ - fn to_plutus_data(&self) -> PlutusData { - let assoc_map = self - .iter() - .map(|(key, val)| (key.to_plutus_data(), val.to_plutus_data())) - .collect::>(); - - PlutusData::Map(assoc_map) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Map(dict) => dict - .iter() - .map(|(key, val)| Ok((K::from_plutus_data(key)?, V::from_plutus_data(val)?))) - .collect::>(), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Map, - got: PlutusType::from(plutus_data), - }), - } - } -} - -impl IsPlutusData for () { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(BigInt::from(0), Vec::with_capacity(0)) - } - - 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), - }), - } - } -} - -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 IsPlutusData for (A, B) -where - A: IsPlutusData, - B: IsPlutusData, -{ - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(0), - 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(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(plutus_data), - }), - } + PlutusData::Constr(tag, args) => ctor_case(&tag)(&args), + PlutusData::List(args) => list_case(&args), + PlutusData::Integer(i) => int_case(&i), + other => other_case(&other), } } @@ -642,64 +109,3 @@ impl TryFromPLA> for csl::PlutusMap { }) } } - -/// 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), - }) - } else { - Ok(()) - } -} - -/// Given a vector of PlutusData, parse it as an array whose length is known at -/// compile time. -pub fn parse_fixed_len_constr_fields( - v: &[PlutusData], -) -> Result<&[PlutusData; LEN], PlutusDataError> { - v.try_into() - .map_err(|_| PlutusDataError::UnexpectedListLength { - got: v.len(), - wanted: LEN, - }) -} - -/// Given a PlutusData, parse it as PlutusData::Constr and its tag as u32. Return -/// the u32 tag and fields. -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 { - got: err.to_string(), - wanted: "Constr bigint tag within u32 range".into(), - }) - .map(|tag| (tag, fields)), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } -} - -/// Given a PlutusData, parse it as PlutusData::Constr and verify its tag. -pub fn parse_constr_with_tag( - data: &PlutusData, - expected_tag: u32, -) -> 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), - }) - } else { - Ok(fields) - } -} diff --git a/plutus-ledger-api/src/v1/address.rs b/plutus-ledger-api/src/v1/address.rs index b80677f..9230518 100644 --- a/plutus-ledger-api/src/v1/address.rs +++ b/plutus-ledger-api/src/v1/address.rs @@ -3,12 +3,11 @@ use std::str::FromStr; use anyhow::anyhow; use cardano_serialization_lib as csl; +use plutus_data::is_plutus_data::aux::{parse_constr, parse_fixed_len_constr_fields}; use crate::csl::csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA}; use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; -use crate::plutus_data::{ - verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType, -}; +use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; use crate::v1::crypto::Ed25519PubKeyHash; use crate::v1::script::ValidatorHash; #[cfg(feature = "lbf")] @@ -27,7 +26,7 @@ use serde::{Deserialize, Serialize}; /// 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct Address { @@ -44,43 +43,6 @@ impl Address { } } -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(), - ], - ) - } - - 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(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } - } -} - impl FromStr for Address { type Err = anyhow::Error; @@ -162,54 +124,13 @@ impl std::fmt::Display for AddressWithExtraInfo<'_> { //////////////// /// A public key hash or validator hash credential (used as a payment or a staking credential) -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, IsPlutusData)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Credential { PubKey(Ed25519PubKeyHash), Script(ValidatorHash), } -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()]) - } - } - } - - 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(1) => { - verify_constr_fields(fields, 1)?; - Ok(Credential::Script(ValidatorHash::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(data), - }), - } - } -} - #[cfg(feature = "lbf")] impl Json for Credential { fn to_json(&self) -> serde_json::Value { @@ -289,6 +210,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 { @@ -311,31 +233,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(), }), } } @@ -467,21 +381,12 @@ impl TryFromPLA for csl::Pointer { ////////// /// 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() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) - } -} - impl FromCSL for Slot { fn from_csl(value: &csl::BigNum) -> Self { Slot(BigInt::from_csl(value)) @@ -499,21 +404,12 @@ impl TryFromPLA for csl::BigNum { ////////////////////// /// 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() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) - } -} - impl FromCSL for CertificateIndex { fn from_csl(value: &csl::BigNum) -> Self { CertificateIndex(BigInt::from_csl(value)) @@ -532,21 +428,12 @@ impl TryFromPLA for csl::BigNum { /// 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() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) - } -} - impl FromCSL for TransactionIndex { fn from_csl(value: &csl::BigNum) -> Self { TransactionIndex(BigInt::from_csl(value)) diff --git a/plutus-ledger-api/src/v1/crypto.rs b/plutus-ledger-api/src/v1/crypto.rs index 4517dd6..4555ff9 100644 --- a/plutus-ledger-api/src/v1/crypto.rs +++ b/plutus-ledger-api/src/v1/crypto.rs @@ -3,15 +3,13 @@ use cardano_serialization_lib as csl; use data_encoding::HEXLOWER; #[cfg(feature = "lbf")] use lbr_prelude::json::{Error, Json}; +use plutus_data::IsPlutusData; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::{ - csl::{ - csl_to_pla::FromCSL, - pla_to_csl::{TryFromPLA, TryFromPLAError}, - }, - plutus_data::{IsPlutusData, PlutusData, PlutusDataError, PlutusType}, +use crate::csl::{ + csl_to_pla::FromCSL, + pla_to_csl::{TryFromPLA, TryFromPLAError}, }; /////////////////////// @@ -21,28 +19,12 @@ use crate::{ /// 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()) - } - - 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 Ed25519PubKeyHash { fn from_csl(value: &csl::Ed25519KeyHash) -> Self { Ed25519PubKeyHash(LedgerBytes(value.to_bytes())) @@ -69,61 +51,30 @@ impl FromCSL for Vec { /////////////////////// /// 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); @@ -139,22 +90,6 @@ impl std::fmt::Display for LedgerBytes { } } -impl IsPlutusData for LedgerBytes { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Bytes(self.0.clone()) - } - - 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), - }), - } - } -} - #[cfg(feature = "lbf")] impl Json for LedgerBytes { fn to_json(&self) -> serde_json::Value { diff --git a/plutus-ledger-api/src/v1/datum.rs b/plutus-ledger-api/src/v1/datum.rs index eba5f64..49901ab 100644 --- a/plutus-ledger-api/src/v1/datum.rs +++ b/plutus-ledger-api/src/v1/datum.rs @@ -4,7 +4,7 @@ use cardano_serialization_lib as csl; use crate::csl::csl_to_pla::FromCSL; use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; -use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; +use crate::plutus_data::{IsPlutusData, PlutusData}; use crate::v1::crypto::LedgerBytes; #[cfg(feature = "lbf")] use lbr_prelude::json::Json; @@ -17,21 +17,12 @@ use serde::{Deserialize, Serialize}; /////////////// /// 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() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) - } -} - impl FromCSL for DatumHash { fn from_csl(value: &csl::DataHash) -> Self { DatumHash(LedgerBytes(value.to_bytes())) @@ -49,21 +40,12 @@ impl TryFromPLA for csl::DataHash { /////////// /// 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 399e64a..eb78927 100644 --- a/plutus-ledger-api/src/v1/interval.rs +++ b/plutus-ledger-api/src/v1/interval.rs @@ -1,11 +1,12 @@ //! Types related to PlutusInterval use crate::feature_traits::FeatureTraits; -use crate::plutus_data::{ - verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType, -}; +use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; #[cfg(feature = "lbf")] use lbr_prelude::json::Json; use num_bigint::BigInt; +use plutus_data::is_plutus_data::aux::{ + parse_constr, parse_constr_with_tag, parse_fixed_len_constr_fields, +}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::cmp; @@ -268,26 +269,12 @@ 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)?, + }) } } @@ -318,26 +305,12 @@ 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)?, + }) } } @@ -368,26 +341,12 @@ 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)?, + }) } } @@ -465,31 +424,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 95ffa9c..c46e39a 100644 --- a/plutus-ledger-api/src/v1/redeemer.rs +++ b/plutus-ledger-api/src/v1/redeemer.rs @@ -8,7 +8,7 @@ use lbr_prelude::json::Json; use serde::{Deserialize, Serialize}; use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; -use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; +use crate::plutus_data::{IsPlutusData, PlutusData}; use crate::v1::crypto::LedgerBytes; ////////////// @@ -17,21 +17,12 @@ use crate::v1::crypto::LedgerBytes; /// 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() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) - } -} - #[derive(Clone, Debug)] pub struct RedeemerWithExtraInfo<'a> { pub redeemer: &'a Redeemer, @@ -56,17 +47,8 @@ impl TryFromPLA> for csl::Redeemer { ////////////////// /// 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 ddfcb89..5afcf2a 100644 --- a/plutus-ledger-api/src/v1/script.rs +++ b/plutus-ledger-api/src/v1/script.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use crate::csl::csl_to_pla::FromCSL; use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; -use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; +use crate::plutus_data::IsPlutusData; use crate::v1::crypto::LedgerBytes; /////////////////// @@ -17,21 +17,12 @@ use crate::v1::crypto::LedgerBytes; /////////////////// /// 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() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) - } -} - impl FromCSL for ValidatorHash { fn from_csl(value: &csl::ScriptHash) -> Self { ValidatorHash(ScriptHash::from_csl(value)) @@ -43,21 +34,12 @@ impl FromCSL for ValidatorHash { /////////////////////// /// 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() - } - - 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()))) @@ -75,21 +57,12 @@ impl TryFromPLA for csl::PolicyID { //////////////// /// 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() - } - - fn from_plutus_data(data: &PlutusData) -> Result { - IsPlutusData::from_plutus_data(data).map(Self) - } -} - impl FromCSL for ScriptHash { fn from_csl(value: &csl::ScriptHash) -> Self { ScriptHash(LedgerBytes(value.to_bytes())) diff --git a/plutus-ledger-api/src/v1/transaction.rs b/plutus-ledger-api/src/v1/transaction.rs index 54fcff0..efe574b 100644 --- a/plutus-ledger-api/src/v1/transaction.rs +++ b/plutus-ledger-api/src/v1/transaction.rs @@ -16,16 +16,10 @@ use super::{ value::{CurrencySymbol, Value}, }; +use crate::csl::{csl_to_pla::FromCSL, pla_to_csl::TryFromPLA}; use crate::{ csl::pla_to_csl::{TryFromPLAError, TryToCSL}, - plutus_data::{ - parse_constr, parse_constr_with_tag, parse_fixed_len_constr_fields, verify_constr_fields, - IsPlutusData, PlutusData, PlutusDataError, PlutusType, - }, -}; -use crate::{ - csl::{csl_to_pla::FromCSL, pla_to_csl::TryFromPLA}, - utils::aux::{none, singleton}, + plutus_data::IsPlutusData, }; ////////////////////// @@ -36,7 +30,7 @@ use crate::{ /// /// 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionInput { @@ -50,41 +44,6 @@ impl fmt::Display for TransactionInput { } } -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(), - ], - ) - } - - 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 { @@ -129,7 +88,7 @@ impl TryFromPLA> for csl::TransactionInputs { /// /// 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionHash(pub LedgerBytes); @@ -140,32 +99,6 @@ impl fmt::Display for TransactionHash { } } -impl IsPlutusData for TransactionHash { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(BigInt::from(0), vec![self.0.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, 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 FromCSL for TransactionHash { fn from_csl(value: &csl::TransactionHash) -> Self { TransactionHash(LedgerBytes(value.to_bytes())) @@ -187,7 +120,7 @@ impl TryFromPLA for csl::TransactionHash { /// /// 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionOutput { @@ -196,63 +129,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 { @@ -293,7 +180,7 @@ pub type POSIXTimeRange = PlutusInterval; ////////////// /// An input of a pending transaction. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TxInInfo { @@ -301,41 +188,6 @@ 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 } @@ -347,7 +199,7 @@ impl From<(TransactionInput, TransactionOutput)> for TxInInfo { /////////// /// 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub enum DCert { @@ -375,78 +227,12 @@ 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub enum ScriptPurpose { @@ -456,41 +242,12 @@ 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: "Constr tag to be 0, 1, 2 or 3".to_string(), - }), - } - } -} - ///////////////////// // 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionInfo { @@ -506,73 +263,15 @@ 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)] #[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 7c02681..88ce277 100644 --- a/plutus-ledger-api/src/v1/value.rs +++ b/plutus-ledger-api/src/v1/value.rs @@ -20,9 +20,7 @@ use serde_json; use crate::csl::csl_to_pla::FromCSL; use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; -use crate::plutus_data::{ - verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType, -}; +use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; use crate::utils::aux::{singleton, union_b_tree_maps_with, union_btree_maps_with}; use crate::v1::crypto::LedgerBytes; use crate::v1::script::{MintingPolicyHash, ScriptHash}; @@ -643,7 +641,7 @@ impl TryFromPLA for csl::AssetName { //////////////// /// 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 { @@ -661,41 +659,6 @@ impl fmt::Display for AssetClass { } } -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(), - ], - ) - } - - 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), - }), - } - } -} - #[cfg(test)] mod test { use super::*; diff --git a/plutus-ledger-api/src/v2/datum.rs b/plutus-ledger-api/src/v2/datum.rs index a870811..e247d7d 100644 --- a/plutus-ledger-api/src/v2/datum.rs +++ b/plutus-ledger-api/src/v2/datum.rs @@ -3,7 +3,6 @@ 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}; @@ -14,7 +13,7 @@ use crate::{ csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA}, pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}, }, - plutus_data::{verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType}, + plutus_data::IsPlutusData, }; ///////////////// @@ -26,7 +25,7 @@ use crate::{ /// 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum OutputDatum { None, @@ -34,52 +33,6 @@ 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 { diff --git a/plutus-ledger-api/src/v2/transaction.rs b/plutus-ledger-api/src/v2/transaction.rs index 9374256..65335e4 100644 --- a/plutus-ledger-api/src/v2/transaction.rs +++ b/plutus-ledger-api/src/v2/transaction.rs @@ -11,10 +11,7 @@ use serde::{Deserialize, Serialize}; 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_with_tag, parse_fixed_len_constr_fields}; -use crate::plutus_data::{ - verify_constr_fields, IsPlutusData, PlutusData, PlutusDataError, PlutusType, -}; +use crate::plutus_data::IsPlutusData; #[cfg(feature = "chrono")] pub use crate::v1::transaction::POSIXTimeConversionError; pub use crate::v1::transaction::{ @@ -40,7 +37,7 @@ use super::{ /// /// 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionOutput { @@ -50,45 +47,6 @@ 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(), - ], - ) - } - - 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(), - }), - }, - - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } - } -} - impl TryFromCSL for TransactionOutput { fn try_from_csl(value: &csl::TransactionOutput) -> Result { Ok(TransactionOutput { @@ -205,7 +163,7 @@ impl TryFromPLA> for csl::TransactionOutput { ////////////// /// An input of a pending transaction. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, IsPlutusData)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TxInInfo { @@ -219,47 +177,11 @@ 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)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionInfo { @@ -277,49 +199,6 @@ 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(), - ], - ) - } - - 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)?, - }) - } -} - #[derive(Clone, Debug)] pub struct WithdrawalsWithExtraInfo<'a> { pub withdrawals: &'a AssocMap, @@ -350,29 +229,10 @@ impl TryFromPLA> for csl::Withdrawals { /////////////////// /// The context that is presented to the currently-executing script. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, IsPlutusData)] #[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)?, - }) - } -} From b4351b77c7272a2ea2bcc845b3a7dd4ec9e4b7fe Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Fri, 18 Oct 2024 23:52:08 +0800 Subject: [PATCH 08/12] explicitly mark derive strategies --- plutus-ledger-api/src/v1/address.rs | 7 ++++--- plutus-ledger-api/src/v1/transaction.rs | 8 ++++++++ plutus-ledger-api/src/v2/datum.rs | 1 + plutus-ledger-api/src/v2/transaction.rs | 4 ++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/plutus-ledger-api/src/v1/address.rs b/plutus-ledger-api/src/v1/address.rs index 9230518..f3c1aba 100644 --- a/plutus-ledger-api/src/v1/address.rs +++ b/plutus-ledger-api/src/v1/address.rs @@ -13,20 +13,20 @@ use crate::v1::script::ValidatorHash; #[cfg(feature = "lbf")] use lbr_prelude::json::{self, Error, Json}; use num_bigint::BigInt; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; ///////////// // Address // ///////////// -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - /// 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, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct Address { @@ -125,6 +125,7 @@ impl std::fmt::Display for AddressWithExtraInfo<'_> { /// 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), diff --git a/plutus-ledger-api/src/v1/transaction.rs b/plutus-ledger-api/src/v1/transaction.rs index efe574b..44c4533 100644 --- a/plutus-ledger-api/src/v1/transaction.rs +++ b/plutus-ledger-api/src/v1/transaction.rs @@ -31,6 +31,7 @@ use crate::{ /// 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, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "lbf", derive(Json))] pub struct TransactionInput { @@ -89,6 +90,7 @@ impl TryFromPLA> for csl::TransactionInputs { /// 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, 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); @@ -121,6 +123,7 @@ impl TryFromPLA for csl::TransactionHash { /// This must include the target address, the hash of the datum attached, and the amount of output /// tokens #[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 { @@ -181,6 +184,7 @@ pub type POSIXTimeRange = PlutusInterval; /// An input of a pending transaction. #[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 { @@ -200,6 +204,7 @@ impl From<(TransactionInput, TransactionOutput)> for TxInInfo { /// Partial representation of digests of certificates on the ledger. #[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 { @@ -233,6 +238,7 @@ pub enum DCert { /// The purpose of the script that's currently running. #[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 { @@ -248,6 +254,7 @@ pub enum ScriptPurpose { /// A pending transaction as seen by validator scripts, also known as TxInfo in Plutus #[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 { @@ -269,6 +276,7 @@ pub struct TransactionInfo { /// The context that is presented to the currently-executing script. #[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 { diff --git a/plutus-ledger-api/src/v2/datum.rs b/plutus-ledger-api/src/v2/datum.rs index e247d7d..9a3ec6c 100644 --- a/plutus-ledger-api/src/v2/datum.rs +++ b/plutus-ledger-api/src/v2/datum.rs @@ -26,6 +26,7 @@ use crate::{ /// 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, IsPlutusData)] +#[is_plutus_data_derive_strategy = "Constr"] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum OutputDatum { None, diff --git a/plutus-ledger-api/src/v2/transaction.rs b/plutus-ledger-api/src/v2/transaction.rs index 65335e4..2b2fab0 100644 --- a/plutus-ledger-api/src/v2/transaction.rs +++ b/plutus-ledger-api/src/v2/transaction.rs @@ -38,6 +38,7 @@ use super::{ /// This must include the target address, an optional datum, an optional reference script, and the /// amount of output tokens #[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 { @@ -164,6 +165,7 @@ impl TryFromPLA> for csl::TransactionOutput { /// An input of a pending transaction. #[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 { @@ -182,6 +184,7 @@ impl From<(TransactionInput, TransactionOutput)> for TxInInfo { /// A pending transaction as seen by validator scripts, also known as TxInfo in Plutus #[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 { @@ -230,6 +233,7 @@ impl TryFromPLA> for csl::Withdrawals { /// The context that is presented to the currently-executing script. #[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 { From 3e6428164a7eb46075ea7bcdd2f31b8cd620956c Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Mon, 21 Oct 2024 20:27:57 +0800 Subject: [PATCH 09/12] move plutus-data back to where it was --- is-plutus-data-derive/src/derive_impl.rs | 52 +- plutus-ledger-api/Cargo.lock | 15 +- plutus-ledger-api/Cargo.toml | 6 +- plutus-ledger-api/src/lamval.rs | 4 +- plutus-ledger-api/src/plutus_data.rs | 588 ++++++++++++++++++++++- plutus-ledger-api/src/v1/address.rs | 15 +- plutus-ledger-api/src/v1/crypto.rs | 11 +- plutus-ledger-api/src/v1/datum.rs | 2 +- plutus-ledger-api/src/v1/interval.rs | 9 +- plutus-ledger-api/src/v1/redeemer.rs | 1 + plutus-ledger-api/src/v1/script.rs | 1 + plutus-ledger-api/src/v1/transaction.rs | 1 + plutus-ledger-api/src/v1/value.rs | 1 + plutus-ledger-api/src/v2/datum.rs | 2 + plutus-ledger-api/src/v2/transaction.rs | 1 + 15 files changed, 635 insertions(+), 74 deletions(-) diff --git a/is-plutus-data-derive/src/derive_impl.rs b/is-plutus-data-derive/src/derive_impl.rs index 0b0886c..7195196 100644 --- a/is-plutus-data-derive/src/derive_impl.rs +++ b/is-plutus-data-derive/src/derive_impl.rs @@ -32,12 +32,12 @@ pub(crate) fn get_is_plutus_data_instance(input: DeriveInput) -> Result plutus_data::PlutusData { + 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_data::PlutusData) -> Result + fn from_plutus_data(plutus_data: &plutus_ledger_api::plutus_data::PlutusData) -> Result where Self: Sized { #decoder } @@ -170,15 +170,15 @@ fn get_newtype_encoder_decoder(input: &DeriveInput) -> Result<(Block, Block)> { Some(field_name) => { parse_quote!({ Ok(Self { - #field_name: plutus_data::IsPlutusData::from_plutus_data(plutus_data)? + #field_name: plutus_ledger_api::plutus_data::IsPlutusData::from_plutus_data(plutus_data)? }) }) } None => { parse_quote!({ - Ok(Self(plutus_data::IsPlutusData::from_plutus_data( - plutus_data, - )?)) + Ok(Self( + plutus_ledger_api::plutus_data::IsPlutusData::from_plutus_data(plutus_data)?, + )) }) } }; @@ -310,11 +310,11 @@ fn enum_from_plutus_data_constr(e: &DataEnum, plutus_data_input_var: &Ident) -> parse_quote!( { - let (tag, #plutus_data_list_var) = plutus_data::is_plutus_data::aux::parse_constr(#plutus_data_input_var)?; + let (tag, #plutus_data_list_var) = plutus_ledger_api::plutus_data::parse_constr(#plutus_data_input_var)?; match tag { #(#arms),* - tag => Err(plutus_data::PlutusDataError::UnexpectedPlutusInvariant { + tag => Err(plutus_ledger_api::plutus_data::PlutusDataError::UnexpectedPlutusInvariant { wanted: format!(#expected_tags_str), got: tag.to_string(), }), @@ -380,7 +380,7 @@ fn variant_with_named_fields_to_plutus_data( let plutus_data_list = data_fields_to_list_of_plutus_data(&field_accessors); parse_quote!( - #constructor{ #(#field_names),* } => plutus_data::PlutusData::Constr(#tag.into(), #plutus_data_list) + #constructor{ #(#field_names),* } => plutus_ledger_api::plutus_data::PlutusData::Constr(#tag.into(), #plutus_data_list) ) } @@ -407,7 +407,7 @@ fn variant_with_unnamed_field_to_plutus_data( let plutus_data_list = data_fields_to_list_of_plutus_data(&field_accessors); parse_quote!( - #constructor(#(#field_names),*) => plutus_data::PlutusData::Constr(#tag.into(), #plutus_data_list) + #constructor(#(#field_names),*) => plutus_ledger_api::plutus_data::PlutusData::Constr(#tag.into(), #plutus_data_list) ) } @@ -425,7 +425,7 @@ fn variant_with_unnamed_fields_from_plutus_data_list( fn variant_with_no_field_to_plutus_data(constructor: &Path, tag: usize) -> Arm { parse_quote!( - #constructor => plutus_data::PlutusData::Constr(#tag.into(), vec![]) + #constructor => plutus_ledger_api::plutus_data::PlutusData::Constr(#tag.into(), vec![]) ) } @@ -463,7 +463,7 @@ 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_data::PlutusData::List(#to_list_of_plutus_data) + plutus_ledger_api::plutus_data::PlutusData::List(#to_list_of_plutus_data) }) } @@ -477,7 +477,7 @@ fn struct_with_named_fields_from_plutus_data_list( struct_with_named_fields_from_list_of_plutus_data(fields, &list_of_plutus_data_var); parse_quote!({ - let #list_of_plutus_data_var = plutus_data::is_plutus_data::aux::parse_list(#plutus_data_input_var)?; + let #list_of_plutus_data_var = plutus_ledger_api::plutus_data::parse_list(#plutus_data_input_var)?; #from_list_of_plutus_data }) @@ -487,7 +487,7 @@ 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_data::PlutusData::Constr(0.into(), #to_list_of_plutus_data) + plutus_ledger_api::plutus_data::PlutusData::Constr(0.into(), #to_list_of_plutus_data) }) } @@ -501,7 +501,7 @@ fn struct_with_named_fields_from_plutus_data_constr( struct_with_named_fields_from_list_of_plutus_data(fields, &plutus_data_list_var); parse_quote!({ - let #plutus_data_list_var = plutus_data::is_plutus_data::aux::parse_constr_with_tag(#plutus_data_input_var, 0)?; + let #plutus_data_list_var = plutus_ledger_api::plutus_data::parse_constr_with_tag(#plutus_data_input_var, 0)?; #from_plutus_data_list }) @@ -537,7 +537,7 @@ fn struct_with_unnamed_fields_to_plutus_data_list(fields: &FieldsUnnamed) -> Blo let to_list_of_plutus_data = struct_with_unnamed_fields_to_list_of_plutus_data(fields); parse_quote!({ - plutus_data::PlutusData::List(#to_list_of_plutus_data) + plutus_ledger_api::plutus_data::PlutusData::List(#to_list_of_plutus_data) }) } @@ -551,7 +551,7 @@ fn struct_with_unnamed_fields_from_plutus_data_list( struct_with_unnamed_fields_from_list_of_plutus_data(fields, &list_of_plutus_data_var); parse_quote!({ - let #list_of_plutus_data_var = plutus_data::is_plutus_data::aux::parse_list(#plutus_data_input_var)?; + let #list_of_plutus_data_var = plutus_ledger_api::plutus_data::parse_list(#plutus_data_input_var)?; #from_list_of_plutus_data }) @@ -561,7 +561,7 @@ fn struct_with_unnamed_fields_to_plutus_data_constr(fields: &FieldsUnnamed) -> B let to_list_of_plutus_data = struct_with_unnamed_fields_to_list_of_plutus_data(fields); parse_quote!({ - plutus_data::PlutusData::Constr(0.into(), #to_list_of_plutus_data) + plutus_ledger_api::plutus_data::PlutusData::Constr(0.into(), #to_list_of_plutus_data) }) } @@ -574,7 +574,7 @@ fn struct_with_unnamed_fields_from_plutus_data_constr( let from_fields = struct_with_unnamed_fields_from_list_of_plutus_data(fields, &fields_var); parse_quote!({ - let #fields_var = plutus_data::is_plutus_data::aux::parse_constr_with_tag(#plutus_data_input_var, 0)?; + let #fields_var = plutus_ledger_api::plutus_data::parse_constr_with_tag(#plutus_data_input_var, 0)?; #from_fields }) @@ -591,7 +591,7 @@ fn struct_with_no_field_from_plutus_data_list(plutus_data_input_var: &Ident) -> 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_data::is_plutus_data::aux::parse_list(#plutus_data_input_var)?; + let #list_of_plutus_data_var = plutus_ledger_api::plutus_data::parse_list(#plutus_data_input_var)?; #from_list_of_plutus_data }) @@ -608,7 +608,7 @@ fn struct_with_no_field_from_plutus_data_constr(plutus_data_input_var: &Ident) - data_with_no_fields_from_list_of_plutus_data(&parse_quote!(Self), &fields_var); parse_quote!({ - let #fields_var = plutus_data::is_plutus_data::aux::parse_constr_with_tag(#plutus_data_input_var, 0)?; + let #fields_var = plutus_ledger_api::plutus_data::parse_constr_with_tag(#plutus_data_input_var, 0)?; #from_fields }) @@ -641,14 +641,14 @@ fn data_with_named_fields_from_list_of_plutus_data( 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_data::IsPlutusData::from_plutus_data(#unparsed_field_ident)?; + let #field_ident = plutus_ledger_api::plutus_data::IsPlutusData::from_plutus_data(#unparsed_field_ident)?; ) }, ); parse_quote!( { - let [ #(#unparsed_field_idents),* ] = plutus_data::is_plutus_data::aux::parse_fixed_len_constr_fields::<#field_count>(#plutus_data_list_var)?; + 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),* }) } @@ -678,7 +678,7 @@ fn data_with_unnamed_fields_from_list_of_plutus_data( }); parse_quote!({ - let [ #(#unparsed_field_idents),* ] = plutus_data::is_plutus_data::aux::parse_fixed_len_constr_fields::<#field_count>(#plutus_data_list_var)?; + 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),*)) }) @@ -689,7 +689,7 @@ fn data_with_no_fields_from_list_of_plutus_data( list_of_plutus_data_var: &Ident, ) -> Block { parse_quote!({ - let [ ] = plutus_data::is_plutus_data::aux::parse_fixed_len_constr_fields::<0>(#list_of_plutus_data_var)?; + let [ ] = plutus_ledger_api::plutus_data::parse_fixed_len_constr_fields::<0>(#list_of_plutus_data_var)?; Ok(#constructor) }) } diff --git a/plutus-ledger-api/Cargo.lock b/plutus-ledger-api/Cargo.lock index e2f7fa2..79fed43 100644 --- a/plutus-ledger-api/Cargo.lock +++ b/plutus-ledger-api/Cargo.lock @@ -608,19 +608,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "plutus-data" -version = "0.1.0" -dependencies = [ - "data-encoding", - "is-plutus-data-derive", - "lbr-prelude", - "num-bigint", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "plutus-ledger-api" version = "2.0.0-beta.1" @@ -631,11 +618,11 @@ dependencies = [ "data-encoding", "goldie", "impl_ops", + "is-plutus-data-derive", "lbr-prelude", "linked-hash-map", "num-bigint", "num-traits", - "plutus-data", "proptest", "serde", "serde_json", diff --git a/plutus-ledger-api/Cargo.toml b/plutus-ledger-api/Cargo.toml index d09b07f..1ece481 100644 --- a/plutus-ledger-api/Cargo.toml +++ b/plutus-ledger-api/Cargo.toml @@ -22,13 +22,13 @@ num-traits = "~0.2.17" impl_ops = "0.1.1" chrono = { version = "0.4.34", optional = true } cardano-serialization-lib = "12.1.0" +is-plutus-data-derive = { path = ".extras/is-plutus-data-derive-0" } anyhow = "1.0.86" -plutus-data = { path = ".extras/plutus-data-0", features = [ "derive" ]} [features] default = [] -serde = ["dep:serde", "num-bigint/serde", "dep:serde_json", "plutus-data/serde"] -lbf = ["dep:lbr-prelude", "dep:serde_json", "plutus-data/lbf"] +serde = ["dep:serde", "num-bigint/serde", "dep:serde_json"] +lbf = ["dep:lbr-prelude", "dep:serde_json"] chrono = ["dep:chrono"] [dev-dependencies] 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/plutus_data.rs b/plutus-ledger-api/src/plutus_data.rs index e446ed7..0be189b 100644 --- a/plutus-ledger-api/src/plutus_data.rs +++ b/plutus-ledger-api/src/plutus_data.rs @@ -1,27 +1,109 @@ //! 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 plutus_data::{ - is_plutus_data::aux::*, IsPlutusData, PlutusData, PlutusDataError, PlutusType, +pub use is_plutus_data_derive::IsPlutusData; + +#[cfg(feature = "lbf")] +use data_encoding::HEXLOWER; +#[cfg(feature = "lbf")] +use lbr_prelude::error::Error; +#[cfg(feature = "lbf")] +use lbr_prelude::json::{ + case_json_constructor, case_json_object, json_constructor, json_object, Json, }; -/// 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), +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// Data representation of on-chain data such as Datums and Redeemers +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum PlutusData { + Constr(BigInt, Vec), + Map(Vec<(PlutusData, PlutusData)>), + List(Vec), + Integer(BigInt), + 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) + } + + pub fn map(fields: Vec<(PlutusData, PlutusData)>) -> Self { + PlutusData::Map(fields) + } + + pub fn list(fields: Vec) -> Self { + PlutusData::List(fields) + } + + pub fn integer(value: u32) -> Self { + PlutusData::Integer(BigInt::from(value)) + } + + pub fn bytes(value: Vec) -> Self { + PlutusData::Bytes(value) + } +} + +impl IsPlutusData for PlutusData { + fn to_plutus_data(&self) -> PlutusData { + self.clone() + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + Ok(plutus_data.clone()) } } @@ -44,6 +126,399 @@ impl TryFromCSL for PlutusData { } } +#[cfg(feature = "lbf")] +impl Json for PlutusData { + fn to_json(&self) -> serde_json::Value { + match self { + PlutusData::Constr(index, fields) => json_constructor( + "Constr", + vec![json_object(vec![ + ("index".to_string(), index.to_json()), + ("fields".to_string(), fields.to_json()), + ])], + ), + PlutusData::Map(map) => json_constructor("Map", vec![map.to_json()]), + PlutusData::List(list) => json_constructor("List", vec![list.to_json()]), + PlutusData::Integer(int) => json_constructor("Integer", vec![int.to_json()]), + PlutusData::Bytes(bytes) => { + json_constructor("Bytes", vec![String::to_json(&HEXLOWER.encode(bytes))]) + } + } + } + + fn from_json(value: &serde_json::Value) -> Result { + case_json_constructor( + "PlutusV1.PlutusData", + vec![ + ( + "Constr", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => case_json_object( + |obj| { + let index = obj.get("index").ok_or(Error::UnexpectedFieldName { + wanted: "index".to_owned(), + got: obj.keys().cloned().collect(), + parser: "PlutusV1.PlutusData".to_owned(), + })?; + + let fields = + obj.get("fields").ok_or(Error::UnexpectedFieldName { + wanted: "fields".to_owned(), + got: obj.keys().cloned().collect(), + parser: "PlutusV1.PlutusData".to_owned(), + })?; + Ok(PlutusData::Constr( + BigInt::from_json(index)?, + >::from_json(fields)?, + )) + }, + val, + ), + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ( + "Map", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => Ok(PlutusData::Map(Json::from_json(val)?)), + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ( + "List", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => Ok(PlutusData::List(Json::from_json(val)?)), + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ( + "Integer", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => Ok(PlutusData::Integer(Json::from_json(val)?)), + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ( + "Bytes", + Box::new(|ctor_fields| match &ctor_fields[..] { + [val] => { + let bytes = String::from_json(val).and_then(|str| { + HEXLOWER.decode(&str.into_bytes()).map_err(|_| { + Error::UnexpectedJsonInvariant { + wanted: "base16 string".to_owned(), + got: "unexpected string".to_owned(), + parser: "Plutus.V1.Bytes".to_owned(), + } + }) + })?; + Ok(PlutusData::Bytes(bytes)) + } + _ => Err(Error::UnexpectedArrayLength { + wanted: 1, + got: ctor_fields.len(), + parser: "PlutusV1.PlutusData".to_owned(), + }), + }), + ), + ], + value, + ) + } +} + +// MARK: Orphan IsPlutusData Instances + +impl IsPlutusData for BigInt { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Integer(self.clone()) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + match plutus_data { + PlutusData::Integer(int) => Ok(int.clone()), + _ => Err(PlutusDataError::UnexpectedPlutusType { + wanted: PlutusType::Integer, + got: PlutusType::from(plutus_data), + }), + } + } +} + +impl IsPlutusData for Vec { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Bytes(self.clone()) + } + + 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), + }), + } + } +} + +const BOOL_FALSE_TAG: u32 = 0; +const BOOL_TRUE_TAG: u32 = 1; + +impl IsPlutusData for bool { + fn to_plutus_data(&self) -> PlutusData { + 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 { + 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(), + }), + } + } +} + +impl IsPlutusData for String { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Bytes(self.as_bytes().into()) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + match plutus_data { + PlutusData::Bytes(bytes) => String::from_utf8(bytes.clone()).map_err(|err| { + PlutusDataError::InternalError(format!( + "Couldn't convert Plutus bytes to String: {:?}", + err + )) + }), + _ => Err(PlutusDataError::UnexpectedPlutusType { + 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(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 { + let (tag, fields) = parse_constr(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, + E: IsPlutusData, +{ + fn to_plutus_data(&self) -> PlutusData { + match self { + 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 { + 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(), + }), + } + } +} + +impl IsPlutusData for Vec +where + T: IsPlutusData, +{ + fn to_plutus_data(&self) -> PlutusData { + let values = self + .iter() + .map(|val| val.to_plutus_data()) + .collect::>(); + + PlutusData::List(values) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + let list = parse_list(plutus_data)?; + list.iter().map(T::from_plutus_data).collect() + } +} + +impl IsPlutusData for BTreeSet +where + T: IsPlutusData + Eq + Ord, +{ + fn to_plutus_data(&self) -> PlutusData { + let set = self + .iter() + .map(|val| val.to_plutus_data()) + .collect::>(); + + PlutusData::List(set) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + match plutus_data { + PlutusData::List(vec) => vec + .iter() + .map(|val| T::from_plutus_data(val)) + .collect::>(), + _ => Err(PlutusDataError::UnexpectedPlutusType { + wanted: PlutusType::List, + got: PlutusType::from(plutus_data), + }), + } + } +} + +impl IsPlutusData for BTreeMap +where + K: IsPlutusData + Eq + Ord, + V: IsPlutusData, +{ + fn to_plutus_data(&self) -> PlutusData { + let assoc_map = self + .iter() + .map(|(key, val)| (key.to_plutus_data(), val.to_plutus_data())) + .collect::>(); + + PlutusData::Map(assoc_map) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + match plutus_data { + PlutusData::Map(dict) => dict + .iter() + .map(|(key, val)| Ok((K::from_plutus_data(key)?, V::from_plutus_data(val)?))) + .collect::>(), + _ => Err(PlutusDataError::UnexpectedPlutusType { + wanted: PlutusType::Map, + got: PlutusType::from(plutus_data), + }), + } + } +} + +const UNIT_TAG: u32 = 0; + +impl IsPlutusData for () { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Constr(UNIT_TAG.into(), vec![]) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + let fields = parse_constr_with_tag(plutus_data, UNIT_TAG)?; + let [] = parse_fixed_len_constr_fields::<0>(fields)?; + Ok(()) + } +} + +const PAIR_TAG: u32 = 0; + +impl IsPlutusData for (A, B) +where + A: IsPlutusData, + B: IsPlutusData, +{ + fn to_plutus_data(&self) -> PlutusData { + PlutusData::Constr( + BigInt::from(PAIR_TAG), + vec![self.0.to_plutus_data(), self.1.to_plutus_data()], + ) + } + + fn from_plutus_data(plutus_data: &PlutusData) -> Result { + 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)?)) + } +} + +// MARK: Orphan TryFromCSL instances + impl TryFromCSL for Vec { fn try_from_csl(value: &csl::PlutusList) -> Result { (0..value.len()) @@ -109,3 +584,86 @@ impl TryFromPLA> for csl::PlutusMap { }) } } + +// 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. +/// +/// 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(), + wanted: LEN, + }) +} + +/// Given a PlutusData, parse it as PlutusData::Constr and its tag as u32. Return +/// the u32 tag and fields. +/// +/// 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 { + got: err.to_string(), + wanted: "Constr bigint tag within u32 range".into(), + }) + .map(|tag| (tag, fields)), + _ => Err(PlutusDataError::UnexpectedPlutusType { + wanted: PlutusType::Constr, + got: PlutusType::from(data), + }), + } +} + +/// Given a PlutusData, parse it as PlutusData::Constr and verify its tag. +/// +/// This function is used by the derive macro. +pub fn parse_constr_with_tag( + data: &PlutusData, + expected_tag: u32, +) -> Result<&Vec, PlutusDataError> { + let (tag, fields) = parse_constr(data)?; + + if tag != expected_tag { + Err(PlutusDataError::UnexpectedPlutusInvariant { + got: tag.to_string(), + 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/v1/address.rs b/plutus-ledger-api/src/v1/address.rs index f3c1aba..1da054c 100644 --- a/plutus-ledger-api/src/v1/address.rs +++ b/plutus-ledger-api/src/v1/address.rs @@ -3,19 +3,22 @@ use std::str::FromStr; use anyhow::anyhow; use cardano_serialization_lib as csl; -use plutus_data::is_plutus_data::aux::{parse_constr, parse_fixed_len_constr_fields}; -use crate::csl::csl_to_pla::{FromCSL, TryFromCSL, TryFromCSLError, TryToPLA}; -use crate::csl::pla_to_csl::{TryFromPLA, TryFromPLAError, TryToCSL}; -use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; -use crate::v1::crypto::Ed25519PubKeyHash; -use crate::v1::script::ValidatorHash; #[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 // ///////////// diff --git a/plutus-ledger-api/src/v1/crypto.rs b/plutus-ledger-api/src/v1/crypto.rs index 4555ff9..6565e1a 100644 --- a/plutus-ledger-api/src/v1/crypto.rs +++ b/plutus-ledger-api/src/v1/crypto.rs @@ -3,13 +3,16 @@ use cardano_serialization_lib as csl; use data_encoding::HEXLOWER; #[cfg(feature = "lbf")] use lbr_prelude::json::{Error, Json}; -use plutus_data::IsPlutusData; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use crate::csl::{ - csl_to_pla::FromCSL, - pla_to_csl::{TryFromPLA, TryFromPLAError}, +use crate as plutus_ledger_api; +use crate::{ + csl::{ + csl_to_pla::FromCSL, + pla_to_csl::{TryFromPLA, TryFromPLAError}, + }, + plutus_data::IsPlutusData, }; /////////////////////// diff --git a/plutus-ledger-api/src/v1/datum.rs b/plutus-ledger-api/src/v1/datum.rs index 49901ab..c74fbbc 100644 --- a/plutus-ledger-api/src/v1/datum.rs +++ b/plutus-ledger-api/src/v1/datum.rs @@ -2,13 +2,13 @@ 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}; diff --git a/plutus-ledger-api/src/v1/interval.rs b/plutus-ledger-api/src/v1/interval.rs index eb78927..db827f8 100644 --- a/plutus-ledger-api/src/v1/interval.rs +++ b/plutus-ledger-api/src/v1/interval.rs @@ -1,12 +1,13 @@ //! Types related to PlutusInterval + use crate::feature_traits::FeatureTraits; -use crate::plutus_data::{IsPlutusData, PlutusData, PlutusDataError}; +use crate::plutus_data::{ + parse_constr, parse_constr_with_tag, parse_fixed_len_constr_fields, IsPlutusData, PlutusData, + PlutusDataError, +}; #[cfg(feature = "lbf")] use lbr_prelude::json::Json; use num_bigint::BigInt; -use plutus_data::is_plutus_data::aux::{ - parse_constr, parse_constr_with_tag, parse_fixed_len_constr_fields, -}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::cmp; diff --git a/plutus-ledger-api/src/v1/redeemer.rs b/plutus-ledger-api/src/v1/redeemer.rs index c46e39a..beeb616 100644 --- a/plutus-ledger-api/src/v1/redeemer.rs +++ b/plutus-ledger-api/src/v1/redeemer.rs @@ -7,6 +7,7 @@ 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; diff --git a/plutus-ledger-api/src/v1/script.rs b/plutus-ledger-api/src/v1/script.rs index 5afcf2a..e3f73b9 100644 --- a/plutus-ledger-api/src/v1/script.rs +++ b/plutus-ledger-api/src/v1/script.rs @@ -7,6 +7,7 @@ use lbr_prelude::json::Json; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +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; diff --git a/plutus-ledger-api/src/v1/transaction.rs b/plutus-ledger-api/src/v1/transaction.rs index 44c4533..2ef775e 100644 --- a/plutus-ledger-api/src/v1/transaction.rs +++ b/plutus-ledger-api/src/v1/transaction.rs @@ -16,6 +16,7 @@ use super::{ value::{CurrencySymbol, Value}, }; +use crate as plutus_ledger_api; use crate::csl::{csl_to_pla::FromCSL, pla_to_csl::TryFromPLA}; use crate::{ csl::pla_to_csl::{TryFromPLAError, TryToCSL}, diff --git a/plutus-ledger-api/src/v1/value.rs b/plutus-ledger-api/src/v1/value.rs index 88ce277..4c34c0c 100644 --- a/plutus-ledger-api/src/v1/value.rs +++ b/plutus-ledger-api/src/v1/value.rs @@ -18,6 +18,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "lbf")] use serde_json; +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, PlutusDataError}; diff --git a/plutus-ledger-api/src/v2/datum.rs b/plutus-ledger-api/src/v2/datum.rs index 9a3ec6c..2afce83 100644 --- a/plutus-ledger-api/src/v2/datum.rs +++ b/plutus-ledger-api/src/v2/datum.rs @@ -7,6 +7,7 @@ use lbr_prelude::json::{self, Error, Json}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate as plutus_ledger_api; pub use crate::v1::datum::{Datum, DatumHash}; use crate::{ csl::{ @@ -16,6 +17,7 @@ use crate::{ plutus_data::IsPlutusData, }; +// use crate as plutus_data/ ///////////////// // OutputDatum // ///////////////// diff --git a/plutus-ledger-api/src/v2/transaction.rs b/plutus-ledger-api/src/v2/transaction.rs index 2b2fab0..45c1c5e 100644 --- a/plutus-ledger-api/src/v2/transaction.rs +++ b/plutus-ledger-api/src/v2/transaction.rs @@ -9,6 +9,7 @@ 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; From 84f92b6a7920e2dddfca37c0a156273f1bccd401 Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Mon, 21 Oct 2024 20:29:32 +0800 Subject: [PATCH 10/12] remove plutus-data --- flake.nix | 1 - plutus-data/.envrc | 1 - plutus-data/Cargo.lock | 657 ------------------ plutus-data/Cargo.toml | 19 - plutus-data/build.nix | 19 - plutus-data/src/is_plutus_data/aux.rs | 66 -- plutus-data/src/is_plutus_data/instances.rs | 292 -------- .../src/is_plutus_data/is_plutus_data.rs | 22 - plutus-data/src/is_plutus_data/mod.rs | 3 - plutus-data/src/lib.rs | 8 - plutus-data/src/plutus_data.rs | 183 ----- plutus-ledger-api/build.nix | 1 - 12 files changed, 1272 deletions(-) delete mode 100644 plutus-data/.envrc delete mode 100644 plutus-data/Cargo.lock delete mode 100644 plutus-data/Cargo.toml delete mode 100644 plutus-data/build.nix delete mode 100644 plutus-data/src/is_plutus_data/aux.rs delete mode 100644 plutus-data/src/is_plutus_data/instances.rs delete mode 100644 plutus-data/src/is_plutus_data/is_plutus_data.rs delete mode 100644 plutus-data/src/is_plutus_data/mod.rs delete mode 100644 plutus-data/src/lib.rs delete mode 100644 plutus-data/src/plutus_data.rs diff --git a/flake.nix b/flake.nix index 29f46ea..8d68e4d 100644 --- a/flake.nix +++ b/flake.nix @@ -24,7 +24,6 @@ ./plutus-ledger-api/build.nix ./is-plutus-data-derive/build.nix - ./plutus-data/build.nix ]; debug = true; systems = [ "x86_64-linux" "x86_64-darwin" ]; diff --git a/plutus-data/.envrc b/plutus-data/.envrc deleted file mode 100644 index 6841e87..0000000 --- a/plutus-data/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake .#dev-plutus-data-rust diff --git a/plutus-data/Cargo.lock b/plutus-data/Cargo.lock deleted file mode 100644 index 690b993..0000000 --- a/plutus-data/Cargo.lock +++ /dev/null @@ -1,657 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "dissimilar" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "fastrand" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "hashbrown" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" - -[[package]] -name = "indexmap" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "is-plutus-data-derive" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "thiserror", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "lbr-prelude" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21471892874c4667636a067ed7a158b9691178988a4c5988585e1010e9b5817d" -dependencies = [ - "data-encoding", - "lbr-prelude-derive", - "num-bigint", - "proptest", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "lbr-prelude-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370d0aa0809ee84ccf7c6d84ae2d6ac97b84cb1337b3d145101bcf6a7319deac" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "trybuild", -] - -[[package]] -name = "libc" -version = "0.2.160" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "plutus-data" -version = "0.1.0" -dependencies = [ - "data-encoding", - "is-plutus-data-derive", - "lbr-prelude", - "num-bigint", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro2" -version = "1.0.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags", - "lazy_static", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "rustix" -version = "0.38.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - -[[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 = "tempfile" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[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 = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "trybuild" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8923cde76a6329058a86f04d033f0945a2c6df8b94093512e4ab188b3e3a8950" -dependencies = [ - "dissimilar", - "glob", - "serde", - "serde_derive", - "serde_json", - "termcolor", - "toml", -] - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -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.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -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-data/Cargo.toml b/plutus-data/Cargo.toml deleted file mode 100644 index ade6890..0000000 --- a/plutus-data/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "plutus-data" -version = "0.1.0" -edition = "2021" - -[dependencies] -serde = { version = "^1.0.189", features = ["derive"], optional = true } -serde_json = { version = "1.0.128", optional = true } -num-bigint = "~0.4" -is-plutus-data-derive = { path = ".extras/is-plutus-data-derive-0", optional = true } -lbr-prelude = { version = "0.1.1", optional = true } -data-encoding = { version = "2.6.0", optional = true } -thiserror = "1.0.64" - -[features] -default = [] -serde = ["dep:serde", "num-bigint/serde", "dep:serde_json"] -lbf = ["dep:lbr-prelude", "dep:serde_json", "dep:data-encoding"] -derive = ["dep:is-plutus-data-derive"] diff --git a/plutus-data/build.nix b/plutus-data/build.nix deleted file mode 100644 index 33b0093..0000000 --- a/plutus-data/build.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ inputs, ... }: { - perSystem = { config, system, ... }: - let - rustFlake = - inputs.flake-lang.lib.${system}.rustFlake { - src = ./.; - version = "0"; - crateName = "plutus-data"; - devShellHook = config.settings.shell.hook; - cargoNextestExtraArgs = "--all-features"; - extraSources = [ - config.packages.is-plutus-data-derive-rust-src - ]; - }; - in - { - inherit (rustFlake) packages checks devShells; - }; -} diff --git a/plutus-data/src/is_plutus_data/aux.rs b/plutus-data/src/is_plutus_data/aux.rs deleted file mode 100644 index b793f7f..0000000 --- a/plutus-data/src/is_plutus_data/aux.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::{PlutusData, PlutusDataError, PlutusType}; - -/// Given a vector of PlutusData, parse it as an array whose length is known at -/// compile time. -/// -/// 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(), - wanted: LEN, - }) -} - -/// Given a PlutusData, parse it as PlutusData::Constr and its tag as u32. Return -/// the u32 tag and fields. -/// -/// 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 { - got: err.to_string(), - wanted: "Constr bigint tag within u32 range".into(), - }) - .map(|tag| (tag, fields)), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Constr, - got: PlutusType::from(data), - }), - } -} - -/// Given a PlutusData, parse it as PlutusData::Constr and verify its tag. -/// -/// This function is used by the derive macro. -pub fn parse_constr_with_tag( - data: &PlutusData, - expected_tag: u32, -) -> Result<&Vec, PlutusDataError> { - let (tag, fields) = parse_constr(data)?; - - if tag != expected_tag { - Err(PlutusDataError::UnexpectedPlutusInvariant { - got: tag.to_string(), - 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-data/src/is_plutus_data/instances.rs b/plutus-data/src/is_plutus_data/instances.rs deleted file mode 100644 index 5c7838a..0000000 --- a/plutus-data/src/is_plutus_data/instances.rs +++ /dev/null @@ -1,292 +0,0 @@ -use std::collections::{BTreeMap, BTreeSet}; - -use num_bigint::BigInt; - -use crate::{IsPlutusData, PlutusData, PlutusDataError, PlutusType}; - -use super::aux::{parse_constr, parse_constr_with_tag, parse_fixed_len_constr_fields, parse_list}; - -impl IsPlutusData for PlutusData { - fn to_plutus_data(&self) -> PlutusData { - self.clone() - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - Ok(plutus_data.clone()) - } -} - -// MARK: Orphan Instances - -impl IsPlutusData for BigInt { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Integer(self.clone()) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Integer(int) => Ok(int.clone()), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Integer, - got: PlutusType::from(plutus_data), - }), - } - } -} - -impl IsPlutusData for Vec { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Bytes(self.clone()) - } - - 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), - }), - } - } -} - -const BOOL_FALSE_TAG: u32 = 0; -const BOOL_TRUE_TAG: u32 = 1; - -impl IsPlutusData for bool { - fn to_plutus_data(&self) -> PlutusData { - 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 { - 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(), - }), - } - } -} - -impl IsPlutusData for String { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Bytes(self.as_bytes().into()) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Bytes(bytes) => String::from_utf8(bytes.clone()).map_err(|err| { - PlutusDataError::InternalError(format!( - "Couldn't convert Plutus bytes to String: {:?}", - err - )) - }), - _ => Err(PlutusDataError::UnexpectedPlutusType { - 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(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 { - let (tag, fields) = parse_constr(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, - E: IsPlutusData, -{ - fn to_plutus_data(&self) -> PlutusData { - match self { - 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 { - 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(), - }), - } - } -} - -impl IsPlutusData for Vec -where - T: IsPlutusData, -{ - fn to_plutus_data(&self) -> PlutusData { - let values = self - .iter() - .map(|val| val.to_plutus_data()) - .collect::>(); - - PlutusData::List(values) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - let list = parse_list(plutus_data)?; - list.iter().map(T::from_plutus_data).collect() - } -} - -impl IsPlutusData for BTreeSet -where - T: IsPlutusData + Eq + Ord, -{ - fn to_plutus_data(&self) -> PlutusData { - let set = self - .iter() - .map(|val| val.to_plutus_data()) - .collect::>(); - - PlutusData::List(set) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::List(vec) => vec - .iter() - .map(|val| T::from_plutus_data(val)) - .collect::>(), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::List, - got: PlutusType::from(plutus_data), - }), - } - } -} - -impl IsPlutusData for BTreeMap -where - K: IsPlutusData + Eq + Ord, - V: IsPlutusData, -{ - fn to_plutus_data(&self) -> PlutusData { - let assoc_map = self - .iter() - .map(|(key, val)| (key.to_plutus_data(), val.to_plutus_data())) - .collect::>(); - - PlutusData::Map(assoc_map) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - match plutus_data { - PlutusData::Map(dict) => dict - .iter() - .map(|(key, val)| Ok((K::from_plutus_data(key)?, V::from_plutus_data(val)?))) - .collect::>(), - _ => Err(PlutusDataError::UnexpectedPlutusType { - wanted: PlutusType::Map, - got: PlutusType::from(plutus_data), - }), - } - } -} - -const UNIT_TAG: u32 = 0; - -impl IsPlutusData for () { - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr(UNIT_TAG.into(), vec![]) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - let fields = parse_constr_with_tag(plutus_data, UNIT_TAG)?; - let [] = parse_fixed_len_constr_fields::<0>(fields)?; - Ok(()) - } -} - -const PAIR_TAG: u32 = 0; - -impl IsPlutusData for (A, B) -where - A: IsPlutusData, - B: IsPlutusData, -{ - fn to_plutus_data(&self) -> PlutusData { - PlutusData::Constr( - BigInt::from(PAIR_TAG), - vec![self.0.to_plutus_data(), self.1.to_plutus_data()], - ) - } - - fn from_plutus_data(plutus_data: &PlutusData) -> Result { - 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)?)) - } -} diff --git a/plutus-data/src/is_plutus_data/is_plutus_data.rs b/plutus-data/src/is_plutus_data/is_plutus_data.rs deleted file mode 100644 index 4f73c15..0000000 --- a/plutus-data/src/is_plutus_data/is_plutus_data.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::{PlutusData, PlutusType}; - -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), -} diff --git a/plutus-data/src/is_plutus_data/mod.rs b/plutus-data/src/is_plutus_data/mod.rs deleted file mode 100644 index 526bebf..0000000 --- a/plutus-data/src/is_plutus_data/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod aux; -mod instances; -pub mod is_plutus_data; diff --git a/plutus-data/src/lib.rs b/plutus-data/src/lib.rs deleted file mode 100644 index c2c607a..0000000 --- a/plutus-data/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod is_plutus_data; -pub mod plutus_data; - -#[cfg(feature = "derive")] -pub use is_plutus_data_derive::IsPlutusData; - -pub use is_plutus_data::is_plutus_data::{IsPlutusData, PlutusDataError}; -pub use plutus_data::{PlutusData, PlutusType}; diff --git a/plutus-data/src/plutus_data.rs b/plutus-data/src/plutus_data.rs deleted file mode 100644 index 9c3cbfc..0000000 --- a/plutus-data/src/plutus_data.rs +++ /dev/null @@ -1,183 +0,0 @@ -use num_bigint::BigInt; - -#[cfg(feature = "lbf")] -use data_encoding::HEXLOWER; -#[cfg(feature = "lbf")] -use lbr_prelude::error::Error; -#[cfg(feature = "lbf")] -use lbr_prelude::json::{ - case_json_constructor, case_json_object, json_constructor, json_object, Json, -}; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// Data representation of on-chain data such as Datums and Redeemers -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum PlutusData { - Constr(BigInt, Vec), - Map(Vec<(PlutusData, PlutusData)>), - List(Vec), - Integer(BigInt), - Bytes(Vec), -} - -#[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, - } - } -} - -impl PlutusData { - pub fn constr(tag: u32, fields: Vec) -> Self { - PlutusData::Constr(BigInt::from(tag), fields) - } - - pub fn map(fields: Vec<(PlutusData, PlutusData)>) -> Self { - PlutusData::Map(fields) - } - - pub fn list(fields: Vec) -> Self { - PlutusData::List(fields) - } - - pub fn integer(value: u32) -> Self { - PlutusData::Integer(BigInt::from(value)) - } - - pub fn bytes(value: Vec) -> Self { - PlutusData::Bytes(value) - } -} - -#[cfg(feature = "lbf")] -impl Json for PlutusData { - fn to_json(&self) -> serde_json::Value { - match self { - PlutusData::Constr(index, fields) => json_constructor( - "Constr", - vec![json_object(vec![ - ("index".to_string(), index.to_json()), - ("fields".to_string(), fields.to_json()), - ])], - ), - PlutusData::Map(map) => json_constructor("Map", vec![map.to_json()]), - PlutusData::List(list) => json_constructor("List", vec![list.to_json()]), - PlutusData::Integer(int) => json_constructor("Integer", vec![int.to_json()]), - PlutusData::Bytes(bytes) => { - json_constructor("Bytes", vec![String::to_json(&HEXLOWER.encode(bytes))]) - } - } - } - - fn from_json(value: &serde_json::Value) -> Result { - case_json_constructor( - "PlutusV1.PlutusData", - vec![ - ( - "Constr", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => case_json_object( - |obj| { - let index = obj.get("index").ok_or(Error::UnexpectedFieldName { - wanted: "index".to_owned(), - got: obj.keys().cloned().collect(), - parser: "PlutusV1.PlutusData".to_owned(), - })?; - - let fields = - obj.get("fields").ok_or(Error::UnexpectedFieldName { - wanted: "fields".to_owned(), - got: obj.keys().cloned().collect(), - parser: "PlutusV1.PlutusData".to_owned(), - })?; - Ok(PlutusData::Constr( - BigInt::from_json(index)?, - >::from_json(fields)?, - )) - }, - val, - ), - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ( - "Map", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => Ok(PlutusData::Map(Json::from_json(val)?)), - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ( - "List", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => Ok(PlutusData::List(Json::from_json(val)?)), - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ( - "Integer", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => Ok(PlutusData::Integer(Json::from_json(val)?)), - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ( - "Bytes", - Box::new(|ctor_fields| match &ctor_fields[..] { - [val] => { - let bytes = String::from_json(val).and_then(|str| { - HEXLOWER.decode(&str.into_bytes()).map_err(|_| { - Error::UnexpectedJsonInvariant { - wanted: "base16 string".to_owned(), - got: "unexpected string".to_owned(), - parser: "Plutus.V1.Bytes".to_owned(), - } - }) - })?; - Ok(PlutusData::Bytes(bytes)) - } - _ => Err(Error::UnexpectedArrayLength { - wanted: 1, - got: ctor_fields.len(), - parser: "PlutusV1.PlutusData".to_owned(), - }), - }), - ), - ], - value, - ) - } -} diff --git a/plutus-ledger-api/build.nix b/plutus-ledger-api/build.nix index 4fa2517..67602f5 100644 --- a/plutus-ledger-api/build.nix +++ b/plutus-ledger-api/build.nix @@ -13,7 +13,6 @@ ]; extraSources = [ config.packages.is-plutus-data-derive-rust-src - config.packages.plutus-data-rust-src ]; }; From e84bbb02284e27f4af2dc955632ce89f495da692 Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Mon, 21 Oct 2024 20:40:02 +0800 Subject: [PATCH 11/12] fogot to commit .envrc --- is-plutus-data-derive/.envrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 is-plutus-data-derive/.envrc 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 From 32b00e6c6630b59ea3d95dc319de3b76ee8ae13f Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Mon, 21 Oct 2024 20:44:41 +0800 Subject: [PATCH 12/12] update changelog --- plutus-ledger-api/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plutus-ledger-api/CHANGELOG.md b/plutus-ledger-api/CHANGELOG.md index 385b92b..325603b 100644 --- a/plutus-ledger-api/CHANGELOG.md +++ b/plutus-ledger-api/CHANGELOG.md @@ -8,6 +8,8 @@ Changelog](https://keepachangelog.com/en/1.1.0). ### Added +- Added the ability to derive `IsPlutusData` instances ([#56](https://github.com/mlabs-haskell/plutus-ledger-api-rust/pull/56)) + ### Changed ### Removed