fix(alonzo): avoid indef arrays isomorphic codec issues

This commit is contained in:
Santiago Carmuega 2021-12-13 08:30:34 -03:00
parent 866deb0211
commit 002ad622dc
2 changed files with 146 additions and 80 deletions

View file

@ -5,9 +5,9 @@
use log::warn; use log::warn;
use minicbor::{bytes::ByteVec, data::Tag}; use minicbor::{bytes::ByteVec, data::Tag};
use minicbor_derive::{Decode, Encode}; 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)] #[derive(Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct SkipCbor<const N: usize> {} pub struct SkipCbor<const N: usize> {}
@ -616,22 +616,21 @@ pub enum NetworkId {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum TransactionBodyComponent { pub enum TransactionBodyComponent {
Inputs(Vec<TransactionInput>), Inputs(MaybeIndefArray<TransactionInput>),
Outputs(Vec<TransactionOutput>), Outputs(MaybeIndefArray<TransactionOutput>),
Fee(u64), Fee(u64),
Ttl(Option<u64>), Ttl(u64),
Certificates(Option<Vec<Certificate>>), Certificates(Vec<Certificate>),
Withdrawals(Option<BTreeMap<RewardAccount, Coin>>), Withdrawals(KeyValuePairs<RewardAccount, Coin>),
Update(Option<SkipCbor<22>>), Update(Option<SkipCbor<22>>),
AuxiliaryDataHash(Option<ByteVec>), AuxiliaryDataHash(ByteVec),
ValidityIntervalStart(Option<u64>), ValidityIntervalStart(u64),
Mint(Option<Multiasset<i64>>), Mint(Multiasset<i64>),
ScriptDataHash(Option<Hash32>), ScriptDataHash(Hash32),
Collateral(Option<Vec<TransactionInput>>), Collateral(MaybeIndefArray<TransactionInput>),
RequiredSigners(Option<Vec<AddrKeyhash>>), RequiredSigners(MaybeIndefArray<AddrKeyhash>),
NetworkId(Option<NetworkId>), NetworkId(NetworkId),
} }
impl<'b> minicbor::decode::Decode<'b> for TransactionBodyComponent { impl<'b> minicbor::decode::Decode<'b> for TransactionBodyComponent {
fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> { fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> {
let key: u32 = d.decode()?; let key: u32 = d.decode()?;
@ -731,6 +730,14 @@ impl minicbor::encode::Encode for TransactionBodyComponent {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct TransactionBody(Vec<TransactionBodyComponent>); pub struct TransactionBody(Vec<TransactionBodyComponent>);
impl Deref for TransactionBody {
type Target = Vec<TransactionBodyComponent>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'b> minicbor::decode::Decode<'b> for TransactionBody { impl<'b> minicbor::decode::Decode<'b> for TransactionBody {
fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> { fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> {
let len = d.map()?.unwrap_or_default(); let len = d.map()?.unwrap_or_default();
@ -767,9 +774,9 @@ pub struct VKeyWitness {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum NativeScript { pub enum NativeScript {
ScriptPubkey(AddrKeyhash), ScriptPubkey(AddrKeyhash),
ScriptAll(Vec<NativeScript>), ScriptAll(MaybeIndefArray<NativeScript>),
ScriptAny(Vec<NativeScript>), ScriptAny(MaybeIndefArray<NativeScript>),
ScriptNOfK(u32, Vec<NativeScript>), ScriptNOfK(u32, MaybeIndefArray<NativeScript>),
InvalidBefore(u64), InvalidBefore(u64),
InvalidHereafter(u64), InvalidHereafter(u64),
} }
@ -904,11 +911,11 @@ impl minicbor::encode::Encode for BigInt {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum PlutusData { pub enum PlutusData {
Constr(Constr<PlutusData>), Constr(Constr<PlutusData>),
Map(BTreeMap<PlutusData, PlutusData>), Map(KeyValuePairs<PlutusData, PlutusData>),
BigInt(BigInt), BigInt(BigInt),
BoundedBytes(ByteVec), BoundedBytes(ByteVec),
Array(Vec<PlutusData>), Array(MaybeIndefArray<PlutusData>),
ArrayIndef(IndefVec<PlutusData>), ArrayIndef(MaybeIndefArray<PlutusData>),
} }
impl<'b> minicbor::decode::Decode<'b> for PlutusData { 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::I64 => Ok(Self::BigInt(d.decode()?)),
minicbor::data::Type::Map => Ok(Self::Map(d.decode()?)), minicbor::data::Type::Map => Ok(Self::Map(d.decode()?)),
minicbor::data::Type::Bytes => Ok(Self::BoundedBytes(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::Array => Ok(Self::Array(d.decode()?)),
minicbor::data::Type::ArrayIndef => Ok(Self::ArrayIndef(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<A>(pub Vec<A>);
impl<'b, A> minicbor::decode::Decode<'b> for IndefVec<A>
where
A: minicbor::decode::Decode<'b>,
{
fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> {
let values: Vec<A> = d.decode()?;
Ok(IndefVec(values))
}
}
impl<A> minicbor::encode::Encode for IndefVec<A>
where
A: minicbor::encode::Encode,
{
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
) -> Result<(), minicbor::encode::Error<W::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)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Constr<A> { pub struct Constr<A> {
pub tag: u64, pub tag: u64,
pub prefix: Option<u32>, pub prefix: Option<u32>,
pub values: IndefVec<A>, pub values: MaybeIndefArray<A>,
} }
impl<'b, A> minicbor::decode::Decode<'b> for Constr<A> impl<'b, A> minicbor::decode::Decode<'b> for Constr<A>
@ -1146,22 +1117,22 @@ pub struct BootstrapWitness {
#[cbor(map)] #[cbor(map)]
pub struct TransactionWitnessSet { pub struct TransactionWitnessSet {
#[n(0)] #[n(0)]
pub vkeywitness: Option<Vec<VKeyWitness>>, pub vkeywitness: Option<MaybeIndefArray<VKeyWitness>>,
#[n(1)] #[n(1)]
pub native_script: Option<Vec<NativeScript>>, pub native_script: Option<MaybeIndefArray<NativeScript>>,
#[n(2)] #[n(2)]
pub bootstrap_witness: Option<Vec<BootstrapWitness>>, pub bootstrap_witness: Option<MaybeIndefArray<BootstrapWitness>>,
#[n(3)] #[n(3)]
pub plutus_script: Option<Vec<PlutusScript>>, pub plutus_script: Option<MaybeIndefArray<PlutusScript>>,
#[n(4)] #[n(4)]
pub plutus_data: Option<Vec<PlutusData>>, pub plutus_data: Option<MaybeIndefArray<PlutusData>>,
#[n(5)] #[n(5)]
pub redeemer: Option<Vec<Redeemer>>, pub redeemer: Option<MaybeIndefArray<Redeemer>>,
} }
#[derive(Encode, Decode, Debug, PartialEq)] #[derive(Encode, Decode, Debug, PartialEq)]
@ -1170,7 +1141,7 @@ pub struct AlonzoAuxiliaryData {
#[n(0)] #[n(0)]
pub metadata: Option<Metadata>, pub metadata: Option<Metadata>,
#[n(1)] #[n(1)]
pub native_scripts: Option<Vec<NativeScript>>, pub native_scripts: Option<MaybeIndefArray<NativeScript>>,
#[n(2)] #[n(2)]
pub plutus_scripts: Option<PlutusScript>, pub plutus_scripts: Option<PlutusScript>,
} }
@ -1180,7 +1151,7 @@ pub enum Metadatum {
Int(i64), Int(i64),
Bytes(ByteVec), Bytes(ByteVec),
Text(String), Text(String),
Array(Vec<Metadatum>), Array(MaybeIndefArray<Metadatum>),
Map(Metadata), Map(Metadata),
} }
@ -1329,16 +1300,16 @@ pub struct Block {
pub header: Header, pub header: Header,
#[n(1)] #[n(1)]
pub transaction_bodies: Vec<TransactionBody>, pub transaction_bodies: MaybeIndefArray<TransactionBody>,
#[n(2)] #[n(2)]
pub transaction_witness_sets: Vec<TransactionWitnessSet>, pub transaction_witness_sets: MaybeIndefArray<TransactionWitnessSet>,
#[n(3)] #[n(3)]
pub auxiliary_data_set: BTreeMap<TransactionIndex, AuxiliaryData>, pub auxiliary_data_set: KeyValuePairs<TransactionIndex, AuxiliaryData>,
#[n(4)] #[n(4)]
pub invalid_transactions: Vec<TransactionIndex>, pub invalid_transactions: MaybeIndefArray<TransactionIndex>,
} }
#[derive(Encode, Decode, Debug)] #[derive(Encode, Decode, Debug)]
@ -1360,8 +1331,7 @@ mod tests {
include_str!("test_data/test6.block"), include_str!("test_data/test6.block"),
include_str!("test_data/test7.block"), include_str!("test_data/test7.block"),
include_str!("test_data/test8.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() { for (idx, block_str) in test_blocks.iter().enumerate() {
@ -1371,6 +1341,7 @@ mod tests {
.expect(&format!("error decoding cbor for file {}", idx)); .expect(&format!("error decoding cbor for file {}", idx));
let bytes2 = let bytes2 =
to_vec(block).expect(&format!("error encoding block cbor for file {}", idx)); to_vec(block).expect(&format!("error encoding block cbor for file {}", idx));
assert_eq!(bytes, bytes2); assert_eq!(bytes, bytes2);
} }
} }

View file

@ -9,13 +9,19 @@ use minicbor::{Decode, Encode};
/// as the underlaying struct for storage of the items (as opposed to a BTreeMap /// as the underlaying struct for storage of the items (as opposed to a BTreeMap
/// or HashMap). /// or HashMap).
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct KeyValuePairs<K, V>(Vec<(K, V)>); pub enum KeyValuePairs<K, V> {
Def(Vec<(K, V)>),
Indef(Vec<(K, V)>),
}
impl<K, V> Deref for KeyValuePairs<K, V> { impl<K, V> Deref for KeyValuePairs<K, V> {
type Target = Vec<(K, V)>; type Target = Vec<(K, V)>;
fn deref(&self) -> &Self::Target { 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>, V: Encode + Decode<'b>,
{ {
fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> { fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> {
let datatype = d.datatype()?;
let items: Result<Vec<_>, _> = d.map_iter::<K, V>()?.collect(); let items: Result<Vec<_>, _> = d.map_iter::<K, V>()?.collect();
let items = items?; 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, &self,
e: &mut minicbor::Encoder<W>, e: &mut minicbor::Encoder<W>,
) -> Result<(), minicbor::encode::Error<W::Error>> { ) -> Result<(), minicbor::encode::Error<W::Error>> {
e.map(self.0.len() as u64)?; match self {
for (k, v) in &self.0 { KeyValuePairs::Def(x) => {
k.encode(e)?; e.map(x.len() as u64)?;
v.encode(e)?;
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(()) 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<A> {
Def(Vec<A>),
Indef(Vec<A>),
}
impl<A> Deref for MaybeIndefArray<A> {
type Target = Vec<A>;
fn deref(&self) -> &Self::Target {
match self {
MaybeIndefArray::Def(x) => &x,
MaybeIndefArray::Indef(x) => &x,
}
}
}
impl<'b, A> minicbor::decode::Decode<'b> for MaybeIndefArray<A>
where
A: minicbor::decode::Decode<'b>,
{
fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> {
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<A> minicbor::encode::Encode for MaybeIndefArray<A>
where
A: minicbor::encode::Encode,
{
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
) -> Result<(), minicbor::encode::Error<W::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(())
}
}