diff --git a/pallas-traverse/src/input.rs b/pallas-traverse/src/input.rs index be87549..7a06743 100644 --- a/pallas-traverse/src/input.rs +++ b/pallas-traverse/src/input.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt::Display, ops::Deref}; +use std::{borrow::Cow, fmt::Display, ops::Deref, str::FromStr}; use pallas_codec::utils::CborWrap; use pallas_crypto::hash::Hash; @@ -6,7 +6,11 @@ use pallas_primitives::{alonzo, byron}; use crate::{MultiEraInput, OutputRef}; -impl OutputRef<'_> { +impl OutputRef { + pub fn new(hash: Hash<32>, index: u64) -> Self { + Self(hash, index) + } + pub fn tx_id(&self) -> &Hash<32> { &self.0 } @@ -16,12 +20,29 @@ impl OutputRef<'_> { } } -impl Display for OutputRef<'_> { +impl Display for OutputRef { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}#{}", self.tx_id(), self.tx_index()) } } +impl FromStr for OutputRef { + type Err = crate::Error; + + fn from_str(s: &str) -> Result { + let parts: Vec<_> = s.trim().split('#').collect(); + let (hash, idx) = match &parts[..] { + &[a, b] => ( + Hash::<32>::from_str(a).map_err(|_| crate::Error::invalid_utxo_ref(s))?, + u64::from_str(b).map_err(|_| crate::Error::invalid_utxo_ref(s))?, + ), + _ => return Err(crate::Error::invalid_utxo_ref(s)), + }; + + Ok(Self::new(hash, idx)) + } +} + impl<'b> MultiEraInput<'b> { pub fn from_byron(input: &'b byron::TxIn) -> Self { Self::Byron(Box::new(Cow::Borrowed(input))) @@ -34,14 +55,10 @@ impl<'b> MultiEraInput<'b> { pub fn output_ref(&self) -> Option { match self { MultiEraInput::Byron(x) => match x.deref().deref() { - byron::TxIn::Variant0(CborWrap((tx, idx))) => { - Some(OutputRef(Cow::Borrowed(tx), *idx as u64)) - } + byron::TxIn::Variant0(CborWrap((tx, idx))) => Some(OutputRef(*tx, *idx as u64)), byron::TxIn::Other(_, _) => None, }, - MultiEraInput::AlonzoCompatible(x) => { - Some(OutputRef(Cow::Borrowed(&x.transaction_id), x.index)) - } + MultiEraInput::AlonzoCompatible(x) => Some(OutputRef(x.transaction_id, x.index)), } } @@ -62,6 +79,8 @@ impl<'b> MultiEraInput<'b> { #[cfg(test)] mod tests { + use std::str::FromStr; + use crate::*; #[test] @@ -99,4 +118,22 @@ mod tests { } } } + + #[test] + fn test_utxo_ref_parsing() { + let valid_vectors = [ + "da832fb5ef57df5b91817e9a7448d26e92552afb34f8ee5adb491b24bbe990d5#14", + " da832fb5ef57df5b91817e9a7448d26e92552afb34f8ee5adb491b24bbe990d5#14 ", + ]; + + for vector in valid_vectors.iter() { + let sample = OutputRef::from_str(vector).unwrap(); + + assert_eq!( + sample.tx_id().to_string(), + "da832fb5ef57df5b91817e9a7448d26e92552afb34f8ee5adb491b24bbe990d5" + ); + assert_eq!(sample.tx_index(), 14); + } + } } diff --git a/pallas-traverse/src/lib.rs b/pallas-traverse/src/lib.rs index 0b31957..699ac9a 100644 --- a/pallas-traverse/src/lib.rs +++ b/pallas-traverse/src/lib.rs @@ -67,7 +67,7 @@ pub enum MultiEraTx<'b> { Byron(Box>>), } -#[derive(Debug)] +#[derive(Debug, Clone)] #[non_exhaustive] pub enum MultiEraOutput<'b> { AlonzoCompatible(Box>), @@ -75,7 +75,7 @@ pub enum MultiEraOutput<'b> { Byron(Box>), } -#[derive(Debug)] +#[derive(Debug, Clone)] #[non_exhaustive] pub enum MultiEraInput<'b> { Byron(Box>), @@ -87,7 +87,8 @@ pub enum MultiEraCert<'b> { AlonzoCompatible(Box>), } -pub struct OutputRef<'a>(Cow<'a, Hash<32>>, u64); +#[derive(Debug, Clone)] +pub struct OutputRef(Hash<32>, u64); #[derive(Debug, Error)] pub enum Error { @@ -102,6 +103,9 @@ pub enum Error { #[error("Invalid era for request: {0}")] InvalidEra(Era), + + #[error("Invalid UTxO ref: {0}")] + InvalidUtxoRef(String), } impl Error { @@ -112,4 +116,8 @@ impl Error { pub fn unknown_cbor(bytes: &[u8]) -> Self { Error::UnknownCbor(hex::encode(bytes)) } + + pub fn invalid_utxo_ref(str: &str) -> Self { + Error::InvalidUtxoRef(str.to_owned()) + } } diff --git a/pallas-traverse/src/output.rs b/pallas-traverse/src/output.rs index 6f6737e..7c42197 100644 --- a/pallas-traverse/src/output.rs +++ b/pallas-traverse/src/output.rs @@ -1,8 +1,9 @@ use std::{borrow::Cow, ops::Deref}; +use pallas_codec::minicbor; use pallas_primitives::{alonzo, babbage, byron}; -use crate::MultiEraOutput; +use crate::{Era, MultiEraOutput}; impl<'b> MultiEraOutput<'b> { pub fn from_byron(output: &'b byron::TxOut) -> Self { @@ -70,4 +71,32 @@ impl<'b> MultiEraOutput<'b> { MultiEraOutput::Byron(x) => Some(x), } } + + pub fn encode(&self) -> Result, minicbor::encode::Error> { + match self { + Self::AlonzoCompatible(x) => minicbor::to_vec(x), + Self::Babbage(x) => minicbor::to_vec(x), + Self::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(Self::Byron(tx)) + } + Era::Shelley | Era::Allegra | Era::Mary | Era::Alonzo => { + let tx = minicbor::decode(cbor)?; + let tx = Box::new(Cow::Owned(tx)); + Ok(Self::AlonzoCompatible(tx)) + } + Era::Babbage => { + let tx = minicbor::decode(cbor)?; + let tx = Box::new(Cow::Owned(tx)); + Ok(Self::Babbage(tx)) + } + } + } }