From 3ac707e48679639360bb95ac612760e11fa1d3ed Mon Sep 17 00:00:00 2001 From: Santiago Carmuega Date: Sat, 16 Jul 2022 19:31:27 -0300 Subject: [PATCH] feat(traverse): Introduce new accessor methods (#152) --- pallas-addresses/Cargo.toml | 1 - pallas-addresses/src/byron.rs | 159 ++++++++++++++++++++++ pallas-addresses/src/lib.rs | 5 +- pallas-codec/src/utils.rs | 10 +- pallas-multiplexer/tests/integration.rs | 7 +- pallas-primitives/src/alonzo/model.rs | 4 +- pallas-primitives/src/byron/model.rs | 168 +++--------------------- pallas-traverse/src/block.rs | 82 +++++++----- pallas-traverse/src/cert.rs | 2 +- pallas-traverse/src/header.rs | 19 ++- pallas-traverse/src/input.rs | 45 +++++-- pallas-traverse/src/lib.rs | 38 +++++- pallas-traverse/src/meta.rs | 36 ++++- pallas-traverse/src/mint.rs | 32 +++++ pallas-traverse/src/output.rs | 16 ++- pallas-traverse/src/tx.rs | 88 +++++++++++-- pallas-traverse/src/withdrawals.rs | 33 +++++ 17 files changed, 507 insertions(+), 238 deletions(-) create mode 100644 pallas-addresses/src/byron.rs create mode 100644 pallas-traverse/src/mint.rs create mode 100644 pallas-traverse/src/withdrawals.rs diff --git a/pallas-addresses/Cargo.toml b/pallas-addresses/Cargo.toml index 69b46a7..67ac146 100644 --- a/pallas-addresses/Cargo.toml +++ b/pallas-addresses/Cargo.toml @@ -16,7 +16,6 @@ authors = [ hex = "0.4.3" pallas-crypto = { version = "0.11.0", path = "../pallas-crypto" } pallas-codec = { version = "0.11.0", path = "../pallas-codec" } -pallas-primitives = { version = "0.11.0", path = "../pallas-primitives" } base58 = "0.2.0" bech32 = "0.8.1" thiserror = "1.0.31" diff --git a/pallas-addresses/src/byron.rs b/pallas-addresses/src/byron.rs new file mode 100644 index 0000000..780b6fd --- /dev/null +++ b/pallas-addresses/src/byron.rs @@ -0,0 +1,159 @@ +use pallas_codec::{ + minicbor::{self, bytes::ByteVec, Decode, Encode}, + utils::OrderPreservingProperties, +}; + +use pallas_crypto::hash::Hash; + +pub type Blake2b224 = Hash<28>; + +pub type AddressId = Blake2b224; +pub type StakeholderId = Blake2b224; + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +pub enum AddrDistr { + Variant0(StakeholderId), + Variant1, +} + +impl<'b, C> minicbor::Decode<'b, C> for AddrDistr { + fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result { + d.array()?; + let variant = d.u32()?; + + match variant { + 0 => Ok(AddrDistr::Variant0(d.decode_with(ctx)?)), + 1 => Ok(AddrDistr::Variant1), + _ => Err(minicbor::decode::Error::message( + "invalid variant for addrdstr", + )), + } + } +} + +impl minicbor::Encode<()> for AddrDistr { + fn encode( + &self, + e: &mut minicbor::Encoder, + _ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + match self { + AddrDistr::Variant0(x) => { + e.array(2)?; + e.u32(0)?; + e.encode(x)?; + + Ok(()) + } + AddrDistr::Variant1 => { + e.array(1)?; + e.u32(1)?; + + Ok(()) + } + } + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +pub enum AddrType { + PubKey, + Script, + Redeem, + Other(u64), +} + +impl<'b, C> minicbor::Decode<'b, C> for AddrType { + fn decode( + d: &mut minicbor::Decoder<'b>, + _ctx: &mut C, + ) -> Result { + let variant = d.u64()?; + + match variant { + 0 => Ok(AddrType::PubKey), + 1 => Ok(AddrType::Script), + 2 => Ok(AddrType::Redeem), + x => Ok(AddrType::Other(x)), + } + } +} + +impl minicbor::Encode for AddrType { + fn encode( + &self, + e: &mut minicbor::Encoder, + _ctx: &mut C, + ) -> Result<(), minicbor::encode::Error> { + match self { + AddrType::PubKey => e.u64(0)?, + AddrType::Script => e.u64(1)?, + AddrType::Redeem => e.u64(2)?, + AddrType::Other(x) => e.u64(*x)?, + }; + + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +pub enum AddrAttrProperty { + AddrDistr(AddrDistr), + Bytes(ByteVec), + Unparsed(u8, ByteVec), +} + +impl<'b, C> minicbor::Decode<'b, C> for AddrAttrProperty { + fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result { + let key = d.u8()?; + + match key { + 0 => Ok(AddrAttrProperty::AddrDistr(d.decode_with(ctx)?)), + 1 => Ok(AddrAttrProperty::Bytes(d.decode_with(ctx)?)), + x => Ok(AddrAttrProperty::Unparsed(x, d.decode_with(ctx)?)), + } + } +} + +impl minicbor::Encode for AddrAttrProperty { + fn encode( + &self, + e: &mut minicbor::Encoder, + _ctx: &mut C, + ) -> Result<(), minicbor::encode::Error> { + match self { + AddrAttrProperty::AddrDistr(x) => { + e.u32(0)?; + e.encode(x)?; + + Ok(()) + } + AddrAttrProperty::Bytes(x) => { + e.u32(1)?; + e.encode(x)?; + + Ok(()) + } + AddrAttrProperty::Unparsed(a, b) => { + e.encode(a)?; + e.encode(b)?; + + Ok(()) + } + } + } +} + +pub type AddrAttr = OrderPreservingProperties; + +#[derive(Debug, Encode, Decode, Clone, PartialEq, PartialOrd)] +pub struct AddressPayload { + #[n(0)] + pub root: AddressId, + + #[n(1)] + pub attributes: AddrAttr, + + #[n(2)] + pub addrtype: AddrType, +} diff --git a/pallas-addresses/src/lib.rs b/pallas-addresses/src/lib.rs index 0bc1d85..232a850 100644 --- a/pallas-addresses/src/lib.rs +++ b/pallas-addresses/src/lib.rs @@ -7,6 +7,7 @@ //! //! For more information regarding Cardano addresses and their formats, please refer to [CIP-19](https://cips.cardano.org/cips/cip19/). +pub mod byron; pub mod varuint; use std::io::Cursor; @@ -236,10 +237,10 @@ pub struct StakeAddress(Network, StakePayload); /// New type wrapping a Byron address primitive #[derive(Debug, Clone, PartialEq, PartialOrd)] -pub struct ByronAddress(pallas_primitives::byron::Address); +pub struct ByronAddress(byron::AddressPayload); impl ByronAddress { - pub fn new(primitive: pallas_primitives::byron::Address) -> Self { + pub fn new(primitive: byron::AddressPayload) -> Self { Self(primitive) } } diff --git a/pallas-codec/src/utils.rs b/pallas-codec/src/utils.rs index 89e0ef9..41face8 100644 --- a/pallas-codec/src/utils.rs +++ b/pallas-codec/src/utils.rs @@ -273,7 +273,7 @@ impl Deref for CborWrap { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct TagWrap(I); impl TagWrap { @@ -309,6 +309,14 @@ where } } +impl Deref for TagWrap { + type Target = I; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + /// An empty map /// /// don't ask me why, that's what the CDDL asks for. diff --git a/pallas-multiplexer/tests/integration.rs b/pallas-multiplexer/tests/integration.rs index 133db91..7bc6934 100644 --- a/pallas-multiplexer/tests/integration.rs +++ b/pallas-multiplexer/tests/integration.rs @@ -4,12 +4,7 @@ use std::{ }; use log::info; -use pallas_codec::minicbor; -use pallas_multiplexer::{ - agents::{Channel, ChannelBuffer}, - bearers::Bearer, - Payload, StdPlexer, -}; +use pallas_multiplexer::{agents::Channel, bearers::Bearer, StdPlexer}; use rand::{distributions::Uniform, Rng}; fn setup_passive_muxer() -> JoinHandle { diff --git a/pallas-primitives/src/alonzo/model.rs b/pallas-primitives/src/alonzo/model.rs index 7a32bfe..3a0eab0 100644 --- a/pallas-primitives/src/alonzo/model.rs +++ b/pallas-primitives/src/alonzo/model.rs @@ -280,6 +280,8 @@ pub struct MoveInstantaneousReward { pub type RewardAccount = ByteVec; +pub type Withdrawals = KeyValuePairs; + pub type Port = u32; pub type IPv4 = ByteVec; pub type IPv6 = ByteVec; @@ -719,7 +721,7 @@ pub struct TransactionBody { pub certificates: Option>, #[n(5)] - pub withdrawals: Option>, + pub withdrawals: Option, #[n(6)] pub update: Option, diff --git a/pallas-primitives/src/byron/model.rs b/pallas-primitives/src/byron/model.rs index 2c105b5..3e201e6 100644 --- a/pallas-primitives/src/byron/model.rs +++ b/pallas-primitives/src/byron/model.rs @@ -6,8 +6,7 @@ use pallas_codec::minicbor::{bytes::ByteVec, Decode, Encode}; use pallas_crypto::hash::Hash; use pallas_codec::utils::{ - CborWrap, EmptyMap, KeepRaw, KeyValuePairs, MaybeIndefArray, OrderPreservingProperties, - TagWrap, ZeroOrOneArray, + CborWrap, EmptyMap, KeepRaw, KeyValuePairs, MaybeIndefArray, TagWrap, ZeroOrOneArray, }; // required for derive attrs to work @@ -49,161 +48,14 @@ pub type Signature = ByteVec; // attributes = {* any => any} pub type Attributes = EmptyMap; -// Addresses - -#[derive(Debug, Clone, PartialEq, PartialOrd)] -pub enum AddrDistr { - Variant0(StakeholderId), - Variant1, -} - -impl<'b, C> minicbor::Decode<'b, C> for AddrDistr { - fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result { - d.array()?; - let variant = d.u32()?; - - match variant { - 0 => Ok(AddrDistr::Variant0(d.decode_with(ctx)?)), - 1 => Ok(AddrDistr::Variant1), - _ => Err(minicbor::decode::Error::message( - "invalid variant for addrdstr", - )), - } - } -} - -impl minicbor::Encode<()> for AddrDistr { - fn encode( - &self, - e: &mut minicbor::Encoder, - _ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - match self { - AddrDistr::Variant0(x) => { - e.array(2)?; - e.u32(0)?; - e.encode(x)?; - - Ok(()) - } - AddrDistr::Variant1 => { - e.array(1)?; - e.u32(1)?; - - Ok(()) - } - } - } -} - -#[derive(Debug, Clone, PartialEq, PartialOrd)] -pub enum AddrType { - PubKey, - Script, - Redeem, - Other(u64), -} - -impl<'b, C> minicbor::Decode<'b, C> for AddrType { - fn decode( - d: &mut minicbor::Decoder<'b>, - _ctx: &mut C, - ) -> Result { - let variant = d.u64()?; - - match variant { - 0 => Ok(AddrType::PubKey), - 1 => Ok(AddrType::Script), - 2 => Ok(AddrType::Redeem), - x => Ok(AddrType::Other(x)), - } - } -} - -impl minicbor::Encode for AddrType { - fn encode( - &self, - e: &mut minicbor::Encoder, - _ctx: &mut C, - ) -> Result<(), minicbor::encode::Error> { - match self { - AddrType::PubKey => e.u64(0)?, - AddrType::Script => e.u64(1)?, - AddrType::Redeem => e.u64(2)?, - AddrType::Other(x) => e.u64(*x)?, - }; - - Ok(()) - } -} - -#[derive(Debug, Clone, PartialEq, PartialOrd)] -pub enum AddrAttrProperty { - AddrDistr(AddrDistr), - Bytes(ByteVec), - Unparsed(u8, ByteVec), -} - -impl<'b, C> minicbor::Decode<'b, C> for AddrAttrProperty { - fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result { - let key = d.u8()?; - - match key { - 0 => Ok(AddrAttrProperty::AddrDistr(d.decode_with(ctx)?)), - 1 => Ok(AddrAttrProperty::Bytes(d.decode_with(ctx)?)), - x => Ok(AddrAttrProperty::Unparsed(x, d.decode_with(ctx)?)), - } - } -} - -impl minicbor::Encode for AddrAttrProperty { - fn encode( - &self, - e: &mut minicbor::Encoder, - _ctx: &mut C, - ) -> Result<(), minicbor::encode::Error> { - match self { - AddrAttrProperty::AddrDistr(x) => { - e.u32(0)?; - e.encode(x)?; - - Ok(()) - } - AddrAttrProperty::Bytes(x) => { - e.u32(1)?; - e.encode(x)?; - - Ok(()) - } - AddrAttrProperty::Unparsed(a, b) => { - e.encode(a)?; - e.encode(b)?; - - Ok(()) - } - } - } -} - -pub type AddrAttr = OrderPreservingProperties; - -#[derive(Debug, Encode, Decode, Clone, PartialEq, PartialOrd)] -pub struct AddressPayload { - #[n(0)] - pub root: AddressId, - - #[n(1)] - pub attributes: AddrAttr, - - #[n(2)] - pub addrtype: AddrType, -} +// The cbor struct of the address payload is now defined in pallas-addresses. +// The primitives crate will treat addresses as a black-box vec of bytes. // address = [ #6.24(bytes .cbor ([addressid, addrattr, addrtype])), u64 ] #[derive(Debug, Encode, Decode, Clone, PartialEq, PartialOrd)] pub struct Address { #[n(0)] - pub payload: CborWrap, + pub payload: TagWrap, #[n(1)] pub crc: u64, @@ -954,6 +806,18 @@ pub struct EbBlock { pub extra: MaybeIndefArray, } +#[derive(Encode, Decode, Debug, Clone)] +pub struct MintedEbBlock<'b> { + #[b(0)] + pub header: KeepRaw<'b, EbbHead>, + + #[n(1)] + pub body: MaybeIndefArray, + + #[n(2)] + pub extra: MaybeIndefArray, +} + #[cfg(test)] mod tests { use super::{BlockHead, EbBlock, MintedBlock}; diff --git a/pallas-traverse/src/block.rs b/pallas-traverse/src/block.rs index 2f31de8..bf89c94 100644 --- a/pallas-traverse/src/block.rs +++ b/pallas-traverse/src/block.rs @@ -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 = (u16, T); impl<'b> MultiEraBlock<'b> { pub fn decode_epoch_boundary(cbor: &'b [u8]) -> Result { - let (_, block): BlockWrapper = + let (_, block): BlockWrapper = 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 { 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, diff --git a/pallas-traverse/src/cert.rs b/pallas-traverse/src/cert.rs index 555e3ca..de3585c 100644 --- a/pallas-traverse/src/cert.rs +++ b/pallas-traverse/src/cert.rs @@ -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, } } } diff --git a/pallas-traverse/src/header.rs b/pallas-traverse/src/header.rs index 0e4dbb3..85b85de 100644 --- a/pallas-traverse/src/header.rs +++ b/pallas-traverse/src/header.rs @@ -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 diff --git a/pallas-traverse/src/input.rs b/pallas-traverse/src/input.rs index 7a06743..d7272e1 100644 --- a/pallas-traverse/src/input.rs +++ b/pallas-traverse/src/input.rs @@ -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 { + 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); } } } diff --git a/pallas-traverse/src/lib.rs b/pallas-traverse/src/lib.rs index 28279b5..33fab3f 100644 --- a/pallas-traverse/src/lib.rs +++ b/pallas-traverse/src/lib.rs @@ -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>), + EpochBoundary(Box>>), AlonzoCompatible(Box>>, Era), Babbage(Box>>), Byron(Box>>), @@ -83,12 +85,36 @@ pub enum MultiEraInput<'b> { AlonzoCompatible(Box>), } +#[derive(Debug, Clone)] +#[non_exhaustive] pub enum MultiEraCert<'b> { NotApplicable, AlonzoCompatible(Box>), } -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); diff --git a/pallas-traverse/src/meta.rs b/pallas-traverse/src/meta.rs index 18701bd..e87a423 100644 --- a/pallas-traverse/src/meta.rs +++ b/pallas-traverse/src/meta.rs @@ -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) + } + } + } } diff --git a/pallas-traverse/src/mint.rs b/pallas-traverse/src/mint.rs new file mode 100644 index 0000000..233e8e9 --- /dev/null +++ b/pallas-traverse/src/mint.rs @@ -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, + } + } +} diff --git a/pallas-traverse/src/output.rs b/pallas-traverse/src/output.rs index dd33d64..6261f65 100644 --- a/pallas-traverse/src/output.rs +++ b/pallas-traverse/src/output.rs @@ -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 { + 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::from_bytes(self.address_raw()) + } + pub fn ada_amount(&self) -> u64 { match self { MultiEraOutput::Byron(x) => x.amount, diff --git a/pallas-traverse/src/tx.rs b/pallas-traverse/src/tx.rs index d713591..20e4dc0 100644 --- a/pallas-traverse/src/tx.rs +++ b/pallas-traverse/src/tx.rs @@ -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 { + 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 { - 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, } } diff --git a/pallas-traverse/src/withdrawals.rs b/pallas-traverse/src/withdrawals.rs new file mode 100644 index 0000000..4afbee1 --- /dev/null +++ b/pallas-traverse/src/withdrawals.rs @@ -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) + } + } + } +}