diff --git a/pallas-codec/src/utils.rs b/pallas-codec/src/utils.rs index 7b23d1f1..dab56780 100644 --- a/pallas-codec/src/utils.rs +++ b/pallas-codec/src/utils.rs @@ -268,6 +268,12 @@ impl Deref for CborWrap { #[derive(Debug)] pub struct TagWrap(I); +impl TagWrap { + pub fn new(inner: I) -> Self { + TagWrap(inner) + } +} + impl<'b, I, const T: u64> minicbor::Decode<'b> for TagWrap where I: minicbor::Decode<'b>, diff --git a/pallas-primitives/src/alonzo/crypto.rs b/pallas-primitives/src/alonzo/crypto.rs index 4d5a804e..d023d176 100644 --- a/pallas-primitives/src/alonzo/crypto.rs +++ b/pallas-primitives/src/alonzo/crypto.rs @@ -1,4 +1,4 @@ -use super::{AuxiliaryData, Header, NativeScript, PlutusData, TransactionBody}; +use super::{AuxiliaryData, Header, NativeScript, PlutusData, PlutusScript, TransactionBody}; use pallas_crypto::hash::{Hash, Hasher}; pub fn hash_block_header(data: &Header) -> Hash<32> { @@ -25,6 +25,12 @@ impl NativeScript { } } +impl PlutusScript { + pub fn to_hash(&self) -> Hash<28> { + Hasher::<224>::hash_tagged_cbor(self, 1) + } +} + impl PlutusData { pub fn to_hash(&self) -> Hash<32> { Hasher::<256>::hash_cbor(self) @@ -41,10 +47,11 @@ impl TransactionBody { mod tests { use std::str::FromStr; + use pallas_codec::minicbor::data::Int; use pallas_codec::utils::MaybeIndefArray; use pallas_crypto::hash::Hash; - use crate::alonzo::{BlockWrapper, NativeScript}; + use crate::alonzo::{BigInt, BlockWrapper, Constr, NativeScript, PlutusData}; use crate::Fragment; #[test] @@ -73,7 +80,7 @@ mod tests { } #[test] - fn native_script_hashes_cardano_cli() { + fn native_script_hashes_as_cardano_cli() { // construct an arbitrary script to use as example let ns = NativeScript::ScriptAll(MaybeIndefArray::Def(vec![ NativeScript::ScriptPubkey( @@ -91,4 +98,45 @@ mod tests { Hash::<28>::from_str(cardano_cli_output).unwrap() ) } + + #[test] + fn plutus_data_hashes_as_cardano_cli() { + // construct an arbitrary complex datum to use as example + let pd = PlutusData::Constr(Constr:: { + tag: 1280, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BigInt(BigInt::Int(Int::from(4))), + PlutusData::Constr(Constr:: { + tag: 124, + any_constructor: None, + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BigInt(BigInt::Int(Int::from(-4))), + PlutusData::Constr(Constr:: { + tag: 102, + any_constructor: Some(453), + fields: MaybeIndefArray::Indef(vec![ + PlutusData::BigInt(BigInt::Int(Int::from(2))), + PlutusData::BigInt(BigInt::Int(Int::from(3434))), + ]), + }), + PlutusData::BigInt(BigInt::Int(Int::from(-11828293))), + ]), + }), + PlutusData::BigInt(BigInt::Int(Int::from(11828293))), + ]), + }); + + // if you need to try this out in the cardano-cli, uncomment this line to see + // the json representation of the above struct: + // println!("{}", crate::ToCanonicalJson::to_json(&pd)); + + // hash that we assume correct since it was generated through the cardano-cli + let cardano_cli_output = "d9bc0eb6ac664286155f70d720cafd2af16277fbd9014a930997431a2ffbe554"; + + assert_eq!( + pd.to_hash(), + Hash::<32>::from_str(cardano_cli_output).unwrap() + ) + } } diff --git a/pallas-primitives/src/alonzo/json.rs b/pallas-primitives/src/alonzo/json.rs index b9ceea22..4139037e 100644 --- a/pallas-primitives/src/alonzo/json.rs +++ b/pallas-primitives/src/alonzo/json.rs @@ -2,14 +2,24 @@ use serde_json::json; use crate::ToCanonicalJson; +impl super::Constr { + pub fn constructor_value(&self) -> Option { + match self.tag { + 121..=127 => Some(self.tag - 121), + 1280..=1400 => Some(self.tag - 1280 + 7), + 102 => self.any_constructor, + _ => None, + } + } +} + // infered from https://github.com/input-output-hk/cardano-node/blob/c1efb2f97134c0607c982246a36e3da7266ac194/cardano-api/src/Cardano/Api/ScriptData.hs#L254 impl ToCanonicalJson for super::PlutusData { fn to_json(&self) -> serde_json::Value { match self { super::PlutusData::Constr(x) => { - let constructor = x.prefix.map(|x| x as u64).unwrap_or(x.tag); - let fields: Vec<_> = x.values.iter().map(|i| i.to_json()).collect(); - json!({ "constructor": constructor, "fields": fields }) + let fields: Vec<_> = x.fields.iter().map(|i| i.to_json()).collect(); + json!({ "constructor": x.constructor_value(), "fields": fields }) } super::PlutusData::Map(x) => { let map: Vec<_> = x diff --git a/pallas-primitives/src/alonzo/model.rs b/pallas-primitives/src/alonzo/model.rs index 1f7f915a..e7c0993d 100644 --- a/pallas-primitives/src/alonzo/model.rs +++ b/pallas-primitives/src/alonzo/model.rs @@ -908,7 +908,9 @@ impl minicbor::encode::Encode for NativeScript { } } -pub type PlutusScript = ByteVec; +#[derive(Encode, Decode, Debug, PartialEq)] +#[cbor(transparent)] +pub struct PlutusScript(#[n(0)] ByteVec); /* big_int = int / big_uint / big_nint ; New @@ -1058,8 +1060,8 @@ impl minicbor::encode::Encode for PlutusData { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Constr { pub tag: u64, - pub prefix: Option, - pub values: MaybeIndefArray, + pub any_constructor: Option, + pub fields: MaybeIndefArray, } impl<'b, A> minicbor::decode::Decode<'b> for Constr @@ -1073,17 +1075,16 @@ where Tag::Unassigned(x) => match x { 121..=127 | 1280..=1400 => Ok(Constr { tag: x, - values: d.decode()?, - prefix: None, + fields: d.decode()?, + any_constructor: None, }), 102 => { d.array()?; - let prefix = Some(d.decode()?); - let values = d.decode()?; + Ok(Constr { - tag: 102, - prefix, - values, + tag: x, + any_constructor: Some(d.decode()?), + fields: d.decode()?, }) } _ => Err(minicbor::decode::Error::message( @@ -1110,13 +1111,13 @@ where match self.tag { 102 => { e.array(2)?; - e.encode(self.prefix)?; - e.encode(&self.values)?; + e.encode(self.any_constructor.unwrap_or_default())?; + e.encode(&self.fields)?; Ok(()) } _ => { - e.encode(&self.values)?; + e.encode(&self.fields)?; Ok(()) }