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 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<const N: usize> {}
@ -616,22 +616,21 @@ pub enum NetworkId {
#[derive(Debug, PartialEq)]
pub enum TransactionBodyComponent {
Inputs(Vec<TransactionInput>),
Outputs(Vec<TransactionOutput>),
Inputs(MaybeIndefArray<TransactionInput>),
Outputs(MaybeIndefArray<TransactionOutput>),
Fee(u64),
Ttl(Option<u64>),
Certificates(Option<Vec<Certificate>>),
Withdrawals(Option<BTreeMap<RewardAccount, Coin>>),
Ttl(u64),
Certificates(Vec<Certificate>),
Withdrawals(KeyValuePairs<RewardAccount, Coin>),
Update(Option<SkipCbor<22>>),
AuxiliaryDataHash(Option<ByteVec>),
ValidityIntervalStart(Option<u64>),
Mint(Option<Multiasset<i64>>),
ScriptDataHash(Option<Hash32>),
Collateral(Option<Vec<TransactionInput>>),
RequiredSigners(Option<Vec<AddrKeyhash>>),
NetworkId(Option<NetworkId>),
AuxiliaryDataHash(ByteVec),
ValidityIntervalStart(u64),
Mint(Multiasset<i64>),
ScriptDataHash(Hash32),
Collateral(MaybeIndefArray<TransactionInput>),
RequiredSigners(MaybeIndefArray<AddrKeyhash>),
NetworkId(NetworkId),
}
impl<'b> minicbor::decode::Decode<'b> for TransactionBodyComponent {
fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> {
let key: u32 = d.decode()?;
@ -731,6 +730,14 @@ impl minicbor::encode::Encode for TransactionBodyComponent {
#[derive(Debug, PartialEq)]
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 {
fn decode(d: &mut minicbor::Decoder<'b>) -> Result<Self, minicbor::decode::Error> {
let len = d.map()?.unwrap_or_default();
@ -767,9 +774,9 @@ pub struct VKeyWitness {
#[derive(Debug, PartialEq)]
pub enum NativeScript {
ScriptPubkey(AddrKeyhash),
ScriptAll(Vec<NativeScript>),
ScriptAny(Vec<NativeScript>),
ScriptNOfK(u32, Vec<NativeScript>),
ScriptAll(MaybeIndefArray<NativeScript>),
ScriptAny(MaybeIndefArray<NativeScript>),
ScriptNOfK(u32, MaybeIndefArray<NativeScript>),
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<PlutusData>),
Map(BTreeMap<PlutusData, PlutusData>),
Map(KeyValuePairs<PlutusData, PlutusData>),
BigInt(BigInt),
BoundedBytes(ByteVec),
Array(Vec<PlutusData>),
ArrayIndef(IndefVec<PlutusData>),
Array(MaybeIndefArray<PlutusData>),
ArrayIndef(MaybeIndefArray<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::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<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)]
pub struct Constr<A> {
pub tag: u64,
pub prefix: Option<u32>,
pub values: IndefVec<A>,
pub values: MaybeIndefArray<A>,
}
impl<'b, A> minicbor::decode::Decode<'b> for Constr<A>
@ -1146,22 +1117,22 @@ pub struct BootstrapWitness {
#[cbor(map)]
pub struct TransactionWitnessSet {
#[n(0)]
pub vkeywitness: Option<Vec<VKeyWitness>>,
pub vkeywitness: Option<MaybeIndefArray<VKeyWitness>>,
#[n(1)]
pub native_script: Option<Vec<NativeScript>>,
pub native_script: Option<MaybeIndefArray<NativeScript>>,
#[n(2)]
pub bootstrap_witness: Option<Vec<BootstrapWitness>>,
pub bootstrap_witness: Option<MaybeIndefArray<BootstrapWitness>>,
#[n(3)]
pub plutus_script: Option<Vec<PlutusScript>>,
pub plutus_script: Option<MaybeIndefArray<PlutusScript>>,
#[n(4)]
pub plutus_data: Option<Vec<PlutusData>>,
pub plutus_data: Option<MaybeIndefArray<PlutusData>>,
#[n(5)]
pub redeemer: Option<Vec<Redeemer>>,
pub redeemer: Option<MaybeIndefArray<Redeemer>>,
}
#[derive(Encode, Decode, Debug, PartialEq)]
@ -1170,7 +1141,7 @@ pub struct AlonzoAuxiliaryData {
#[n(0)]
pub metadata: Option<Metadata>,
#[n(1)]
pub native_scripts: Option<Vec<NativeScript>>,
pub native_scripts: Option<MaybeIndefArray<NativeScript>>,
#[n(2)]
pub plutus_scripts: Option<PlutusScript>,
}
@ -1180,7 +1151,7 @@ pub enum Metadatum {
Int(i64),
Bytes(ByteVec),
Text(String),
Array(Vec<Metadatum>),
Array(MaybeIndefArray<Metadatum>),
Map(Metadata),
}
@ -1329,16 +1300,16 @@ pub struct Block {
pub header: Header,
#[n(1)]
pub transaction_bodies: Vec<TransactionBody>,
pub transaction_bodies: MaybeIndefArray<TransactionBody>,
#[n(2)]
pub transaction_witness_sets: Vec<TransactionWitnessSet>,
pub transaction_witness_sets: MaybeIndefArray<TransactionWitnessSet>,
#[n(3)]
pub auxiliary_data_set: BTreeMap<TransactionIndex, AuxiliaryData>,
pub auxiliary_data_set: KeyValuePairs<TransactionIndex, AuxiliaryData>,
#[n(4)]
pub invalid_transactions: Vec<TransactionIndex>,
pub invalid_transactions: MaybeIndefArray<TransactionIndex>,
}
#[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);
}
}

View file

@ -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<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> {
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<Self, minicbor::decode::Error> {
let datatype = d.datatype()?;
let items: Result<Vec<_>, _> = d.map_iter::<K, V>()?.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<W>,
) -> Result<(), minicbor::encode::Error<W::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<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(())
}
}