Skip to content

Commit

Permalink
Merge branch 'master' into cip30-multiasset-largest-first
Browse files Browse the repository at this point in the history
  • Loading branch information
vsubhuman committed Jan 23, 2022
2 parents 03609e1 + b0930b2 commit c7ecdd2
Showing 1 changed file with 98 additions and 31 deletions.
129 changes: 98 additions & 31 deletions rust/src/plutus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,10 @@ impl PlutusMap {
}

pub fn keys(&self) -> PlutusList {
PlutusList(self.0.iter().map(|(k, _v)| k.clone()).collect::<Vec<_>>())
PlutusList {
elems: self.0.iter().map(|(k, _v)| k.clone()).collect::<Vec<_>>(),
definite_encoding: None,
}
}
}

Expand All @@ -359,34 +362,54 @@ enum PlutusDataEnum {

#[wasm_bindgen]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct PlutusData(PlutusDataEnum);
pub struct PlutusData {
datum: PlutusDataEnum,
// We should always preserve the original datums when deserialized as this is NOT canonicized
// before computing datum hashes. So this field stores the original bytes to re-use.
original_bytes: Option<Vec<u8>>,
}

to_from_bytes!(PlutusData);

#[wasm_bindgen]
impl PlutusData {
pub fn new_constr_plutus_data(constr_plutus_data: &ConstrPlutusData) -> Self {
Self(PlutusDataEnum::ConstrPlutusData(constr_plutus_data.clone()))
Self {
datum: PlutusDataEnum::ConstrPlutusData(constr_plutus_data.clone()),
original_bytes: None,
}
}

pub fn new_map(map: &PlutusMap) -> Self {
Self(PlutusDataEnum::Map(map.clone()))
Self {
datum: PlutusDataEnum::Map(map.clone()),
original_bytes: None,
}
}

pub fn new_list(list: &PlutusList) -> Self {
Self(PlutusDataEnum::List(list.clone()))
Self {
datum: PlutusDataEnum::List(list.clone()),
original_bytes: None,
}
}

pub fn new_integer(integer: &BigInt) -> Self {
Self(PlutusDataEnum::Integer(integer.clone()))
Self {
datum: PlutusDataEnum::Integer(integer.clone()),
original_bytes: None,
}
}

pub fn new_bytes(bytes: Vec<u8>) -> Self {
Self(PlutusDataEnum::Bytes(bytes))
Self {
datum: PlutusDataEnum::Bytes(bytes),
original_bytes: None,
}
}

pub fn kind(&self) -> PlutusDataKind {
match &self.0 {
match &self.datum {
PlutusDataEnum::ConstrPlutusData(_) => PlutusDataKind::ConstrPlutusData,
PlutusDataEnum::Map(_) => PlutusDataKind::Map,
PlutusDataEnum::List(_) => PlutusDataKind::List,
Expand All @@ -396,35 +419,35 @@ impl PlutusData {
}

pub fn as_constr_plutus_data(&self) -> Option<ConstrPlutusData> {
match &self.0 {
match &self.datum {
PlutusDataEnum::ConstrPlutusData(x) => Some(x.clone()),
_ => None,
}
}

pub fn as_map(&self) -> Option<PlutusMap> {
match &self.0 {
match &self.datum {
PlutusDataEnum::Map(x) => Some(x.clone()),
_ => None,
}
}

pub fn as_list(&self) -> Option<PlutusList> {
match &self.0 {
match &self.datum {
PlutusDataEnum::List(x) => Some(x.clone()),
_ => None,
}
}

pub fn as_integer(&self) -> Option<BigInt> {
match &self.0 {
match &self.datum {
PlutusDataEnum::Integer(x) => Some(x.clone()),
_ => None,
}
}

pub fn as_bytes(&self) -> Option<Vec<u8>> {
match &self.0 {
match &self.datum {
PlutusDataEnum::Bytes(x) => Some(x.clone()),
_ => None,
}
Expand All @@ -433,26 +456,36 @@ impl PlutusData {

#[wasm_bindgen]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct PlutusList(Vec<PlutusData>);
pub struct PlutusList {
elems: Vec<PlutusData>,
// We should always preserve the original datums when deserialized as this is NOT canonicized
// before computing datum hashes. This field will default to cardano-cli behavior if None
// and will re-use the provided one if deserialized, unless the list is modified.
definite_encoding: Option<bool>,
}

to_from_bytes!(PlutusList);

#[wasm_bindgen]
impl PlutusList {
pub fn new() -> Self {
Self(Vec::new())
Self {
elems: Vec::new(),
definite_encoding: None,
}
}

pub fn len(&self) -> usize {
self.0.len()
self.elems.len()
}

pub fn get(&self, index: usize) -> PlutusData {
self.0[index].clone()
self.elems[index].clone()
}

pub fn add(&mut self, elem: &PlutusData) {
self.0.push(elem.clone());
self.elems.push(elem.clone());
self.definite_encoding = None;
}
}

Expand Down Expand Up @@ -985,34 +1018,56 @@ impl Deserialize for PlutusDataEnum {

impl cbor_event::se::Serialize for PlutusData {
fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer<W>) -> cbor_event::Result<&'se mut Serializer<W>> {
self.0.serialize(serializer)
match &self.original_bytes {
Some(bytes) => serializer.write_raw_bytes(bytes),
None => self.datum.serialize(serializer),
}
}
}

impl Deserialize for PlutusData {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
Ok(Self(PlutusDataEnum::deserialize(raw)?))
// these unwraps are fine since we're seeking the current position
let before = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap();
let datum = PlutusDataEnum::deserialize(raw)?;
let after = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap();
let bytes_read = (after - before) as usize;
raw.as_mut_ref().seek(SeekFrom::Start(before)).unwrap();
// these unwraps are fine since we read the above already
let original_bytes = raw.as_mut_ref().fill_buf().unwrap()[..bytes_read].to_vec();
raw.as_mut_ref().consume(bytes_read);
Ok(Self {
datum,
original_bytes: Some(original_bytes),
})
}
}

impl cbor_event::se::Serialize for PlutusList {
fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer<W>) -> cbor_event::Result<&'se mut Serializer<W>> {
if self.0.len() == 0 {
return Ok(serializer.write_array(cbor_event::Len::Len(0))?);
let use_definite_encoding = match self.definite_encoding {
Some(definite) => definite,
None => self.elems.is_empty(),
};
if use_definite_encoding {
serializer.write_array(cbor_event::Len::Len(self.elems.len() as u64))?;
} else {
serializer.write_array(cbor_event::Len::Indefinite)?;
}
serializer.write_array(cbor_event::Len::Indefinite)?;
for element in &self.0 {
for element in &self.elems {
element.serialize(serializer)?;
}
serializer.write_special(cbor_event::Special::Break)?;
if !use_definite_encoding {
serializer.write_special(cbor_event::Special::Break)?;
}
Ok(serializer)
}
}

impl Deserialize for PlutusList {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
let mut arr = Vec::new();
(|| -> Result<_, DeserializeError> {
let len = (|| -> Result<_, DeserializeError> {
let len = raw.array()?;
while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } {
if raw.cbor_type()? == CBORType::Special {
Expand All @@ -1021,9 +1076,12 @@ impl Deserialize for PlutusList {
}
arr.push(PlutusData::deserialize(raw)?);
}
Ok(())
Ok(len)
})().map_err(|e| e.annotate("PlutusList"))?;
Ok(Self(arr))
Ok(Self {
elems: arr,
definite_encoding: Some(len != cbor_event::Len::Indefinite),
})
}
}

Expand Down Expand Up @@ -1187,12 +1245,13 @@ mod tests {
let constr_0_hash = hex::encode(hash_plutus_data(&constr_0).to_bytes());
assert_eq!(constr_0_hash, "923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec");
let constr_0_roundtrip = PlutusData::from_bytes(constr_0.to_bytes()).unwrap();
assert_eq!(constr_0, constr_0_roundtrip);
// TODO: do we want semantic equality or bytewise equality?
//assert_eq!(constr_0, constr_0_roundtrip);
let constr_1854 = PlutusData::new_constr_plutus_data(
&ConstrPlutusData::new(&to_bignum(1854), &PlutusList::new())
);
let constr_1854_roundtrip = PlutusData::from_bytes(constr_1854.to_bytes()).unwrap();
assert_eq!(constr_1854, constr_1854_roundtrip);
//assert_eq!(constr_1854, constr_1854_roundtrip);
}

#[test]
Expand All @@ -1203,7 +1262,7 @@ mod tests {
assert_eq!(datum_cli, hex::encode(datum.to_bytes()));

// encode empty arrays as fixed
assert_eq!("80", hex::encode(PlutusList::from_bytes(Vec::from_hex("9fff").unwrap()).unwrap().to_bytes()));
assert_eq!("80", hex::encode(PlutusList::new().to_bytes()));

// encode arrays as indefinite length array
let mut list = PlutusList::new();
Expand All @@ -1220,4 +1279,12 @@ mod tests {
witness_set.set_plutus_data(&list);
assert_eq!(format!("a10481{}", datum_cli), hex::encode(witness_set.to_bytes()));
}

#[test]
pub fn plutus_datums_respect_deserialized_encoding() {
let orig_bytes = Vec::from_hex("81d8799f581ce1cbb80db89e292269aeb93ec15eb963dda5176b66949fe1c2a6a38da140a1401864ff").unwrap();
let datums = PlutusList::from_bytes(orig_bytes.clone()).unwrap();
let new_bytes = datums.to_bytes();
assert_eq!(orig_bytes, new_bytes);
}
}

0 comments on commit c7ecdd2

Please sign in to comment.