Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement Plutus Data hashing / JSON #100

Merged
merged 3 commits into from
Apr 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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