Skip to content

Commit

Permalink
PlutusData::to_cardano_node_format() (#357)
Browse files Browse the repository at this point in the history
Returns the same datum but with the encoding details changed so that it
will match cardano-node/CSL/Lucid

This is only in cases where dealing in raw bytes and/or
`from_cbor_bytes()` is somehow not possible, as that solution is 100%
fool-proof with any tool always for calculating hashes.

Fixes #356
  • Loading branch information
rooooooooob authored Sep 4, 2024
1 parent 964cb87 commit 7a6fc38
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 1 deletion.
75 changes: 75 additions & 0 deletions chain/rust/src/plutus/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::json::plutus_datums::{
decode_plutus_datum_to_json_value, encode_json_value_to_plutus_datum,
CardanoNodePlutusDatumSchema,
};
use crate::utils::BigInteger;
use cbor_event::de::Deserializer;
use cbor_event::se::Serializer;
use cml_core::error::*;
Expand Down Expand Up @@ -64,6 +65,80 @@ impl schemars::JsonSchema for PlutusData {
}
}

impl PlutusData {
/**
* Convert to a Datum that will serialize equivalent to cardano-node's format
*
* Please VERY STRONGLY consider using PlutusData::from_cbor_bytes() instead wherever possible.
* You should try to never rely on a tool encoding CBOR a certain way as there are many possible,
* and just because it matches with a specific datum, doesn't mean that a different datum won't differ.
* This is critical as that means the datum hash won't match.
* After creation a datum (or other hashable CBOR object) should only be treated as raw CBOR bytes,
* or through a type that respects its specific CBOR format e.g. CML's PlutusData::from_cbor_bytes()
*
* This function is just here in case there's no possible way at all to create from CBOR bytes and
* thus cold only be constructed manually and then had this function called on it.
*
* This is also the format that CSL and Lucid use
*/
pub fn to_cardano_node_format(&self) -> Self {
match self {
Self::ConstrPlutusData(c) => Self::ConstrPlutusData(ConstrPlutusData {
alternative: c.alternative,
fields: c
.fields
.iter()
.map(|datum| datum.to_cardano_node_format())
.collect(),
encodings: Some(ConstrPlutusDataEncoding {
len_encoding: LenEncoding::Indefinite,
tag_encoding: None,
alternative_encoding: None,
fields_encoding: if c.fields.is_empty() {
LenEncoding::Canonical
} else {
LenEncoding::Indefinite
},
prefer_compact: true,
}),
}),
Self::Bytes { bytes, .. } => Self::Bytes {
bytes: bytes.clone(),
bytes_encoding: StringEncoding::Canonical,
},
// canonical
Self::Integer(bigint) => Self::Integer(BigInteger::from(bigint.num.clone())),
Self::List { list, .. } => Self::List {
list: list
.iter()
.map(|datum| datum.to_cardano_node_format())
.collect(),
list_encoding: if list.is_empty() {
LenEncoding::Canonical
} else {
LenEncoding::Indefinite
},
},
Self::Map(map) => Self::Map(PlutusMap {
entries: {
let mut sorted_entries: Vec<_> = map
.entries
.iter()
.map(|(k, v)| (k.to_cardano_node_format(), v.to_cardano_node_format()))
.collect();
sorted_entries.sort_by(|(a, _), (b, _)| a.cmp(b));
sorted_entries
},
encoding: if map.entries.is_empty() {
LenEncoding::Canonical
} else {
LenEncoding::Indefinite
},
}),
}
}
}

#[derive(
Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema, derivative::Derivative,
)]
Expand Down
2 changes: 1 addition & 1 deletion chain/rust/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ enum BigIntEncoding {
#[derive(Clone, Debug, Derivative)]
#[derivative(Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct BigInteger {
num: num_bigint::BigInt,
pub(crate) num: num_bigint::BigInt,
#[derivative(
PartialEq = "ignore",
Ord = "ignore",
Expand Down
22 changes: 22 additions & 0 deletions chain/wasm/src/plutus/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,28 @@ impl ConstrPlutusData {
}
}

#[wasm_bindgen]
impl PlutusData {
/**
* Convert to a Datum that will serialize equivalent to cardano-node's format
*
* Please VERY STRONGLY consider using PlutusData::from_cbor_bytes() instead wherever possible.
* You should try to never rely on a tool encoding CBOR a certain way as there are many possible,
* and just because it matches with a specific datum, doesn't mean that a different datum won't differ.
* This is critical as that means the datum hash won't match.
* After creation a datum (or other hashable CBOR object) should only be treated as raw CBOR bytes,
* or through a type that respects its specific CBOR format e.g. CML's PlutusData::from_cbor_bytes()
*
* This function is just here in case there's no possible way at all to create from CBOR bytes and
* thus cold only be constructed manually and then had this function called on it.
*
* This is also the format that CSL and Lucid use
*/
pub fn to_cardano_node_format(&self) -> Self {
self.0.to_cardano_node_format().into()
}
}

#[derive(Clone, Debug)]
#[wasm_bindgen]
pub struct PlutusMap(cml_chain::plutus::PlutusMap);
Expand Down

0 comments on commit 7a6fc38

Please sign in to comment.