feat(traverse): Introduce new accessor methods (#152)

This commit is contained in:
Santiago Carmuega 2022-07-16 19:31:27 -03:00 committed by GitHub
parent fbea30aafe
commit 33c6152f19
17 changed files with 507 additions and 238 deletions

View file

@ -2,15 +2,15 @@ use std::borrow::Cow;
use pallas_codec::minicbor;
use pallas_crypto::hash::Hash;
use pallas_primitives::{alonzo, babbage, byron, ToHash};
use pallas_primitives::{alonzo, babbage, byron};
use crate::{probe, support, Era, Error, MultiEraBlock, MultiEraTx};
use crate::{probe, support, Era, Error, MultiEraBlock, MultiEraHeader, MultiEraTx};
type BlockWrapper<T> = (u16, T);
impl<'b> MultiEraBlock<'b> {
pub fn decode_epoch_boundary(cbor: &'b [u8]) -> Result<Self, Error> {
let (_, block): BlockWrapper<byron::EbBlock> =
let (_, block): BlockWrapper<byron::MintedEbBlock> =
minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
Ok(Self::EpochBoundary(Box::new(Cow::Owned(block))))
@ -85,27 +85,24 @@ impl<'b> MultiEraBlock<'b> {
}
}
pub fn number(&self) -> u64 {
pub fn header(&self) -> MultiEraHeader<'_> {
match self {
MultiEraBlock::EpochBoundary(x) => x
.header
.consensus_data
.difficulty
.first()
.cloned()
.unwrap_or_default(),
MultiEraBlock::AlonzoCompatible(x, _) => x.header.header_body.block_number,
MultiEraBlock::Babbage(x) => x.header.header_body.block_number,
MultiEraBlock::Byron(x) => x
.header
.consensus_data
.2
.first()
.cloned()
.unwrap_or_default(),
MultiEraBlock::EpochBoundary(x) => {
MultiEraHeader::EpochBoundary(Cow::Borrowed(&x.header))
}
MultiEraBlock::Byron(x) => MultiEraHeader::Byron(Cow::Borrowed(&x.header)),
MultiEraBlock::AlonzoCompatible(x, _) => {
MultiEraHeader::AlonzoCompatible(Cow::Borrowed(&x.header))
}
MultiEraBlock::Babbage(x) => MultiEraHeader::Babbage(Cow::Borrowed(&x.header)),
}
}
/// Returns the block number (aka: height)
pub fn number(&self) -> u64 {
self.header().number()
}
pub fn era(&self) -> Era {
match self {
MultiEraBlock::EpochBoundary(_) => Era::Byron,
@ -116,23 +113,14 @@ impl<'b> MultiEraBlock<'b> {
}
pub fn hash(&self) -> Hash<32> {
match self {
MultiEraBlock::EpochBoundary(x) => x.header.to_hash(),
MultiEraBlock::AlonzoCompatible(x, _) => x.header.to_hash(),
MultiEraBlock::Babbage(x) => x.header.to_hash(),
MultiEraBlock::Byron(x) => x.header.to_hash(),
}
self.header().hash()
}
pub fn slot(&self) -> u64 {
match self {
MultiEraBlock::EpochBoundary(x) => x.header.to_abs_slot(),
MultiEraBlock::AlonzoCompatible(x, _) => x.header.header_body.slot,
MultiEraBlock::Babbage(x) => x.header.header_body.slot,
MultiEraBlock::Byron(x) => x.header.consensus_data.0.to_abs_slot(),
}
self.header().slot()
}
/// Builds a vec with the Txs of the block
pub fn txs(&self) -> Vec<MultiEraTx> {
match self {
MultiEraBlock::AlonzoCompatible(x, era) => support::clone_alonzo_txs(x)
@ -151,6 +139,36 @@ impl<'b> MultiEraBlock<'b> {
}
}
/// Returns true if the there're no tx in the block
pub fn is_empty(&self) -> bool {
match self {
MultiEraBlock::EpochBoundary(_) => true,
MultiEraBlock::AlonzoCompatible(x, _) => x.transaction_bodies.is_empty(),
MultiEraBlock::Babbage(x) => x.transaction_bodies.is_empty(),
MultiEraBlock::Byron(x) => x.body.tx_payload.is_empty(),
}
}
/// Returns the count of txs in the block
pub fn tx_count(&self) -> usize {
match self {
MultiEraBlock::EpochBoundary(_) => 0,
MultiEraBlock::AlonzoCompatible(x, _) => x.transaction_bodies.len(),
MultiEraBlock::Babbage(x) => x.transaction_bodies.len(),
MultiEraBlock::Byron(x) => x.body.tx_payload.len(),
}
}
/// Returns true if the block has any auxiliary data
pub fn has_aux_data(&self) -> bool {
match self {
MultiEraBlock::EpochBoundary(_) => false,
MultiEraBlock::AlonzoCompatible(x, _) => !x.auxiliary_data_set.is_empty(),
MultiEraBlock::Babbage(x) => !x.auxiliary_data_set.is_empty(),
MultiEraBlock::Byron(_) => false,
}
}
pub fn as_alonzo(&self) -> Option<&alonzo::MintedBlock> {
match self {
MultiEraBlock::EpochBoundary(_) => None,

View file

@ -5,8 +5,8 @@ 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),
_ => None,
}
}
}

View file

@ -1,3 +1,5 @@
use std::borrow::Cow;
use pallas_codec::minicbor;
use pallas_crypto::hash::{Hash, Hasher};
use pallas_primitives::ToHash;
@ -11,24 +13,33 @@ impl<'b> MultiEraHeader<'b> {
0 => match subtag {
Some(0) => {
let header = minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
Ok(MultiEraHeader::EpochBoundary(header))
Ok(MultiEraHeader::EpochBoundary(Cow::Owned(header)))
}
_ => {
let header = minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
Ok(MultiEraHeader::Byron(header))
Ok(MultiEraHeader::Byron(Cow::Owned(header)))
}
},
5 => {
let header = minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
Ok(MultiEraHeader::Babbage(header))
Ok(MultiEraHeader::Babbage(Cow::Owned(header)))
}
_ => {
let header = minicbor::decode(cbor).map_err(Error::invalid_cbor)?;
Ok(MultiEraHeader::AlonzoCompatible(header))
Ok(MultiEraHeader::AlonzoCompatible(Cow::Owned(header)))
}
}
}
pub fn cbor(&self) -> &'b [u8] {
match self {
MultiEraHeader::EpochBoundary(x) => x.raw_cbor(),
MultiEraHeader::AlonzoCompatible(x) => x.raw_cbor(),
MultiEraHeader::Babbage(x) => x.raw_cbor(),
MultiEraHeader::Byron(x) => x.raw_cbor(),
}
}
pub fn number(&self) -> u64 {
match self {
MultiEraHeader::EpochBoundary(x) => x

View file

@ -11,18 +11,18 @@ impl OutputRef {
Self(hash, index)
}
pub fn tx_id(&self) -> &Hash<32> {
pub fn hash(&self) -> &Hash<32> {
&self.0
}
pub fn tx_index(&self) -> u64 {
pub fn index(&self) -> u64 {
self.1
}
}
impl Display for OutputRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}#{}", self.tx_id(), self.tx_index())
write!(f, "{}#{}", self.hash(), self.index())
}
}
@ -52,13 +52,33 @@ impl<'b> MultiEraInput<'b> {
Self::AlonzoCompatible(Box::new(Cow::Borrowed(input)))
}
pub fn output_ref(&self) -> Option<OutputRef> {
pub fn output_ref(&self) -> OutputRef {
match self {
MultiEraInput::Byron(x) => match x.deref().deref() {
byron::TxIn::Variant0(CborWrap((tx, idx))) => Some(OutputRef(*tx, *idx as u64)),
byron::TxIn::Other(_, _) => None,
byron::TxIn::Variant0(CborWrap((tx, idx))) => OutputRef(*tx, *idx as u64),
byron::TxIn::Other(_, _) => unreachable!(),
},
MultiEraInput::AlonzoCompatible(x) => Some(OutputRef(x.transaction_id, x.index)),
MultiEraInput::AlonzoCompatible(x) => OutputRef(x.transaction_id, x.index),
}
}
pub fn hash(&self) -> &Hash<32> {
match self {
MultiEraInput::Byron(x) => match x.deref().deref() {
byron::TxIn::Variant0(CborWrap((x, _))) => x,
byron::TxIn::Other(_, _) => unreachable!(),
},
MultiEraInput::AlonzoCompatible(x) => &x.transaction_id,
}
}
pub fn index(&self) -> u64 {
match self {
MultiEraInput::Byron(x) => match x.deref().deref() {
byron::TxIn::Variant0(CborWrap((_, x))) => *x as u64,
byron::TxIn::Other(_, _) => unreachable!(),
},
MultiEraInput::AlonzoCompatible(x) => x.index,
}
}
@ -110,10 +130,9 @@ mod tests {
let block = MultiEraBlock::decode(&cbor).expect("invalid cbor");
for tx in block.txs() {
for input in tx.inputs() {
if let Some(out) = input.output_ref() {
let right = expected.remove(0);
assert_eq!(out.to_string(), right);
}
let ref_ = input.output_ref();
let right = expected.remove(0);
assert_eq!(ref_.to_string(), right);
}
}
}
@ -130,10 +149,10 @@ mod tests {
let sample = OutputRef::from_str(vector).unwrap();
assert_eq!(
sample.tx_id().to_string(),
sample.hash().to_string(),
"da832fb5ef57df5b91817e9a7448d26e92552afb34f8ee5adb491b24bbe990d5"
);
assert_eq!(sample.tx_index(), 14);
assert_eq!(sample.index(), 14);
}
}
}

View file

@ -15,10 +15,12 @@ pub mod era;
pub mod header;
pub mod input;
pub mod meta;
pub mod mint;
pub mod output;
pub mod probe;
mod support;
pub mod tx;
pub mod withdrawals;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
@ -45,16 +47,16 @@ pub enum Feature {
#[derive(Debug)]
pub enum MultiEraHeader<'b> {
EpochBoundary(KeepRaw<'b, byron::EbbHead>),
AlonzoCompatible(KeepRaw<'b, alonzo::Header>),
Babbage(KeepRaw<'b, babbage::Header>),
Byron(KeepRaw<'b, byron::BlockHead>),
EpochBoundary(Cow<'b, KeepRaw<'b, byron::EbbHead>>),
AlonzoCompatible(Cow<'b, KeepRaw<'b, alonzo::Header>>),
Babbage(Cow<'b, KeepRaw<'b, babbage::Header>>),
Byron(Cow<'b, KeepRaw<'b, byron::BlockHead>>),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MultiEraBlock<'b> {
EpochBoundary(Box<Cow<'b, byron::EbBlock>>),
EpochBoundary(Box<Cow<'b, byron::MintedEbBlock<'b>>>),
AlonzoCompatible(Box<Cow<'b, alonzo::MintedBlock<'b>>>, Era),
Babbage(Box<Cow<'b, babbage::MintedBlock<'b>>>),
Byron(Box<Cow<'b, byron::MintedBlock<'b>>>),
@ -83,12 +85,36 @@ pub enum MultiEraInput<'b> {
AlonzoCompatible(Box<Cow<'b, alonzo::TransactionInput>>),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MultiEraCert<'b> {
NotApplicable,
AlonzoCompatible(Box<Cow<'b, alonzo::Certificate>>),
}
pub struct MultiEraMeta<'b>(Cow<'b, alonzo::Metadata>);
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MultiEraMeta<'b> {
NotApplicable,
Empty,
AlonzoCompatible(Cow<'b, alonzo::Metadata>),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MultiEraMint<'b> {
NotApplicable,
Empty,
AlonzoCompatible(Cow<'b, alonzo::Mint>),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum MultiEraWithdrawals<'b> {
NotApplicable,
Empty,
AlonzoCompatible(Cow<'b, alonzo::Withdrawals>),
}
#[derive(Debug, Clone)]
pub struct OutputRef(Hash<32>, u64);

View file

@ -2,14 +2,44 @@ use pallas_primitives::alonzo;
use crate::MultiEraMeta;
impl Default for MultiEraMeta<'_> {
fn default() -> Self {
MultiEraMeta::Empty
}
}
impl<'b> MultiEraMeta<'b> {
pub fn entries(&self) -> &alonzo::Metadata {
&self.0
pub fn as_alonzo(&self) -> Option<&alonzo::Metadata> {
match self {
Self::AlonzoCompatible(x) => Some(x),
_ => None,
}
}
pub fn is_empty(&self) -> bool {
match self {
MultiEraMeta::AlonzoCompatible(x) => x.is_empty(),
_ => true,
}
}
pub fn find(&self, label: alonzo::MetadatumLabel) -> Option<&alonzo::Metadatum> {
self.entries()
self.as_alonzo()?
.iter()
.find_map(|(key, value)| if key.eq(&label) { Some(value) } else { None })
}
pub fn collect<'a, T>(&'a self) -> T
where
T: FromIterator<(&'a alonzo::MetadatumLabel, &'a alonzo::Metadatum)>,
{
match self {
MultiEraMeta::NotApplicable => T::from_iter(std::iter::empty()),
MultiEraMeta::Empty => T::from_iter(std::iter::empty()),
MultiEraMeta::AlonzoCompatible(x) => {
let iter = x.iter().map(|(k, v)| (k, v));
T::from_iter(iter)
}
}
}
}

View file

@ -0,0 +1,32 @@
use pallas_primitives::alonzo;
use crate::MultiEraMint;
impl Default for MultiEraMint<'_> {
fn default() -> Self {
MultiEraMint::Empty
}
}
impl<'b> MultiEraMint<'b> {
pub fn len(&self) -> usize {
match self {
MultiEraMint::AlonzoCompatible(x) => x.len(),
_ => 0,
}
}
pub fn is_empty(&self) -> bool {
match self {
MultiEraMint::AlonzoCompatible(x) => x.is_empty(),
_ => true,
}
}
pub fn as_alonzo(&self) -> Option<&alonzo::Mint> {
match self {
Self::AlonzoCompatible(x) => Some(x),
_ => None,
}
}
}

View file

@ -1,6 +1,6 @@
use std::{borrow::Cow, ops::Deref};
use pallas_addresses::{Address, ByronAddress, Error as AddressError};
use pallas_addresses::{Address, Error as AddressError};
use pallas_codec::minicbor;
use pallas_primitives::{alonzo, babbage, byron};
@ -19,17 +19,21 @@ impl<'b> MultiEraOutput<'b> {
Self::Babbage(Box::new(Cow::Borrowed(output)))
}
pub fn to_address(&self) -> Result<Address, AddressError> {
pub fn address_raw(&self) -> &[u8] {
match self {
MultiEraOutput::AlonzoCompatible(x) => Address::from_bytes(&x.address),
MultiEraOutput::AlonzoCompatible(x) => &x.address,
MultiEraOutput::Babbage(x) => match x.deref().deref() {
babbage::TransactionOutput::Legacy(x) => Address::from_bytes(&x.address),
babbage::TransactionOutput::PostAlonzo(x) => Address::from_bytes(&x.address),
babbage::TransactionOutput::Legacy(x) => &x.address,
babbage::TransactionOutput::PostAlonzo(x) => &x.address,
},
MultiEraOutput::Byron(x) => Ok(ByronAddress::new(x.address.clone()).into()),
MultiEraOutput::Byron(x) => x.address.payload.deref(),
}
}
pub fn address(&self) -> Result<Address, AddressError> {
Address::from_bytes(self.address_raw())
}
pub fn ada_amount(&self) -> u64 {
match self {
MultiEraOutput::Byron(x) => x.amount,

View file

@ -6,7 +6,10 @@ use pallas_primitives::{
};
use std::{borrow::Cow, ops::Deref};
use crate::{Era, MultiEraCert, MultiEraInput, MultiEraMeta, MultiEraOutput, MultiEraTx};
use crate::{
Era, MultiEraCert, MultiEraInput, MultiEraMeta, MultiEraMint, MultiEraOutput, MultiEraTx,
MultiEraWithdrawals,
};
impl<'b> MultiEraTx<'b> {
pub fn from_byron(tx: &'b byron::MintedTxPayload<'b>) -> Self {
@ -151,6 +154,58 @@ impl<'b> MultiEraTx<'b> {
}
}
pub fn mint(&self) -> MultiEraMint {
match self {
MultiEraTx::AlonzoCompatible(x, _) => x
.transaction_body
.mint
.as_ref()
.map(|c| MultiEraMint::AlonzoCompatible(Cow::Borrowed(c)))
.unwrap_or_default(),
MultiEraTx::Babbage(x) => x
.transaction_body
.mint
.as_ref()
.map(|c| MultiEraMint::AlonzoCompatible(Cow::Borrowed(c)))
.unwrap_or_default(),
MultiEraTx::Byron(_) => MultiEraMint::NotApplicable,
}
}
pub fn collateral(&self) -> Vec<MultiEraInput> {
match self {
MultiEraTx::AlonzoCompatible(x, _) => x
.transaction_body
.collateral
.iter()
.flat_map(|x| x.iter())
.map(MultiEraInput::from_alonzo_compatible)
.collect(),
MultiEraTx::Babbage(x) => x
.transaction_body
.collateral
.iter()
.flat_map(|x| x.iter())
.map(MultiEraInput::from_alonzo_compatible)
.collect(),
MultiEraTx::Byron(_) => vec![],
}
}
pub fn withdrawals(&'b self) -> MultiEraWithdrawals<'b> {
match self {
MultiEraTx::AlonzoCompatible(x, _) => match &x.transaction_body.withdrawals {
Some(x) => MultiEraWithdrawals::AlonzoCompatible(Cow::Borrowed(x)),
None => MultiEraWithdrawals::Empty,
},
MultiEraTx::Babbage(x) => match &x.transaction_body.withdrawals {
Some(x) => MultiEraWithdrawals::AlonzoCompatible(Cow::Borrowed(x)),
None => MultiEraWithdrawals::Empty,
},
MultiEraTx::Byron(_) => MultiEraWithdrawals::NotApplicable,
}
}
fn aux_data(&self) -> Option<&KeepRaw<'_, AuxiliaryData>> {
match self {
MultiEraTx::AlonzoCompatible(x, _) => x.auxiliary_data.as_ref(),
@ -159,15 +214,28 @@ impl<'b> MultiEraTx<'b> {
}
}
pub fn metadata(&self) -> Option<MultiEraMeta> {
match self.aux_data()?.deref() {
AuxiliaryData::Shelley(x) => MultiEraMeta(Cow::Borrowed(x)).into(),
AuxiliaryData::ShelleyMa(x) => {
MultiEraMeta(Cow::Borrowed(&x.transaction_metadata)).into()
}
AuxiliaryData::PostAlonzo(x) => {
x.metadata.as_ref().map(|x| MultiEraMeta(Cow::Borrowed(x)))
}
pub fn metadata(&'b self) -> MultiEraMeta<'b> {
match self.aux_data() {
Some(x) => match x.deref() {
AuxiliaryData::Shelley(x) => MultiEraMeta::AlonzoCompatible(Cow::Borrowed(x)),
AuxiliaryData::ShelleyMa(x) => {
MultiEraMeta::AlonzoCompatible(Cow::Borrowed(&x.transaction_metadata))
}
AuxiliaryData::PostAlonzo(x) => x
.metadata
.as_ref()
.map(|x| MultiEraMeta::AlonzoCompatible(Cow::Borrowed(x)))
.unwrap_or_default(),
},
None => MultiEraMeta::Empty,
}
}
pub fn is_valid(&self) -> bool {
match self {
MultiEraTx::AlonzoCompatible(x, _) => x.success,
MultiEraTx::Babbage(x) => x.success,
MultiEraTx::Byron(_) => true,
}
}

View file

@ -0,0 +1,33 @@
use pallas_primitives::alonzo;
use crate::MultiEraWithdrawals;
impl<'b> MultiEraWithdrawals<'b> {
pub fn as_alonzo(&self) -> Option<&alonzo::Withdrawals> {
match self {
Self::AlonzoCompatible(x) => Some(x),
_ => None,
}
}
pub fn is_empty(&self) -> bool {
match self {
MultiEraWithdrawals::AlonzoCompatible(x) => x.is_empty(),
_ => true,
}
}
pub fn collect<'a, T>(&'a self) -> T
where
T: FromIterator<(&'a [u8], u64)>,
{
match self {
MultiEraWithdrawals::NotApplicable => T::from_iter(std::iter::empty()),
MultiEraWithdrawals::Empty => T::from_iter(std::iter::empty()),
MultiEraWithdrawals::AlonzoCompatible(x) => {
let iter = x.iter().map(|(k, v)| (k.as_slice(), v.into()));
T::from_iter(iter)
}
}
}
}