diff --git a/pallas-traverse/src/block.rs b/pallas-traverse/src/block.rs index a7f4f2e7..c77721a8 100644 --- a/pallas-traverse/src/block.rs +++ b/pallas-traverse/src/block.rs @@ -103,9 +103,9 @@ impl<'b> MultiEraBlock<'b> { pub fn txs(&self) -> Vec { match self { - MultiEraBlock::AlonzoCompatible(x, _) => support::clone_alonzo_txs(x) + MultiEraBlock::AlonzoCompatible(x, era) => support::clone_alonzo_txs(x) .into_iter() - .map(|x| MultiEraTx::AlonzoCompatible(Box::new(Cow::Owned(x)))) + .map(|x| MultiEraTx::AlonzoCompatible(Box::new(Cow::Owned(x)), *era)) .collect(), MultiEraBlock::Byron(x) => support::clone_byron_txs(x) .into_iter() diff --git a/pallas-traverse/src/era.rs b/pallas-traverse/src/era.rs index aa5595b5..7348dc80 100644 --- a/pallas-traverse/src/era.rs +++ b/pallas-traverse/src/era.rs @@ -15,3 +15,33 @@ impl Era { } } } + +// for consistency, we use the same tag convention used by the node's cbor +// encoding +impl TryFrom for Era { + type Error = crate::Error; + + fn try_from(value: u16) -> Result { + match value { + 0 => Ok(Era::Byron), + 1 => Ok(Era::Byron), + 2 => Ok(Era::Shelley), + 3 => Ok(Era::Allegra), + 4 => Ok(Era::Mary), + 5 => Ok(Era::Alonzo), + x => Err(crate::Error::UnkownEra(x)), + } + } +} + +impl From for u16 { + fn from(other: Era) -> Self { + match other { + Era::Byron => 1, + Era::Shelley => 2, + Era::Allegra => 3, + Era::Mary => 4, + Era::Alonzo => 5, + } + } +} diff --git a/pallas-traverse/src/header.rs b/pallas-traverse/src/header.rs new file mode 100644 index 00000000..5e505f39 --- /dev/null +++ b/pallas-traverse/src/header.rs @@ -0,0 +1,42 @@ +use pallas_codec::minicbor; +use pallas_crypto::hash::Hash; +use pallas_primitives::ToHash; + +use crate::{Error, MultiEraHeader}; + +impl<'b> MultiEraHeader<'b> { + pub fn decode(tag: u8, subtag: Option, cbor: &'b [u8]) -> Result { + match tag { + 0 => match subtag { + Some(0) => { + let header = minicbor::decode(cbor).map_err(Error::invalid_cbor)?; + Ok(MultiEraHeader::EpochBoundary(header)) + } + _ => { + let header = minicbor::decode(cbor).map_err(Error::invalid_cbor)?; + Ok(MultiEraHeader::Byron(header)) + } + }, + _ => { + let header = minicbor::decode(cbor).map_err(Error::invalid_cbor)?; + Ok(MultiEraHeader::AlonzoCompatible(header)) + } + } + } + + pub fn slot(&self) -> u64 { + match self { + MultiEraHeader::EpochBoundary(x) => x.to_abs_slot(), + MultiEraHeader::AlonzoCompatible(x) => x.header_body.slot, + MultiEraHeader::Byron(x) => x.consensus_data.0.to_abs_slot(), + } + } + + pub fn hash(&self) -> Hash<32> { + match self { + MultiEraHeader::EpochBoundary(x) => x.to_hash(), + MultiEraHeader::AlonzoCompatible(x) => x.to_hash(), + MultiEraHeader::Byron(x) => x.to_hash(), + } + } +} diff --git a/pallas-traverse/src/lib.rs b/pallas-traverse/src/lib.rs index 58a9cc74..2c707d1d 100644 --- a/pallas-traverse/src/lib.rs +++ b/pallas-traverse/src/lib.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::fmt::Display; +use pallas_codec::utils::KeepRaw; use pallas_crypto::hash::Hash; use pallas_primitives::{alonzo, byron}; use thiserror::Error; @@ -10,6 +11,7 @@ use thiserror::Error; pub mod block; pub mod cert; pub mod era; +pub mod header; pub mod input; pub mod output; pub mod probe; @@ -35,6 +37,13 @@ pub enum Feature { SmartContracts, } +#[derive(Debug)] +pub enum MultiEraHeader<'b> { + EpochBoundary(KeepRaw<'b, byron::EbbHead>), + AlonzoCompatible(KeepRaw<'b, alonzo::Header>), + Byron(KeepRaw<'b, byron::BlockHead>), +} + #[derive(Debug, Clone)] #[non_exhaustive] pub enum MultiEraBlock<'b> { @@ -46,7 +55,7 @@ pub enum MultiEraBlock<'b> { #[derive(Debug)] #[non_exhaustive] pub enum MultiEraTx<'b> { - AlonzoCompatible(Box>>), + AlonzoCompatible(Box>>, Era), Byron(Box>>), } @@ -78,6 +87,9 @@ pub enum Error { #[error("Unknown CBOR structure: {0}")] UnknownCbor(String), + + #[error("Unknown era tag: {0}")] + UnkownEra(u16), } impl Error { diff --git a/pallas-traverse/src/tx.rs b/pallas-traverse/src/tx.rs index d7e37df3..9774bdbf 100644 --- a/pallas-traverse/src/tx.rs +++ b/pallas-traverse/src/tx.rs @@ -3,34 +3,56 @@ use pallas_crypto::hash::Hash; use pallas_primitives::{alonzo, byron, ToHash}; use std::borrow::Cow; -use crate::{MultiEraCert, MultiEraInput, MultiEraOutput, MultiEraTx}; +use crate::{Era, MultiEraCert, MultiEraInput, MultiEraOutput, MultiEraTx}; impl<'b> MultiEraTx<'b> { pub fn from_byron(tx: &'b byron::MintedTxPayload<'b>) -> Self { Self::Byron(Box::new(Cow::Borrowed(tx))) } - pub fn from_alonzo_compatible(tx: &'b alonzo::MintedTx<'b>) -> Self { - Self::AlonzoCompatible(Box::new(Cow::Borrowed(tx))) + pub fn from_alonzo_compatible(tx: &'b alonzo::MintedTx<'b>, era: Era) -> Self { + Self::AlonzoCompatible(Box::new(Cow::Borrowed(tx)), era) } pub fn encode(&self) -> Result, minicbor::encode::Error> { match self { - MultiEraTx::AlonzoCompatible(x) => minicbor::to_vec(x), + MultiEraTx::AlonzoCompatible(x, _) => minicbor::to_vec(x), MultiEraTx::Byron(x) => minicbor::to_vec(x), } } + pub fn decode(era: Era, cbor: &'b [u8]) -> Result { + match era { + Era::Byron => { + let tx = minicbor::decode(cbor)?; + let tx = Box::new(Cow::Owned(tx)); + Ok(MultiEraTx::Byron(tx)) + } + Era::Shelley | Era::Allegra | Era::Mary | Era::Alonzo => { + let tx = minicbor::decode(cbor)?; + let tx = Box::new(Cow::Owned(tx)); + Ok(MultiEraTx::AlonzoCompatible(tx, era)) + } + } + } + + pub fn era(&self) -> Era { + match self { + MultiEraTx::AlonzoCompatible(_, era) => *era, + MultiEraTx::Byron(_) => Era::Byron, + } + } + pub fn hash(&self) -> Hash<32> { match self { - MultiEraTx::AlonzoCompatible(x) => x.transaction_body.to_hash(), + MultiEraTx::AlonzoCompatible(x, _) => x.transaction_body.to_hash(), MultiEraTx::Byron(x) => x.transaction.to_hash(), } } pub fn outputs(&self) -> Vec { match self { - MultiEraTx::AlonzoCompatible(x) => x + MultiEraTx::AlonzoCompatible(x, _) => x .transaction_body .outputs .iter() @@ -47,7 +69,7 @@ impl<'b> MultiEraTx<'b> { pub fn inputs(&self) -> Vec { match self { - MultiEraTx::AlonzoCompatible(x) => x + MultiEraTx::AlonzoCompatible(x, _) => x .transaction_body .inputs .iter() @@ -65,7 +87,7 @@ impl<'b> MultiEraTx<'b> { pub fn certs(&self) -> Vec { match self { - MultiEraTx::AlonzoCompatible(x) => x + MultiEraTx::AlonzoCompatible(x, _) => x .transaction_body .certificates .iter() @@ -78,14 +100,14 @@ impl<'b> MultiEraTx<'b> { pub fn as_alonzo(&self) -> Option<&alonzo::MintedTx> { match self { - MultiEraTx::AlonzoCompatible(x) => Some(x), + MultiEraTx::AlonzoCompatible(x, _) => Some(x), MultiEraTx::Byron(_) => None, } } pub fn as_byron(&self) -> Option<&byron::MintedTxPayload> { match self { - MultiEraTx::AlonzoCompatible(_) => None, + MultiEraTx::AlonzoCompatible(_, _) => None, MultiEraTx::Byron(x) => Some(x), } }