From 7539e17dfca9fa448c6717be5d62c135c5e7fbbf Mon Sep 17 00:00:00 2001 From: Santiago Carmuega Date: Sun, 4 Jun 2023 01:46:41 +0200 Subject: [PATCH] feat(traverse): improve native asset access (#259) --- pallas-traverse/src/assets.rs | 111 +++++++++++++++++++++------------- pallas-traverse/src/lib.rs | 19 +++++- pallas-traverse/src/meta.rs | 4 +- pallas-traverse/src/output.rs | 37 +++++------- pallas-traverse/src/tx.rs | 27 ++++++--- 5 files changed, 118 insertions(+), 80 deletions(-) diff --git a/pallas-traverse/src/assets.rs b/pallas-traverse/src/assets.rs index 006b106..37df086 100644 --- a/pallas-traverse/src/assets.rs +++ b/pallas-traverse/src/assets.rs @@ -1,70 +1,95 @@ use pallas_crypto::hash::Hash; -use pallas_primitives::alonzo; -use crate::MultiEraAsset; +use crate::{MultiEraAsset, MultiEraPolicyAssets}; + +impl<'b> MultiEraPolicyAssets<'b> { + pub fn policy(&self) -> &Hash<28> { + match self { + MultiEraPolicyAssets::AlonzoCompatibleMint(x, _) => x, + MultiEraPolicyAssets::AlonzoCompatibleOutput(x, _) => x, + } + } + + pub fn is_output(&self) -> bool { + match self { + MultiEraPolicyAssets::AlonzoCompatibleOutput(_, _) => true, + MultiEraPolicyAssets::AlonzoCompatibleMint(_, _) => false, + } + } + + pub fn is_mint(&self) -> bool { + match self { + MultiEraPolicyAssets::AlonzoCompatibleMint(_, _) => true, + MultiEraPolicyAssets::AlonzoCompatibleOutput(_, _) => false, + } + } + + pub fn assets(&self) -> Vec { + match self { + MultiEraPolicyAssets::AlonzoCompatibleMint(p, x) => x + .iter() + .map(|(k, v)| MultiEraAsset::AlonzoCompatibleMint(p, k, *v)) + .collect(), + MultiEraPolicyAssets::AlonzoCompatibleOutput(p, x) => x + .iter() + .map(|(k, v)| MultiEraAsset::AlonzoCompatibleOutput(p, k, *v)) + .collect(), + } + } + + pub fn collect<'a, T>(&'a self) -> T + where + T: FromIterator<(&'a [u8], i128)>, + { + match self { + MultiEraPolicyAssets::AlonzoCompatibleMint(_, x) => { + x.iter().map(|(k, v)| (k.as_slice(), *v as i128)).collect() + } + MultiEraPolicyAssets::AlonzoCompatibleOutput(_, x) => { + x.iter().map(|(k, v)| (k.as_slice(), *v as i128)).collect() + } + } + } +} impl<'b> MultiEraAsset<'b> { - pub fn collect_alonzo_compatible_output(source: &'b alonzo::Multiasset) -> Vec { - source - .iter() - .flat_map(|(policy, assets)| { - assets.iter().map(|(name, amount)| { - MultiEraAsset::AlonzoCompatible(policy, name, *amount as i64) - }) - }) - .collect::>() - } - - pub fn collect_alonzo_compatible_mint(source: &'b alonzo::Multiasset) -> Vec { - source - .iter() - .flat_map(|(policy, assets)| { - assets - .iter() - .map(|(name, amount)| MultiEraAsset::AlonzoCompatible(policy, name, *amount)) - }) - .collect::>() - } - - pub fn policy(&self) -> Option<&Hash<28>> { + pub fn policy(&self) -> &Hash<28> { match self { - Self::AlonzoCompatible(x, ..) => Some(*x), - Self::Lovelace(_) => None, + MultiEraAsset::AlonzoCompatibleMint(x, ..) => x, + MultiEraAsset::AlonzoCompatibleOutput(x, ..) => x, } } - pub fn name(&self) -> Option<&[u8]> { + pub fn name(&self) -> &[u8] { match self { - Self::AlonzoCompatible(_, n, _) => Some(n.as_ref()), - Self::Lovelace(_) => None, + MultiEraAsset::AlonzoCompatibleOutput(_, x, _) => x, + MultiEraAsset::AlonzoCompatibleMint(_, x, _) => x, } } - pub fn coin(&self) -> i64 { + pub fn is_output(&self) -> bool { match self { - Self::AlonzoCompatible(_, _, x) => *x, - Self::Lovelace(x) => *x as i64, + MultiEraAsset::AlonzoCompatibleOutput(..) => true, + MultiEraAsset::AlonzoCompatibleMint(..) => false, } } - pub fn as_alonzo(&self) -> Option<(&alonzo::PolicyId, &alonzo::AssetName, i64)> { + pub fn is_mint(&self) -> bool { match self { - Self::AlonzoCompatible(a, b, c) => Some((*a, *b, *c)), - _ => None, + MultiEraAsset::AlonzoCompatibleMint(..) => true, + MultiEraAsset::AlonzoCompatibleOutput(..) => false, } } - pub fn to_subject(&self) -> Option { + pub fn coin(&self) -> i128 { match self { - Self::AlonzoCompatible(p, n, _) => Some(format!("{p}.{}", hex::encode(n.to_vec()))), - _ => None, + MultiEraAsset::AlonzoCompatibleOutput(_, _, x) => *x as i128, + MultiEraAsset::AlonzoCompatibleMint(_, _, x) => *x as i128, } } pub fn to_ascii_name(&self) -> Option { - match self { - Self::AlonzoCompatible(_, n, _) => String::from_utf8(n.to_vec()).ok(), - _ => None, - } + let name = self.name(); + String::from_utf8(name.to_vec()).ok() } } diff --git a/pallas-traverse/src/lib.rs b/pallas-traverse/src/lib.rs index 328b0bf..20c9a01 100644 --- a/pallas-traverse/src/lib.rs +++ b/pallas-traverse/src/lib.rs @@ -5,7 +5,7 @@ use std::fmt::Display; use thiserror::Error; -use pallas_codec::utils::KeepRaw; +use pallas_codec::utils::{KeepRaw, KeyValuePairs}; use pallas_crypto::hash::Hash; use pallas_primitives::{alonzo, babbage, byron}; @@ -112,11 +112,24 @@ pub enum MultiEraMeta<'b> { AlonzoCompatible(&'b alonzo::Metadata), } +#[derive(Debug, Clone)] +#[non_exhaustive] +pub enum MultiEraPolicyAssets<'b> { + AlonzoCompatibleMint( + &'b alonzo::PolicyId, + &'b KeyValuePairs, + ), + AlonzoCompatibleOutput( + &'b alonzo::PolicyId, + &'b KeyValuePairs, + ), +} + #[derive(Debug, Clone)] #[non_exhaustive] pub enum MultiEraAsset<'b> { - Lovelace(u64), - AlonzoCompatible(&'b alonzo::PolicyId, &'b alonzo::AssetName, i64), + AlonzoCompatibleOutput(&'b alonzo::PolicyId, &'b alonzo::AssetName, u64), + AlonzoCompatibleMint(&'b alonzo::PolicyId, &'b alonzo::AssetName, i64), } #[derive(Debug, Clone)] diff --git a/pallas-traverse/src/meta.rs b/pallas-traverse/src/meta.rs index 574b6d9..9f0101b 100644 --- a/pallas-traverse/src/meta.rs +++ b/pallas-traverse/src/meta.rs @@ -25,12 +25,12 @@ impl<'b> MultiEraMeta<'b> { pub fn collect<'a, T>(&'a self) -> T where - T: FromIterator<(&'a alonzo::MetadatumLabel, &'a alonzo::Metadatum)>, + T: FromIterator<(alonzo::MetadatumLabel, &'a alonzo::Metadatum)>, { match self { MultiEraMeta::NotApplicable => std::iter::empty().collect(), MultiEraMeta::Empty => std::iter::empty().collect(), - MultiEraMeta::AlonzoCompatible(x) => x.iter().map(|(k, v)| (k, v)).collect(), + MultiEraMeta::AlonzoCompatible(x) => x.iter().map(|(k, v)| (*k, v)).collect(), } } } diff --git a/pallas-traverse/src/output.rs b/pallas-traverse/src/output.rs index 66553fa..3e609b5 100644 --- a/pallas-traverse/src/output.rs +++ b/pallas-traverse/src/output.rs @@ -4,7 +4,7 @@ use pallas_addresses::{Address, ByronAddress, Error as AddressError}; use pallas_codec::minicbor; use pallas_primitives::{alonzo, babbage, byron}; -use crate::{Era, MultiEraAsset, MultiEraOutput}; +use crate::{Era, MultiEraOutput, MultiEraPolicyAssets}; impl<'b> MultiEraOutput<'b> { pub fn from_byron(output: &'b byron::TxOut) -> Self { @@ -140,41 +140,32 @@ impl<'b> MultiEraOutput<'b> { /// Returns a list of Asset structs where each one represent a native asset /// present in the output of the tx. ADA assets are not included in this /// list. - pub fn non_ada_assets(&self) -> Vec { + pub fn non_ada_assets(&self) -> Vec { match self { MultiEraOutput::Byron(_) => vec![], MultiEraOutput::Babbage(x) => match x.deref().deref() { babbage::MintedTransactionOutput::Legacy(x) => match &x.amount { babbage::Value::Coin(_) => vec![], - babbage::Value::Multiasset(_, x) => { - MultiEraAsset::collect_alonzo_compatible_output(x) - } + babbage::Value::Multiasset(_, x) => x + .iter() + .map(|(k, v)| MultiEraPolicyAssets::AlonzoCompatibleOutput(k, v)) + .collect(), }, babbage::MintedTransactionOutput::PostAlonzo(x) => match &x.value { babbage::Value::Coin(_) => vec![], - babbage::Value::Multiasset(_, x) => { - MultiEraAsset::collect_alonzo_compatible_output(x) - } + babbage::Value::Multiasset(_, x) => x + .iter() + .map(|(k, v)| MultiEraPolicyAssets::AlonzoCompatibleOutput(k, v)) + .collect(), }, }, MultiEraOutput::AlonzoCompatible(x) => match &x.amount { alonzo::Value::Coin(_) => vec![], - alonzo::Value::Multiasset(_, x) => { - MultiEraAsset::collect_alonzo_compatible_output(x) - } + alonzo::Value::Multiasset(_, x) => x + .iter() + .map(|(k, v)| MultiEraPolicyAssets::AlonzoCompatibleOutput(k, v)) + .collect(), }, } } - - /// List of all assets in the output - /// - /// Returns a list of Asset structs where each one represent either ADA or a - /// native asset present in the output of the tx. - pub fn assets(&self) -> Vec { - [ - vec![MultiEraAsset::Lovelace(self.lovelace_amount())], - self.non_ada_assets(), - ] - .concat() - } } diff --git a/pallas-traverse/src/tx.rs b/pallas-traverse/src/tx.rs index 91374e6..b8211f5 100644 --- a/pallas-traverse/src/tx.rs +++ b/pallas-traverse/src/tx.rs @@ -9,8 +9,8 @@ use pallas_primitives::{ }; use crate::{ - Era, MultiEraAsset, MultiEraCert, MultiEraInput, MultiEraMeta, MultiEraOutput, MultiEraSigners, - MultiEraTx, MultiEraWithdrawals, OriginalHash, + Era, MultiEraCert, MultiEraInput, MultiEraMeta, MultiEraOutput, MultiEraPolicyAssets, + MultiEraSigners, MultiEraTx, MultiEraWithdrawals, OriginalHash, }; impl<'b> MultiEraTx<'b> { @@ -182,20 +182,22 @@ impl<'b> MultiEraTx<'b> { } } - pub fn mints(&self) -> Vec { + pub fn mints(&self) -> Vec { match self { MultiEraTx::AlonzoCompatible(x, _) => x .transaction_body .mint - .as_ref() - .map(MultiEraAsset::collect_alonzo_compatible_mint) - .unwrap_or_default(), + .iter() + .flat_map(|x| x.iter()) + .map(|(k, v)| MultiEraPolicyAssets::AlonzoCompatibleMint(k, v)) + .collect(), MultiEraTx::Babbage(x) => x .transaction_body .mint - .as_ref() - .map(MultiEraAsset::collect_alonzo_compatible_mint) - .unwrap_or_default(), + .iter() + .flat_map(|x| x.iter()) + .map(|(k, v)| MultiEraPolicyAssets::AlonzoCompatibleMint(k, v)) + .collect(), MultiEraTx::Byron(_) => vec![], } } @@ -235,6 +237,13 @@ impl<'b> MultiEraTx<'b> { } } + pub fn total_collateral(&self) -> Option { + match self { + MultiEraTx::Babbage(x) => x.transaction_body.total_collateral.clone(), + _ => None, + } + } + /// Returns the list of inputs consumed by the Tx /// /// Helper method to abstract the logic of which inputs are consumed