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)?, - }) - } -}