Skip to content

Commit

Permalink
feat(traverse): Add helper methods to Asset data (#195)
Browse files Browse the repository at this point in the history
  • Loading branch information
scarmuega authored Sep 28, 2022
1 parent 2f7bfc8 commit 47a5c52
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 135 deletions.
110 changes: 110 additions & 0 deletions pallas-traverse/src/assets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use std::ops::Deref;

use pallas_codec::utils::{Bytes, KeyValuePairs};
use pallas_crypto::hash::Hash;
use pallas_primitives::{alonzo, babbage};

use crate::{Asset, MultiEraOutput};

fn iter_policy_assets<'b>(
policy: &'b Hash<28>,
assets: &'b KeyValuePairs<Bytes, u64>,
) -> impl Iterator<Item = Asset> + 'b {
assets
.iter()
.map(|(name, amount)| Asset::NativeAsset(*policy, Vec::<u8>::clone(name), *amount))
}

fn collect_multiassets(multiassets: &alonzo::Multiasset<alonzo::Coin>) -> Vec<Asset> {
multiassets
.iter()
.flat_map(|(p, a)| iter_policy_assets(p, a))
.collect::<Vec<_>>()
}

impl Asset {
pub fn subject(&self) -> String {
match self {
Self::Ada(_) => String::from("ada"),
Self::NativeAsset(p, n, _) => format!("{p}.{}", hex::encode(n)),
}
}

pub fn ascii_name(&self) -> Option<String> {
match self {
Self::Ada(_) => None,
Self::NativeAsset(_, n, _) => String::from_utf8(n.clone()).ok(),
}
}

pub fn policy_hex(&self) -> Option<String> {
match self {
Asset::Ada(_) => None,
Asset::NativeAsset(p, _, _) => Some(p.to_string()),
}
}
}

impl<'b> MultiEraOutput<'b> {
/// The amount of ADA asset expressed in Lovelace unit
///
/// The value returned provides the amount of the ADA in a particular
/// output. The value is expressed in 'lovelace' (1 ADA = 1,000,000
/// lovelace).
pub fn lovelace_amount(&self) -> u64 {
match self {
MultiEraOutput::Byron(x) => x.amount,
MultiEraOutput::Babbage(x) => match x.deref().deref() {
babbage::TransactionOutput::Legacy(x) => match x.amount {
babbage::Value::Coin(c) => c,
babbage::Value::Multiasset(c, _) => c,
},
babbage::TransactionOutput::PostAlonzo(x) => match x.value {
babbage::Value::Coin(c) => c,
babbage::Value::Multiasset(c, _) => c,
},
},
MultiEraOutput::AlonzoCompatible(x) => match x.amount {
alonzo::Value::Coin(c) => c,
alonzo::Value::Multiasset(c, _) => c,
},
}
}

/// List of native assets in the output
///
/// 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<Asset> {
match self {
MultiEraOutput::Byron(_) => vec![],
MultiEraOutput::Babbage(x) => match x.deref().deref() {
babbage::TransactionOutput::Legacy(x) => match &x.amount {
babbage::Value::Coin(_) => vec![],
babbage::Value::Multiasset(_, x) => collect_multiassets(x),
},
babbage::TransactionOutput::PostAlonzo(x) => match &x.value {
babbage::Value::Coin(_) => vec![],
babbage::Value::Multiasset(_, x) => collect_multiassets(x),
},
},
MultiEraOutput::AlonzoCompatible(x) => match &x.amount {
alonzo::Value::Coin(_) => vec![],
alonzo::Value::Multiasset(_, x) => collect_multiassets(x),
},
}
}

/// 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<Asset> {
[
vec![Asset::Ada(self.lovelace_amount())],
self.non_ada_assets(),
]
.concat()
}
}
28 changes: 5 additions & 23 deletions pallas-traverse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ use thiserror::Error;

use pallas_codec::utils::KeepRaw;
use pallas_crypto::hash::Hash;
use pallas_primitives::{
alonzo,
babbage::{self, AssetName, PolicyId},
byron,
};
use pallas_primitives::{alonzo, babbage, byron};

pub mod assets;
pub mod block;
pub mod cert;
pub mod era;
Expand Down Expand Up @@ -137,24 +134,9 @@ pub enum MultiEraSigners<'b> {
pub struct OutputRef(Hash<32>, u64);

#[derive(Debug, Clone)]
pub struct Asset {
pub subject: Subject,
pub quantity: u64,
}

#[derive(Debug, Clone)]
pub enum Subject {
Lovelace,
NativeAsset(PolicyId, AssetName),
}

impl ToString for Subject {
fn to_string(&self) -> String {
match self {
Self::Lovelace => String::from("lovelace"),
Self::NativeAsset(p, n) => format!("{p}.{n}"),
}
}
pub enum Asset {
Ada(u64),
NativeAsset(Hash<28>, Vec<u8>, u64),
}

#[derive(Debug, Error)]
Expand Down
120 changes: 8 additions & 112 deletions pallas-traverse/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ use std::{borrow::Cow, ops::Deref};

use pallas_addresses::{Address, ByronAddress, Error as AddressError};
use pallas_codec::minicbor;
use pallas_primitives::{
alonzo,
babbage::{self, Coin, DatumOption, ScriptRef},
byron,
};
use pallas_primitives::{alonzo, babbage, byron};

use crate::{Asset, Era, MultiEraOutput, Subject};
use crate::{Era, MultiEraOutput};

impl<'b> MultiEraOutput<'b> {
pub fn from_byron(output: &'b byron::TxOut) -> Self {
Expand All @@ -23,18 +19,20 @@ impl<'b> MultiEraOutput<'b> {
Self::Babbage(Box::new(Cow::Borrowed(output)))
}

pub fn datum(&self) -> Option<DatumOption> {
pub fn datum(&self) -> Option<babbage::DatumOption> {
match self {
MultiEraOutput::AlonzoCompatible(x) => x.datum_hash.map(DatumOption::Hash),
MultiEraOutput::AlonzoCompatible(x) => x.datum_hash.map(babbage::DatumOption::Hash),
MultiEraOutput::Babbage(x) => match x.deref().deref() {
babbage::TransactionOutput::Legacy(x) => x.datum_hash.map(DatumOption::Hash),
babbage::TransactionOutput::Legacy(x) => {
x.datum_hash.map(babbage::DatumOption::Hash)
}
babbage::TransactionOutput::PostAlonzo(x) => x.datum_option.clone(),
},
_ => None,
}
}

pub fn script_ref(&self) -> Option<&ScriptRef> {
pub fn script_ref(&self) -> Option<&babbage::ScriptRef> {
match &self {
MultiEraOutput::Babbage(x) => match x.deref().deref() {
babbage::TransactionOutput::Legacy(_) => None,
Expand All @@ -57,70 +55,6 @@ impl<'b> MultiEraOutput<'b> {
}
}

pub fn ada_amount(&self) -> u64 {
match self {
MultiEraOutput::Byron(x) => x.amount,
MultiEraOutput::Babbage(x) => match x.deref().deref() {
babbage::TransactionOutput::Legacy(x) => match x.amount {
babbage::Value::Coin(c) => c,
babbage::Value::Multiasset(c, _) => c,
},
babbage::TransactionOutput::PostAlonzo(x) => match x.value {
babbage::Value::Coin(c) => c,
babbage::Value::Multiasset(c, _) => c,
},
},
MultiEraOutput::AlonzoCompatible(x) => match x.amount {
alonzo::Value::Coin(c) => c,
alonzo::Value::Multiasset(c, _) => c,
},
}
}

pub fn assets(&self) -> Vec<Asset> {
let mut assets = Vec::new();

match self {
MultiEraOutput::Byron(x) => {
push_lovelace(&mut assets, x.amount);
}
MultiEraOutput::Babbage(x) => match x.deref().deref() {
babbage::TransactionOutput::Legacy(x) => match &x.amount {
babbage::Value::Coin(c) => {
push_lovelace(&mut assets, *c);
}
babbage::Value::Multiasset(c, multi_asset) => {
push_lovelace(&mut assets, *c);

push_native_asset(&mut assets, multi_asset);
}
},
babbage::TransactionOutput::PostAlonzo(x) => match &x.value {
babbage::Value::Coin(c) => {
push_lovelace(&mut assets, *c);
}
babbage::Value::Multiasset(c, multi_asset) => {
push_lovelace(&mut assets, *c);

push_native_asset(&mut assets, multi_asset);
}
},
},
MultiEraOutput::AlonzoCompatible(x) => match &x.amount {
alonzo::Value::Coin(c) => {
push_lovelace(&mut assets, *c);
}
alonzo::Value::Multiasset(c, multi_asset) => {
push_lovelace(&mut assets, *c);

push_native_asset(&mut assets, multi_asset);
}
},
};

assets
}

pub fn as_babbage(&self) -> Option<&babbage::TransactionOutput> {
match self {
MultiEraOutput::AlonzoCompatible(_) => None,
Expand Down Expand Up @@ -174,41 +108,3 @@ impl<'b> MultiEraOutput<'b> {
}
}
}

fn push_lovelace(assets: &mut Vec<Asset>, quantity: u64) {
assets.push(Asset {
subject: Subject::Lovelace,
quantity,
})
}

fn push_native_asset(assets: &mut Vec<Asset>, multi_asset: &alonzo::Multiasset<Coin>) {
for (policy_id, names) in multi_asset.iter() {
for (asset_name, quantity) in names.iter() {
assets.push(Asset {
subject: Subject::NativeAsset(*policy_id, asset_name.clone()),
quantity: *quantity,
});
}
}
}

#[cfg(test)]
mod tests {
use crate::MultiEraBlock;

#[test]
fn traverse_block_with_varied_outputs() {
let str = include_str!("../../test_data/alonzo24.block");
let bytes = hex::decode(str).unwrap();
let block = MultiEraBlock::decode(&bytes).unwrap();

for tx in block.txs() {
for output in tx.outputs() {
assert_ne!(output.assets()[0].quantity, 0);
assert_ne!(output.ada_amount(), 0);
assert!(matches!(output.address(), Ok(_)));
}
}
}
}

0 comments on commit 47a5c52

Please sign in to comment.