cat_gateway/cardano/
util.rs

1//! Block stream parsing and filtering utils
2use pallas::ledger::{
3    primitives::conway::StakeCredential,
4    traverse::{Era, MultiEraAsset, MultiEraCert, MultiEraPolicyAssets},
5};
6use serde::Serialize;
7
8/// Witness pub key hashed with blake2b
9pub type WitnessHash = String;
10
11/// Witness pub key in hex
12pub type WitnessPubKey = String;
13
14/// Stake credential hash from the certificate
15pub type StakeCredentialHash = String;
16
17/// Correct stake credential key in hex
18pub type StakeCredentialKey = String;
19
20#[derive(Default, Debug, Serialize)]
21/// Assets
22pub struct Asset {
23    /// Policy id
24    pub policy_id: String,
25    /// Asset name
26    pub name: String,
27    /// Amount in lovelace
28    pub amount: u64,
29}
30
31#[derive(Default, Debug, Serialize)]
32/// Parsed Assets
33pub struct PolicyAsset {
34    /// Policy identifier
35    pub policy_hash: String,
36    /// All policy assets
37    pub assets: Vec<Asset>,
38}
39
40/// Extract assets
41#[allow(dead_code)]
42pub(crate) fn parse_policy_assets(assets: &[MultiEraPolicyAssets<'_>]) -> Vec<PolicyAsset> {
43    assets
44        .iter()
45        .map(|asset| {
46            PolicyAsset {
47                policy_hash: asset.policy().to_string(),
48                assets: parse_child_assets(&asset.assets()),
49            }
50        })
51        .collect()
52}
53
54/// Parse child assets
55#[allow(dead_code)]
56fn parse_child_assets(assets: &[MultiEraAsset]) -> Vec<Asset> {
57    assets
58        .iter()
59        .filter_map(|asset| {
60            match asset {
61                MultiEraAsset::AlonzoCompatibleOutput(id, name, amount) => {
62                    Some(Asset {
63                        policy_id: id.to_string(),
64                        name: name.to_string(),
65                        amount: *amount,
66                    })
67                },
68                MultiEraAsset::AlonzoCompatibleMint(id, name, amount) => {
69                    let amount = u64::try_from(*amount).ok()?;
70                    Some(Asset {
71                        policy_id: id.to_string(),
72                        name: name.to_string(),
73                        amount,
74                    })
75                },
76                _ => Some(Asset::default()),
77            }
78        })
79        .collect()
80}
81
82/// Eras before staking should be ignored
83#[allow(dead_code)]
84pub fn valid_era(era: Era) -> bool {
85    !matches!(era, Era::Byron)
86}
87
88/// Extract stake credentials from certificates. Stake credentials are 28 byte blake2b
89/// hashes.
90#[allow(dead_code)]
91pub fn extract_stake_credentials_from_certs(
92    certs: &[MultiEraCert<'_>],
93) -> Vec<StakeCredentialHash> {
94    let mut stake_credentials = Vec::new();
95
96    for cert in certs {
97        if let Some(cert) = cert.as_alonzo() {
98            match cert {
99                pallas::ledger::primitives::alonzo::Certificate::StakeDelegation(
100                    stake_credential,
101                    _,
102                ) => {
103                    match stake_credential {
104                        StakeCredential::AddrKeyhash(stake_credential) => {
105                            stake_credentials.push(hex::encode(stake_credential.as_slice()));
106                        },
107                        StakeCredential::Scripthash(_) => (),
108                    }
109                },
110                _ => continue,
111            }
112        }
113    }
114
115    stake_credentials
116}
117
118/// Match hashed witness pub keys with hashed stake credentials from the TX certificates
119/// to identify the correct stake credential key.
120#[allow(dead_code)]
121pub fn find_matching_stake_credential(
122    witnesses: &[(WitnessPubKey, WitnessHash)], stake_credentials: &[String],
123) -> anyhow::Result<(StakeCredentialKey, StakeCredentialHash)> {
124    stake_credentials
125        .iter()
126        .zip(witnesses.iter())
127        .find_map(|(stake_credential, (pub_key, pub_key_hash))| {
128            if stake_credential == pub_key_hash {
129                Some((pub_key.clone(), pub_key_hash.clone()))
130            } else {
131                None
132            }
133        })
134        .ok_or(anyhow::anyhow!(
135            "No stake credential from the certificates matches any of the witness pub keys"
136        ))
137}