feat(traverse): Add helper methods to Asset data (#195)

This commit is contained in:
Santiago Carmuega 2022-09-28 11:45:37 -03:00 committed by GitHub
parent 2f7bfc8551
commit 47a5c52a9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 123 additions and 135 deletions

View file

@ -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()
}
}

View file

@ -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;
@ -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)]

View file

@ -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 {
@ -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,
@ -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,
@ -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(_)));
}
}
}
}