Skip to content

Commit

Permalink
feat: Implement Plutus Data hashing / JSON (#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
scarmuega authored Apr 29, 2022
1 parent 0946c83 commit 2c41c17
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 19 deletions.
6 changes: 6 additions & 0 deletions pallas-codec/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,12 @@ impl<T> Deref for CborWrap<T> {
#[derive(Debug)]
pub struct TagWrap<I, const T: u64>(I);

impl<I, const T: u64> TagWrap<I, T> {
pub fn new(inner: I) -> Self {
TagWrap(inner)
}
}

impl<'b, I, const T: u64> minicbor::Decode<'b> for TagWrap<I, T>
where
I: minicbor::Decode<'b>,
Expand Down
54 changes: 51 additions & 3 deletions pallas-primitives/src/alonzo/crypto.rs
Original file line number Diff line number Diff line change
@@ -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> {
Expand All @@ -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)
Expand All @@ -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]
Expand Down Expand Up @@ -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(
Expand All @@ -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::<PlutusData> {
tag: 1280,
any_constructor: None,
fields: MaybeIndefArray::Indef(vec![
PlutusData::BigInt(BigInt::Int(Int::from(4))),
PlutusData::Constr(Constr::<PlutusData> {
tag: 124,
any_constructor: None,
fields: MaybeIndefArray::Indef(vec![
PlutusData::BigInt(BigInt::Int(Int::from(-4))),
PlutusData::Constr(Constr::<PlutusData> {
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()
)
}
}
16 changes: 13 additions & 3 deletions pallas-primitives/src/alonzo/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@ use serde_json::json;

use crate::ToCanonicalJson;

impl<A> super::Constr<A> {
pub fn constructor_value(&self) -> Option<u64> {
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
Expand Down
27 changes: 14 additions & 13 deletions pallas-primitives/src/alonzo/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1058,8 +1060,8 @@ impl minicbor::encode::Encode for PlutusData {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Constr<A> {
pub tag: u64,
pub prefix: Option<u32>,
pub values: MaybeIndefArray<A>,
pub any_constructor: Option<u64>,
pub fields: MaybeIndefArray<A>,
}

impl<'b, A> minicbor::decode::Decode<'b> for Constr<A>
Expand All @@ -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(
Expand All @@ -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(())
}
Expand Down

0 comments on commit 2c41c17

Please sign in to comment.