From 002ad622dc7c2dedbd832b85d8a1d4da1fcddd93 Mon Sep 17 00:00:00 2001 From: Santiago Carmuega Date: Mon, 13 Dec 2021 08:30:34 -0300 Subject: [PATCH] fix(alonzo): avoid indef arrays isomorphic codec issues --- pallas-alonzo/src/model.rs | 117 ++++++++++++++----------------------- pallas-alonzo/src/utils.rs | 109 +++++++++++++++++++++++++++++++--- 2 files changed, 146 insertions(+), 80 deletions(-) diff --git a/pallas-alonzo/src/model.rs b/pallas-alonzo/src/model.rs index 523ce03..3039334 100644 --- a/pallas-alonzo/src/model.rs +++ b/pallas-alonzo/src/model.rs @@ -5,9 +5,9 @@ use log::warn; use minicbor::{bytes::ByteVec, data::Tag}; use minicbor_derive::{Decode, Encode}; -use std::collections::BTreeMap; +use std::{collections::BTreeMap, ops::Deref}; -use crate::utils::KeyValuePairs; +use crate::utils::{KeyValuePairs, MaybeIndefArray}; #[derive(Debug, PartialEq, PartialOrd, Eq, Ord)] pub struct SkipCbor {} @@ -616,22 +616,21 @@ pub enum NetworkId { #[derive(Debug, PartialEq)] pub enum TransactionBodyComponent { - Inputs(Vec), - Outputs(Vec), + Inputs(MaybeIndefArray), + Outputs(MaybeIndefArray), Fee(u64), - Ttl(Option), - Certificates(Option>), - Withdrawals(Option>), + Ttl(u64), + Certificates(Vec), + Withdrawals(KeyValuePairs), Update(Option>), - AuxiliaryDataHash(Option), - ValidityIntervalStart(Option), - Mint(Option>), - ScriptDataHash(Option), - Collateral(Option>), - RequiredSigners(Option>), - NetworkId(Option), + AuxiliaryDataHash(ByteVec), + ValidityIntervalStart(u64), + Mint(Multiasset), + ScriptDataHash(Hash32), + Collateral(MaybeIndefArray), + RequiredSigners(MaybeIndefArray), + NetworkId(NetworkId), } - impl<'b> minicbor::decode::Decode<'b> for TransactionBodyComponent { fn decode(d: &mut minicbor::Decoder<'b>) -> Result { let key: u32 = d.decode()?; @@ -731,6 +730,14 @@ impl minicbor::encode::Encode for TransactionBodyComponent { #[derive(Debug, PartialEq)] pub struct TransactionBody(Vec); +impl Deref for TransactionBody { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + impl<'b> minicbor::decode::Decode<'b> for TransactionBody { fn decode(d: &mut minicbor::Decoder<'b>) -> Result { let len = d.map()?.unwrap_or_default(); @@ -767,9 +774,9 @@ pub struct VKeyWitness { #[derive(Debug, PartialEq)] pub enum NativeScript { ScriptPubkey(AddrKeyhash), - ScriptAll(Vec), - ScriptAny(Vec), - ScriptNOfK(u32, Vec), + ScriptAll(MaybeIndefArray), + ScriptAny(MaybeIndefArray), + ScriptNOfK(u32, MaybeIndefArray), InvalidBefore(u64), InvalidHereafter(u64), } @@ -904,11 +911,11 @@ impl minicbor::encode::Encode for BigInt { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum PlutusData { Constr(Constr), - Map(BTreeMap), + Map(KeyValuePairs), BigInt(BigInt), BoundedBytes(ByteVec), - Array(Vec), - ArrayIndef(IndefVec), + Array(MaybeIndefArray), + ArrayIndef(MaybeIndefArray), } impl<'b> minicbor::decode::Decode<'b> for PlutusData { @@ -938,6 +945,7 @@ impl<'b> minicbor::decode::Decode<'b> for PlutusData { | minicbor::data::Type::I64 => Ok(Self::BigInt(d.decode()?)), minicbor::data::Type::Map => Ok(Self::Map(d.decode()?)), minicbor::data::Type::Bytes => Ok(Self::BoundedBytes(d.decode()?)), + minicbor::data::Type::BytesIndef => Ok(Self::BoundedBytes(d.decode()?)), minicbor::data::Type::Array => Ok(Self::Array(d.decode()?)), minicbor::data::Type::ArrayIndef => Ok(Self::ArrayIndef(d.decode()?)), @@ -978,48 +986,11 @@ impl minicbor::encode::Encode for PlutusData { } } -/// A struct that forces encode / decode using indef arrays -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct IndefVec(pub Vec); - -impl<'b, A> minicbor::decode::Decode<'b> for IndefVec -where - A: minicbor::decode::Decode<'b>, -{ - fn decode(d: &mut minicbor::Decoder<'b>) -> Result { - let values: Vec = d.decode()?; - - Ok(IndefVec(values)) - } -} - -impl minicbor::encode::Encode for IndefVec -where - A: minicbor::encode::Encode, -{ - fn encode( - &self, - e: &mut minicbor::Encoder, - ) -> Result<(), minicbor::encode::Error> { - if self.0.is_empty() { - e.begin_array()?; - for v in &self.0 { - e.encode(v)?; - } - e.end()?; - } else { - e.array(0)?; - } - - Ok(()) - } -} - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Constr { pub tag: u64, pub prefix: Option, - pub values: IndefVec, + pub values: MaybeIndefArray, } impl<'b, A> minicbor::decode::Decode<'b> for Constr @@ -1146,22 +1117,22 @@ pub struct BootstrapWitness { #[cbor(map)] pub struct TransactionWitnessSet { #[n(0)] - pub vkeywitness: Option>, + pub vkeywitness: Option>, #[n(1)] - pub native_script: Option>, + pub native_script: Option>, #[n(2)] - pub bootstrap_witness: Option>, + pub bootstrap_witness: Option>, #[n(3)] - pub plutus_script: Option>, + pub plutus_script: Option>, #[n(4)] - pub plutus_data: Option>, + pub plutus_data: Option>, #[n(5)] - pub redeemer: Option>, + pub redeemer: Option>, } #[derive(Encode, Decode, Debug, PartialEq)] @@ -1170,7 +1141,7 @@ pub struct AlonzoAuxiliaryData { #[n(0)] pub metadata: Option, #[n(1)] - pub native_scripts: Option>, + pub native_scripts: Option>, #[n(2)] pub plutus_scripts: Option, } @@ -1180,7 +1151,7 @@ pub enum Metadatum { Int(i64), Bytes(ByteVec), Text(String), - Array(Vec), + Array(MaybeIndefArray), Map(Metadata), } @@ -1329,16 +1300,16 @@ pub struct Block { pub header: Header, #[n(1)] - pub transaction_bodies: Vec, + pub transaction_bodies: MaybeIndefArray, #[n(2)] - pub transaction_witness_sets: Vec, + pub transaction_witness_sets: MaybeIndefArray, #[n(3)] - pub auxiliary_data_set: BTreeMap, + pub auxiliary_data_set: KeyValuePairs, #[n(4)] - pub invalid_transactions: Vec, + pub invalid_transactions: MaybeIndefArray, } #[derive(Encode, Decode, Debug)] @@ -1360,8 +1331,7 @@ mod tests { include_str!("test_data/test6.block"), include_str!("test_data/test7.block"), include_str!("test_data/test8.block"), - // indef arrays giving trouble, re-encoding doesn't match - //include_str!("test_data/test9.block"), + include_str!("test_data/test9.block"), ]; for (idx, block_str) in test_blocks.iter().enumerate() { @@ -1371,6 +1341,7 @@ mod tests { .expect(&format!("error decoding cbor for file {}", idx)); let bytes2 = to_vec(block).expect(&format!("error encoding block cbor for file {}", idx)); + assert_eq!(bytes, bytes2); } } diff --git a/pallas-alonzo/src/utils.rs b/pallas-alonzo/src/utils.rs index 1011e8a..abfc38f 100644 --- a/pallas-alonzo/src/utils.rs +++ b/pallas-alonzo/src/utils.rs @@ -9,13 +9,19 @@ use minicbor::{Decode, Encode}; /// as the underlaying struct for storage of the items (as opposed to a BTreeMap /// or HashMap). #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct KeyValuePairs(Vec<(K, V)>); +pub enum KeyValuePairs { + Def(Vec<(K, V)>), + Indef(Vec<(K, V)>), +} impl Deref for KeyValuePairs { type Target = Vec<(K, V)>; fn deref(&self) -> &Self::Target { - &self.0 + match self { + KeyValuePairs::Def(x) => x, + KeyValuePairs::Indef(x) => x, + } } } @@ -25,9 +31,18 @@ where V: Encode + Decode<'b>, { fn decode(d: &mut minicbor::Decoder<'b>) -> Result { + let datatype = d.datatype()?; + let items: Result, _> = d.map_iter::()?.collect(); let items = items?; - Ok(KeyValuePairs(items)) + + match datatype { + minicbor::data::Type::Map => Ok(KeyValuePairs::Def(items)), + minicbor::data::Type::MapIndef => Ok(KeyValuePairs::Indef(items)), + _ => Err(minicbor::decode::Error::Message( + "invalid data type for keyvaluepairs", + )), + } } } @@ -40,12 +55,92 @@ where &self, e: &mut minicbor::Encoder, ) -> Result<(), minicbor::encode::Error> { - e.map(self.0.len() as u64)?; - for (k, v) in &self.0 { - k.encode(e)?; - v.encode(e)?; + match self { + KeyValuePairs::Def(x) => { + e.map(x.len() as u64)?; + + for (k, v) in x.iter() { + k.encode(e)?; + v.encode(e)?; + } + } + KeyValuePairs::Indef(x) => { + e.begin_map()?; + + for (k, v) in x.iter() { + k.encode(e)?; + v.encode(e)?; + } + + e.end()?; + } } Ok(()) } } + +/// A struct that maintains a reference to whether a cbor array was indef or not +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum MaybeIndefArray { + Def(Vec), + Indef(Vec), +} + +impl Deref for MaybeIndefArray { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + match self { + MaybeIndefArray::Def(x) => &x, + MaybeIndefArray::Indef(x) => &x, + } + } +} + +impl<'b, A> minicbor::decode::Decode<'b> for MaybeIndefArray +where + A: minicbor::decode::Decode<'b>, +{ + fn decode(d: &mut minicbor::Decoder<'b>) -> Result { + let datatype = d.datatype()?; + + match datatype { + minicbor::data::Type::Array => Ok(Self::Def(d.decode()?)), + minicbor::data::Type::ArrayIndef => Ok(Self::Indef(d.decode()?)), + _ => Err(minicbor::decode::Error::Message( + "unknown data type of maybe indef array", + )), + } + } +} + +impl minicbor::encode::Encode for MaybeIndefArray +where + A: minicbor::encode::Encode, +{ + fn encode( + &self, + e: &mut minicbor::Encoder, + ) -> Result<(), minicbor::encode::Error> { + match self { + MaybeIndefArray::Def(x) => { + e.encode(x)?; + } + MaybeIndefArray::Indef(x) if x.is_empty() => { + e.encode(x)?; + } + MaybeIndefArray::Indef(x) => { + e.begin_array()?; + + for v in x.iter() { + e.encode(v)?; + } + + e.end()?; + } + }; + + Ok(()) + } +}