Skip to content

Commit

Permalink
feat(primitives): Introduce MintedBlock concept (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
scarmuega authored Jun 12, 2022
1 parent 4df94f9 commit fe80ff7
Show file tree
Hide file tree
Showing 48 changed files with 208 additions and 182 deletions.
25 changes: 12 additions & 13 deletions examples/block-decode/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
use std::fmt::Debug;

use pallas::ledger::primitives::{alonzo, byron, probing, Era, Fragment};

fn pretty_print(block: impl Debug) {
println!("{:?}", block)
}
use pallas::ledger::primitives::{alonzo, byron, probing, Era};

fn main() {
let blocks = vec![
Expand All @@ -16,16 +10,21 @@ fn main() {
];

for block_str in blocks.iter() {
let bytes = hex::decode(block_str).expect("valid hex");
let bytes = hex::decode(block_str).expect("invalid hex");

match probing::probe_block_cbor_era(&bytes) {
probing::Outcome::Matched(era) => match era {
Era::Byron => pretty_print(byron::Block::decode_fragment(&bytes)),
Era::Byron => {
let (_, block): (u16, byron::MainBlock) =
pallas::codec::minicbor::decode(&bytes).expect("invalid cbor");
println!("{:?}", block)
}
// we use alonzo for everything post-shelly since it's backward compatible
Era::Shelley => pretty_print(alonzo::BlockWrapper::decode_fragment(&bytes)),
Era::Allegra => pretty_print(alonzo::BlockWrapper::decode_fragment(&bytes)),
Era::Mary => pretty_print(alonzo::BlockWrapper::decode_fragment(&bytes)),
Era::Alonzo => pretty_print(alonzo::BlockWrapper::decode_fragment(&bytes)),
Era::Shelley | Era::Allegra | Era::Mary | Era::Alonzo => {
let (_, block): (u16, alonzo::Block) =
pallas::codec::minicbor::decode(&bytes).expect("invalid cbor");
println!("{:?}", block)
}
},
_ => println!("couldn't infer block era"),
};
Expand Down
11 changes: 7 additions & 4 deletions pallas-primitives/src/alonzo/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ impl TransactionOutput {

#[cfg(test)]
mod tests {
use crate::alonzo::{BlockWrapper, TransactionBodyComponent};
use crate::Fragment;
use pallas_codec::minicbor;

use crate::alonzo::{Block, TransactionBodyComponent};

type BlockWrapper = (u16, Block);

const KNOWN_ADDRESSES: &[&str] =&[
"addr_test1vzzql63nddp8qdgka578hx6pats290js9kmn4uay5we9fwsgza0z3",
Expand All @@ -36,10 +39,10 @@ mod tests {
fn known_address_matches() {
// TODO: expand this test to include more test blocks
let block_idx = 1;
let block_str = include_str!("../test_data/alonzo2.block");
let block_str = include_str!("../../../test_data/alonzo2.block");

let block_bytes = hex::decode(block_str).expect(&format!("bad block file {}", block_idx));
let BlockWrapper(_, block) = BlockWrapper::decode_fragment(&block_bytes[..])
let (_, block): BlockWrapper = minicbor::decode(&block_bytes[..])
.expect(&format!("error decoding cbor for file {}", block_idx));

// don't want to pass if we don't have tx in the block
Expand Down
13 changes: 8 additions & 5 deletions pallas-primitives/src/alonzo/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,24 @@ impl ToHash<32> for KeepRaw<'_, TransactionBody> {
mod tests {
use std::str::FromStr;

use pallas_codec::minicbor;
use pallas_codec::minicbor::data::Int;
use pallas_codec::utils::MaybeIndefArray;
use pallas_crypto::hash::Hash;

use crate::alonzo::{BigInt, BlockWrapper, Constr, NativeScript, PlutusData};
use crate::{Fragment, ToHash};
use crate::alonzo::{BigInt, Constr, MintedBlock, NativeScript, PlutusData};
use crate::ToHash;

type BlockWrapper<'b> = (u16, MintedBlock<'b>);

#[test]
fn transaction_hash_works() {
// TODO: expand this test to include more test blocks
let block_idx = 1;
let block_str = include_str!("../test_data/alonzo1.block");
let block_str = include_str!("../../../test_data/alonzo1.block");

let block_bytes = hex::decode(block_str).expect(&format!("bad block file {}", block_idx));
let block_model = BlockWrapper::decode_fragment(&block_bytes[..])
let (_, block_model): BlockWrapper = minicbor::decode(&block_bytes[..])
.expect(&format!("error decoding cbor for file {}", block_idx));

let valid_hashes = vec![
Expand All @@ -75,7 +78,7 @@ mod tests {
"8838f5ab27894a6543255aeaec086f7b3405a6db6e7457a541409cdbbf0cd474",
];

for (tx_idx, tx) in block_model.1.transaction_bodies.iter().enumerate() {
for (tx_idx, tx) in block_model.transaction_bodies.iter().enumerate() {
let computed_hash = tx.to_hash();
let known_hash = valid_hashes[tx_idx];
assert_eq!(hex::encode(computed_hash), known_hash)
Expand Down
18 changes: 11 additions & 7 deletions pallas-primitives/src/alonzo/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,25 @@ impl ToCanonicalJson for super::NativeScript {

#[cfg(test)]
mod tests {
use crate::{alonzo::BlockWrapper, Fragment, ToCanonicalJson};
use pallas_codec::minicbor;

use crate::{alonzo::Block, ToCanonicalJson};

type BlockWrapper = (u16, Block);

#[test]
fn test_datums_serialize_as_expected() {
let test_blocks = vec![(
include_str!("../test_data/alonzo9.block"),
include_str!("../test_data/alonzo9.datums"),
include_str!("../../../test_data/alonzo9.block"),
include_str!("../../../test_data/alonzo9.datums"),
)];

for (idx, (block_str, jsonl_str)) in test_blocks.iter().enumerate() {
println!("decoding json block {}", idx + 1);

let bytes = hex::decode(block_str).expect(&format!("bad block file {}", idx));

let BlockWrapper(_, block) = BlockWrapper::decode_fragment(&bytes[..])
let (_, block): BlockWrapper = minicbor::decode(&bytes[..])
.expect(&format!("error decoding cbor for file {}", idx));

let mut datums = jsonl_str.lines();
Expand All @@ -115,16 +119,16 @@ mod tests {
#[test]
fn test_native_scripts_serialize_as_expected() {
let test_blocks = vec![(
include_str!("../test_data/alonzo9.block"),
include_str!("../test_data/alonzo9.native"),
include_str!("../../../test_data/alonzo9.block"),
include_str!("../../../test_data/alonzo9.native"),
)];

for (idx, (block_str, jsonl_str)) in test_blocks.iter().enumerate() {
println!("decoding json block {}", idx + 1);

let bytes = hex::decode(block_str).expect(&format!("bad block file {}", idx));

let BlockWrapper(_, block) = BlockWrapper::decode_fragment(&bytes[..])
let (_, block): BlockWrapper = minicbor::decode(&bytes[..])
.expect(&format!("error decoding cbor for file {}", idx));

let mut scripts = jsonl_str.lines();
Expand Down
89 changes: 57 additions & 32 deletions pallas-primitives/src/alonzo/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1418,12 +1418,12 @@ impl<C> minicbor::Encode<C> for AuxiliaryData {
pub type TransactionIndex = u32;

#[derive(Encode, Decode, Debug, PartialEq)]
pub struct Block<'b> {
pub struct Block {
#[n(0)]
pub header: Header,

#[b(1)]
pub transaction_bodies: MaybeIndefArray<KeepRaw<'b, TransactionBody>>,
pub transaction_bodies: MaybeIndefArray<TransactionBody>,

#[n(2)]
pub transaction_witness_sets: MaybeIndefArray<TransactionWitnessSet>,
Expand All @@ -1435,82 +1435,107 @@ pub struct Block<'b> {
pub invalid_transactions: Option<MaybeIndefArray<TransactionIndex>>,
}

#[derive(Encode, Decode, Debug)]
pub struct BlockWrapper<'b>(#[n(0)] pub u16, #[b(1)] pub Block<'b>);
/// A memory representation of an already minted block
///
/// This structure is analogous to [Block], but it allows to retrieve the
/// original CBOR bytes for each structure that might require hashing. In this
/// way, we make sure that the resulting hash matches what exists on-chain.
#[derive(Encode, Decode, Debug, PartialEq)]
pub struct MintedBlock<'b> {
#[n(0)]
pub header: KeepRaw<'b, Header>,

#[b(1)]
pub transaction_bodies: MaybeIndefArray<KeepRaw<'b, TransactionBody>>,

#[n(2)]
pub transaction_witness_sets: MaybeIndefArray<TransactionWitnessSet>,

#[n(3)]
pub auxiliary_data_set: KeyValuePairs<TransactionIndex, KeepRaw<'b, AuxiliaryData>>,

#[n(4)]
pub invalid_transactions: Option<MaybeIndefArray<TransactionIndex>>,
}

#[derive(Encode, Decode, Debug)]
pub struct Transaction {
#[n(0)]
transaction_body: TransactionBody,

#[n(1)]
transaction_witness_set: TransactionWitnessSet,

#[n(2)]
success: bool,

#[n(3)]
auxiliary_data: Option<AuxiliaryData>,
}

#[cfg(test)]
mod tests {
use super::BlockWrapper;
use crate::Fragment;
use pallas_codec::minicbor::to_vec;
use pallas_codec::minicbor::{self, to_vec};

use super::MintedBlock;

type BlockWrapper<'b> = (u16, MintedBlock<'b>);

#[test]
fn block_isomorphic_decoding_encoding() {
let test_blocks = vec![
include_str!("../test_data/alonzo1.block"),
include_str!("../test_data/alonzo2.block"),
include_str!("../test_data/alonzo3.block"),
include_str!("../test_data/alonzo4.block"),
include_str!("../test_data/alonzo5.block"),
include_str!("../test_data/alonzo6.block"),
include_str!("../test_data/alonzo7.block"),
include_str!("../test_data/alonzo8.block"),
include_str!("../test_data/alonzo9.block"),
include_str!("../../../test_data/alonzo1.block"),
include_str!("../../../test_data/alonzo2.block"),
include_str!("../../../test_data/alonzo3.block"),
include_str!("../../../test_data/alonzo4.block"),
include_str!("../../../test_data/alonzo5.block"),
include_str!("../../../test_data/alonzo6.block"),
include_str!("../../../test_data/alonzo7.block"),
include_str!("../../../test_data/alonzo8.block"),
include_str!("../../../test_data/alonzo9.block"),
// old block without invalid_transactions fields
include_str!("../test_data/alonzo10.block"),
include_str!("../../../test_data/alonzo10.block"),
// peculiar block with protocol update params
include_str!("../test_data/alonzo11.block"),
include_str!("../../../test_data/alonzo11.block"),
// peculiar block with decoding issue
// https://github.com/txpipe/oura/issues/37
include_str!("../test_data/alonzo12.block"),
include_str!("../../../test_data/alonzo12.block"),
// peculiar block with protocol update params, including nonce
include_str!("../test_data/alonzo13.block"),
include_str!("../../../test_data/alonzo13.block"),
// peculiar block with overflow crash
// https://github.com/txpipe/oura/issues/113
include_str!("../test_data/alonzo14.block"),
include_str!("../../../test_data/alonzo14.block"),
// peculiar block with many move-instantaneous-rewards certs
include_str!("../test_data/alonzo15.block"),
include_str!("../../../test_data/alonzo15.block"),
// peculiar block with protocol update values
include_str!("../test_data/alonzo16.block"),
include_str!("../../../test_data/alonzo16.block"),
// peculiar block with missing nonce hash
include_str!("../test_data/alonzo17.block"),
include_str!("../../../test_data/alonzo17.block"),
// peculiar block with strange AuxiliaryData variant
include_str!("../test_data/alonzo18.block"),
include_str!("../../../test_data/alonzo18.block"),
// peculiar block with strange AuxiliaryData variant
include_str!("../test_data/alonzo18.block"),
include_str!("../../../test_data/alonzo18.block"),
// peculiar block with nevative i64 overflow
include_str!("../test_data/alonzo19.block"),
include_str!("../../../test_data/alonzo19.block"),
// peculiar block with very BigInt in plutus code
include_str!("../test_data/alonzo20.block"),
include_str!("../../../test_data/alonzo20.block"),
// peculiar block with bad tx hash
include_str!("../test_data/alonzo21.block"),
include_str!("../../../test_data/alonzo21.block"),
// peculiar block with bad tx hash
include_str!("../test_data/alonzo22.block"),
include_str!("../../../test_data/alonzo22.block"),
];

for (idx, block_str) in test_blocks.iter().enumerate() {
println!("decoding test block {}", idx + 1);
let bytes = hex::decode(block_str).expect(&format!("bad block file {}", idx));

let block = BlockWrapper::decode_fragment(&bytes[..])
let block: BlockWrapper = minicbor::decode(&bytes[..])
.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);
assert!(bytes.eq(&bytes2), "re-encoded bytes didn't match original");
}
}
}
17 changes: 6 additions & 11 deletions pallas-primitives/src/byron/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ impl Address {

#[cfg(test)]
mod tests {
use crate::byron::Block;
use crate::Fragment;
use std::ops::Deref;
use crate::byron::MintedMainBlock;

type BlockWrapper<'b> = (u16, MintedMainBlock<'b>);

const KNOWN_ADDRESSES: &[&str] = &[
"DdzFFzCqrht8QHTQXbWy2qoyPaqTN8BjyfKygGmpy9dtot1tvkBfCaVTnR22XCaaDVn3M1U6aiMShoCLzw6VWSwzQKhhJrM3YjYp3wyy",
Expand All @@ -32,22 +32,17 @@ mod tests {
fn known_address_matches() {
// TODO: expand this test to include more test blocks
let block_idx = 1;
let block_str = include_str!("test_data/test2.block");
let block_str = include_str!("../../../test_data/byron2.block");

let block_bytes = hex::decode(block_str).expect(&format!("bad block file {}", block_idx));
let block = Block::decode_fragment(&block_bytes[..])
let (_, block): BlockWrapper = pallas_codec::minicbor::decode(&block_bytes[..])
.expect(&format!("error decoding cbor for file {}", block_idx));

let block = match block {
Block::MainBlock(x) => x,
Block::EbBlock(_) => panic!(),
};

// don't want to pass if we don't have tx in the block
assert!(block.body.tx_payload.len() > 0);

for tx in block.body.tx_payload.iter() {
for output in tx.deref().transaction.outputs.iter() {
for output in tx.transaction.outputs.iter() {
let addr_str = output.address.to_addr_string().unwrap();

assert!(
Expand Down
Loading

0 comments on commit fe80ff7

Please sign in to comment.