feat: Implement common traverse iterators (#119)
This commit is contained in:
parent
2a43dd1061
commit
e67da5e7ef
12 changed files with 378 additions and 316 deletions
|
|
@ -1,54 +1,94 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use pallas_codec::minicbor;
|
||||
use pallas_crypto::hash::Hash;
|
||||
use pallas_primitives::{alonzo, byron, ToHash};
|
||||
|
||||
use crate::{probe, Era, Error, MultiEraBlock};
|
||||
use crate::{probe, support, Era, Error, MultiEraBlock, MultiEraTx};
|
||||
|
||||
type BlockWrapper<T> = (u16, T);
|
||||
|
||||
impl<'b> MultiEraBlock<'b> {
|
||||
pub fn from_epoch_boundary(block: byron::EbBlock) -> Self {
|
||||
Self::EpochBoundary(Box::new(block))
|
||||
pub fn decode_epoch_boundary(cbor: &'b [u8]) -> Result<Self, Error> {
|
||||
let (_, block): BlockWrapper<byron::EbBlock> =
|
||||
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
|
||||
|
||||
Ok(Self::EpochBoundary(Box::new(Cow::Owned(block))))
|
||||
}
|
||||
|
||||
pub fn from_byron(block: byron::MintedBlock<'b>) -> Self {
|
||||
Self::Byron(Box::new(block))
|
||||
pub fn decode_byron(cbor: &'b [u8]) -> Result<Self, Error> {
|
||||
let (_, block): BlockWrapper<byron::MintedBlock> =
|
||||
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
|
||||
|
||||
Ok(Self::Byron(Box::new(Cow::Owned(block))))
|
||||
}
|
||||
|
||||
pub fn from_alonzo_compatible(block: alonzo::MintedBlock<'b>) -> Self {
|
||||
Self::AlonzoCompatible(Box::new(block))
|
||||
pub fn decode_shelley(cbor: &'b [u8]) -> Result<Self, Error> {
|
||||
let (_, block): BlockWrapper<alonzo::MintedBlock> =
|
||||
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
|
||||
|
||||
Ok(Self::AlonzoCompatible(
|
||||
Box::new(Cow::Owned(block)),
|
||||
Era::Shelley,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn decode_allegra(cbor: &'b [u8]) -> Result<Self, Error> {
|
||||
let (_, block): BlockWrapper<alonzo::MintedBlock> =
|
||||
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
|
||||
|
||||
Ok(Self::AlonzoCompatible(
|
||||
Box::new(Cow::Owned(block)),
|
||||
Era::Allegra,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn decode_mary(cbor: &'b [u8]) -> Result<Self, Error> {
|
||||
let (_, block): BlockWrapper<alonzo::MintedBlock> =
|
||||
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
|
||||
|
||||
Ok(Self::AlonzoCompatible(
|
||||
Box::new(Cow::Owned(block)),
|
||||
Era::Mary,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn decode_alonzo(cbor: &'b [u8]) -> Result<Self, Error> {
|
||||
let (_, block): BlockWrapper<alonzo::MintedBlock> =
|
||||
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
|
||||
|
||||
Ok(Self::AlonzoCompatible(
|
||||
Box::new(Cow::Owned(block)),
|
||||
Era::Alonzo,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn decode(cbor: &'b [u8]) -> Result<MultiEraBlock<'b>, Error> {
|
||||
match probe::block_era(cbor) {
|
||||
probe::Outcome::EpochBoundary => {
|
||||
let (_, block): BlockWrapper<byron::EbBlock> =
|
||||
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
|
||||
|
||||
Ok(MultiEraBlock::from_epoch_boundary(block))
|
||||
}
|
||||
probe::Outcome::EpochBoundary => Self::decode_epoch_boundary(cbor),
|
||||
probe::Outcome::Matched(era) => match era {
|
||||
Era::Byron => {
|
||||
let (_, block): BlockWrapper<byron::MintedBlock> =
|
||||
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
|
||||
|
||||
Ok(Self::from_byron(block))
|
||||
}
|
||||
Era::Shelley | Era::Allegra | Era::Mary | Era::Alonzo => {
|
||||
let (_, block): BlockWrapper<alonzo::MintedBlock> =
|
||||
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
|
||||
|
||||
Ok(Self::from_alonzo_compatible(block))
|
||||
}
|
||||
Era::Byron => Self::decode_byron(cbor),
|
||||
Era::Shelley => Self::decode_shelley(cbor),
|
||||
Era::Allegra => Self::decode_allegra(cbor),
|
||||
Era::Mary => Self::decode_mary(cbor),
|
||||
Era::Alonzo => Self::decode_alonzo(cbor),
|
||||
},
|
||||
probe::Outcome::Inconclusive => Err(Error::unknown_cbor(cbor)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn era(&self) -> Era {
|
||||
match self {
|
||||
MultiEraBlock::EpochBoundary(_) => Era::Byron,
|
||||
MultiEraBlock::AlonzoCompatible(_, x) => *x,
|
||||
MultiEraBlock::Byron(_) => Era::Byron,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash(&self) -> Hash<32> {
|
||||
match self {
|
||||
MultiEraBlock::EpochBoundary(x) => x.header.to_hash(),
|
||||
MultiEraBlock::AlonzoCompatible(x) => x.header.to_hash(),
|
||||
MultiEraBlock::AlonzoCompatible(x, _) => x.header.to_hash(),
|
||||
MultiEraBlock::Byron(x) => x.header.to_hash(),
|
||||
}
|
||||
}
|
||||
|
|
@ -56,8 +96,60 @@ impl<'b> MultiEraBlock<'b> {
|
|||
pub fn slot(&self) -> u64 {
|
||||
match self {
|
||||
MultiEraBlock::EpochBoundary(x) => x.header.to_abs_slot(),
|
||||
MultiEraBlock::AlonzoCompatible(x) => x.header.header_body.slot,
|
||||
MultiEraBlock::AlonzoCompatible(x, _) => x.header.header_body.slot,
|
||||
MultiEraBlock::Byron(x) => x.header.consensus_data.0.to_abs_slot(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn txs(&self) -> Vec<MultiEraTx> {
|
||||
match self {
|
||||
MultiEraBlock::AlonzoCompatible(x, _) => support::clone_alonzo_txs(x)
|
||||
.into_iter()
|
||||
.map(|x| MultiEraTx::AlonzoCompatible(Box::new(Cow::Owned(x))))
|
||||
.collect(),
|
||||
MultiEraBlock::Byron(x) => support::clone_byron_txs(x)
|
||||
.into_iter()
|
||||
.map(|x| MultiEraTx::Byron(Box::new(Cow::Owned(x))))
|
||||
.collect(),
|
||||
MultiEraBlock::EpochBoundary(_) => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_alonzo(&self) -> Option<&alonzo::MintedBlock> {
|
||||
match self {
|
||||
MultiEraBlock::EpochBoundary(_) => None,
|
||||
MultiEraBlock::AlonzoCompatible(x, _) => Some(x),
|
||||
MultiEraBlock::Byron(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_byron(&self) -> Option<&byron::MintedBlock> {
|
||||
match self {
|
||||
MultiEraBlock::EpochBoundary(_) => None,
|
||||
MultiEraBlock::AlonzoCompatible(_, _) => None,
|
||||
MultiEraBlock::Byron(x) => Some(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_iteration() {
|
||||
let blocks = vec![
|
||||
(include_str!("../../test_data/byron2.block"), 2usize),
|
||||
(include_str!("../../test_data/shelley1.block"), 0),
|
||||
(include_str!("../../test_data/mary1.block"), 0),
|
||||
(include_str!("../../test_data/allegra1.block"), 0),
|
||||
(include_str!("../../test_data/alonzo1.block"), 5),
|
||||
];
|
||||
|
||||
for (block_str, tx_count) in blocks.into_iter() {
|
||||
let cbor = hex::decode(block_str).expect("invalid hex");
|
||||
let block = MultiEraBlock::decode(&cbor).expect("invalid cbor");
|
||||
assert_eq!(block.txs().len(), tx_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
pallas-traverse/src/cert.rs
Normal file
12
pallas-traverse/src/cert.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
use pallas_primitives::alonzo;
|
||||
|
||||
use crate::MultiEraCert;
|
||||
|
||||
impl<'b> MultiEraCert<'b> {
|
||||
pub fn as_alonzo(&self) -> Option<&alonzo::Certificate> {
|
||||
match self {
|
||||
MultiEraCert::NotApplicable => None,
|
||||
MultiEraCert::AlonzoCompatible(x) => Some(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
//! Iterate over block data
|
||||
|
||||
use pallas_primitives::{alonzo, byron};
|
||||
|
||||
use crate::{MultiEraBlock, MultiEraTx};
|
||||
|
||||
fn clone_alonzo_tx_at<'b>(
|
||||
block: &'b alonzo::MintedBlock,
|
||||
index: usize,
|
||||
) -> Option<alonzo::MintedTx<'b>> {
|
||||
let transaction_body = block.transaction_bodies.get(index).cloned()?;
|
||||
let transaction_witness_set = block.transaction_witness_sets.get(index).cloned()?;
|
||||
let success = block
|
||||
.invalid_transactions
|
||||
.as_ref()?
|
||||
.contains(&(index as u32));
|
||||
|
||||
let auxiliary_data = block
|
||||
.auxiliary_data_set
|
||||
.iter()
|
||||
.find_map(|(idx, val)| {
|
||||
if idx.eq(&(index as u32)) {
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.cloned();
|
||||
|
||||
Some(alonzo::MintedTx {
|
||||
transaction_body,
|
||||
transaction_witness_set,
|
||||
success,
|
||||
auxiliary_data,
|
||||
})
|
||||
}
|
||||
|
||||
fn clone_byron_tx_at<'b>(
|
||||
block: &'b byron::MintedBlock,
|
||||
index: usize,
|
||||
) -> Option<byron::MintedTxPayload<'b>> {
|
||||
block.body.tx_payload.get(index).cloned()
|
||||
}
|
||||
|
||||
pub struct TxIter<'b> {
|
||||
block: &'b MultiEraBlock<'b>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'b> Iterator for TxIter<'b> {
|
||||
type Item = MultiEraTx<'b>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let tx = match self.block {
|
||||
MultiEraBlock::EpochBoundary(_) => None,
|
||||
MultiEraBlock::AlonzoCompatible(x) => {
|
||||
clone_alonzo_tx_at(x, self.index).map(MultiEraTx::from_alonzo_compatible)
|
||||
}
|
||||
MultiEraBlock::Byron(x) => clone_byron_tx_at(x, self.index).map(MultiEraTx::from_byron),
|
||||
}?;
|
||||
|
||||
self.index += 1;
|
||||
Some(tx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> MultiEraBlock<'b> {
|
||||
pub fn tx_iter(&'b self) -> TxIter<'b> {
|
||||
TxIter {
|
||||
index: 0,
|
||||
block: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_iteration() {
|
||||
let blocks = vec![
|
||||
(include_str!("../../test_data/byron2.block"), 2usize),
|
||||
(include_str!("../../test_data/shelley1.block"), 0),
|
||||
(include_str!("../../test_data/mary1.block"), 0),
|
||||
(include_str!("../../test_data/allegra1.block"), 0),
|
||||
(include_str!("../../test_data/alonzo1.block"), 5),
|
||||
];
|
||||
|
||||
for (block_str, tx_count) in blocks.into_iter() {
|
||||
let cbor = hex::decode(block_str).expect("invalid hex");
|
||||
let block = MultiEraBlock::decode(&cbor).expect("invalid cbor");
|
||||
assert_eq!(block.tx_iter().count(), tx_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,16 @@
|
|||
//! Utilities to traverse over multi-era block data
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Display;
|
||||
|
||||
use pallas_primitives::{alonzo, byron};
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod block;
|
||||
pub mod iter;
|
||||
pub mod cert;
|
||||
pub mod output;
|
||||
pub mod probe;
|
||||
mod support;
|
||||
pub mod tx;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
|
@ -19,19 +23,31 @@ pub enum Era {
|
|||
Alonzo, // smart-contracts
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum MultiEraTx<'b> {
|
||||
AlonzoCompatible(Box<alonzo::MintedTx<'b>>),
|
||||
Byron(Box<byron::MintedTxPayload<'b>>),
|
||||
pub enum MultiEraBlock<'b> {
|
||||
EpochBoundary(Box<Cow<'b, byron::EbBlock>>),
|
||||
AlonzoCompatible(Box<Cow<'b, alonzo::MintedBlock<'b>>>, Era),
|
||||
Byron(Box<Cow<'b, byron::MintedBlock<'b>>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum MultiEraBlock<'b> {
|
||||
EpochBoundary(Box<byron::EbBlock>),
|
||||
AlonzoCompatible(Box<alonzo::MintedBlock<'b>>),
|
||||
Byron(Box<byron::MintedBlock<'b>>),
|
||||
pub enum MultiEraTx<'b> {
|
||||
AlonzoCompatible(Box<Cow<'b, alonzo::MintedTx<'b>>>),
|
||||
Byron(Box<Cow<'b, byron::MintedTxPayload<'b>>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum MultiEraOutput<'b> {
|
||||
Byron(Box<Cow<'b, byron::TxOut>>),
|
||||
AlonzoCompatible(Box<Cow<'b, alonzo::TransactionOutput>>),
|
||||
}
|
||||
|
||||
pub enum MultiEraCert<'b> {
|
||||
NotApplicable,
|
||||
AlonzoCompatible(Box<Cow<'b, alonzo::Certificate>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
38
pallas-traverse/src/output.rs
Normal file
38
pallas-traverse/src/output.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use pallas_primitives::{alonzo, byron};
|
||||
|
||||
use crate::MultiEraOutput;
|
||||
|
||||
impl<'b> MultiEraOutput<'b> {
|
||||
pub fn from_byron(output: &'b byron::TxOut) -> Self {
|
||||
Self::Byron(Box::new(Cow::Borrowed(output)))
|
||||
}
|
||||
|
||||
pub fn from_alonzo_compatible(output: &'b alonzo::TransactionOutput) -> Self {
|
||||
Self::AlonzoCompatible(Box::new(Cow::Borrowed(output)))
|
||||
}
|
||||
|
||||
pub fn address(&self, hrp: &str) -> String {
|
||||
match self {
|
||||
MultiEraOutput::Byron(x) => x.address.to_addr_string().expect("invalid address value"),
|
||||
MultiEraOutput::AlonzoCompatible(x) => {
|
||||
x.to_bech32_address(hrp).expect("invalid address value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_alonzo(&self) -> Option<&alonzo::TransactionOutput> {
|
||||
match self {
|
||||
MultiEraOutput::Byron(_) => None,
|
||||
MultiEraOutput::AlonzoCompatible(x) => Some(x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_byron(&self) -> Option<&byron::TxOut> {
|
||||
match self {
|
||||
MultiEraOutput::Byron(x) => Some(x),
|
||||
MultiEraOutput::AlonzoCompatible(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
47
pallas-traverse/src/support.rs
Normal file
47
pallas-traverse/src/support.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//! Internal supporting utilities
|
||||
|
||||
use pallas_primitives::{alonzo, byron};
|
||||
|
||||
pub fn clone_alonzo_tx_at<'b>(
|
||||
block: &'b alonzo::MintedBlock,
|
||||
index: usize,
|
||||
) -> Option<alonzo::MintedTx<'b>> {
|
||||
let transaction_body = block.transaction_bodies.get(index).cloned()?;
|
||||
|
||||
let transaction_witness_set = block.transaction_witness_sets.get(index).cloned()?;
|
||||
|
||||
let success = !block
|
||||
.invalid_transactions
|
||||
.as_ref()?
|
||||
.contains(&(index as u32));
|
||||
|
||||
let auxiliary_data = block
|
||||
.auxiliary_data_set
|
||||
.iter()
|
||||
.find_map(|(idx, val)| {
|
||||
if idx.eq(&(index as u32)) {
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.cloned();
|
||||
|
||||
Some(alonzo::MintedTx {
|
||||
transaction_body,
|
||||
transaction_witness_set,
|
||||
success,
|
||||
auxiliary_data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clone_alonzo_txs<'b>(block: &'b alonzo::MintedBlock) -> Vec<alonzo::MintedTx<'b>> {
|
||||
(0..block.transaction_bodies.len())
|
||||
.step_by(1)
|
||||
.filter_map(|idx| clone_alonzo_tx_at(block, idx))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn clone_byron_txs<'b>(block: &'b byron::MintedBlock) -> Vec<byron::MintedTxPayload<'b>> {
|
||||
block.body.tx_payload.iter().cloned().collect()
|
||||
}
|
||||
|
|
@ -1,13 +1,74 @@
|
|||
use pallas_primitives::{alonzo, byron};
|
||||
use pallas_codec::minicbor;
|
||||
use pallas_crypto::hash::Hash;
|
||||
use pallas_primitives::{alonzo, byron, ToHash};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::MultiEraTx;
|
||||
use crate::{MultiEraCert, MultiEraOutput, MultiEraTx};
|
||||
|
||||
impl<'b> MultiEraTx<'b> {
|
||||
pub fn from_byron(tx: byron::MintedTxPayload<'b>) -> Self {
|
||||
Self::Byron(Box::new(tx))
|
||||
pub fn from_byron(tx: &'b byron::MintedTxPayload<'b>) -> Self {
|
||||
Self::Byron(Box::new(Cow::Borrowed(tx)))
|
||||
}
|
||||
|
||||
pub fn from_alonzo_compatible(tx: alonzo::MintedTx<'b>) -> Self {
|
||||
Self::AlonzoCompatible(Box::new(tx))
|
||||
pub fn from_alonzo_compatible(tx: &'b alonzo::MintedTx<'b>) -> Self {
|
||||
Self::AlonzoCompatible(Box::new(Cow::Borrowed(tx)))
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> Result<Vec<u8>, minicbor::encode::Error<std::io::Error>> {
|
||||
match self {
|
||||
MultiEraTx::AlonzoCompatible(x) => minicbor::to_vec(x),
|
||||
MultiEraTx::Byron(x) => minicbor::to_vec(x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash(&self) -> Hash<32> {
|
||||
match self {
|
||||
MultiEraTx::AlonzoCompatible(x) => x.transaction_body.to_hash(),
|
||||
MultiEraTx::Byron(x) => x.transaction.to_hash(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn outputs(&self) -> Vec<MultiEraOutput> {
|
||||
match self {
|
||||
MultiEraTx::AlonzoCompatible(x) => x
|
||||
.transaction_body
|
||||
.outputs
|
||||
.iter()
|
||||
.map(MultiEraOutput::from_alonzo_compatible)
|
||||
.collect(),
|
||||
MultiEraTx::Byron(x) => x
|
||||
.transaction
|
||||
.outputs
|
||||
.iter()
|
||||
.map(MultiEraOutput::from_byron)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn certs(&self) -> Vec<MultiEraCert> {
|
||||
match self {
|
||||
MultiEraTx::AlonzoCompatible(x) => x
|
||||
.transaction_body
|
||||
.certificates
|
||||
.iter()
|
||||
.flat_map(|c| c.iter())
|
||||
.map(|c| MultiEraCert::AlonzoCompatible(Box::new(Cow::Borrowed(c))))
|
||||
.collect(),
|
||||
MultiEraTx::Byron(_) => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_alonzo(&self) -> Option<&alonzo::MintedTx> {
|
||||
match self {
|
||||
MultiEraTx::AlonzoCompatible(x) => Some(x),
|
||||
MultiEraTx::Byron(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_byron(&self) -> Option<&byron::MintedTxPayload> {
|
||||
match self {
|
||||
MultiEraTx::AlonzoCompatible(_) => None,
|
||||
MultiEraTx::Byron(x) => Some(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue