diff --git a/catalyst-gateway/bin/src/db/index/block/txo/cql/insert_txo_asset.cql b/catalyst-gateway/bin/src/db/index/block/txo/cql/insert_txo_asset.cql index 3bdb6342c4b..92c9dd0c534 100644 --- a/catalyst-gateway/bin/src/db/index/block/txo/cql/insert_txo_asset.cql +++ b/catalyst-gateway/bin/src/db/index/block/txo/cql/insert_txo_asset.cql @@ -6,7 +6,7 @@ INSERT INTO txo_assets_by_stake ( txn, txo, policy_id, - policy_name, + asset_name, value ) VALUES ( :stake_address, @@ -14,6 +14,6 @@ INSERT INTO txo_assets_by_stake ( :txn, :txo, :policy_id, - :policy_name, + :asset_name, :value ); diff --git a/catalyst-gateway/bin/src/db/index/block/txo/cql/insert_unstaked_txo_asset.cql b/catalyst-gateway/bin/src/db/index/block/txo/cql/insert_unstaked_txo_asset.cql index e170a0b46c2..6045229d69e 100644 --- a/catalyst-gateway/bin/src/db/index/block/txo/cql/insert_unstaked_txo_asset.cql +++ b/catalyst-gateway/bin/src/db/index/block/txo/cql/insert_unstaked_txo_asset.cql @@ -3,7 +3,7 @@ INSERT INTO unstaked_txo_assets_by_txn_hash ( txn_hash, txo, policy_id, - policy_name, + asset_name, slot_no, txn, value @@ -11,7 +11,7 @@ INSERT INTO unstaked_txo_assets_by_txn_hash ( :txn_hash, :txo, :policy_id, - :policy_name, + :asset_name, :slot_no, :txn, :value diff --git a/catalyst-gateway/bin/src/db/index/block/txo/insert_txo_asset.rs b/catalyst-gateway/bin/src/db/index/block/txo/insert_txo_asset.rs index ba7bbde7c45..69e547594a7 100644 --- a/catalyst-gateway/bin/src/db/index/block/txo/insert_txo_asset.rs +++ b/catalyst-gateway/bin/src/db/index/block/txo/insert_txo_asset.rs @@ -27,8 +27,8 @@ pub(super) struct Params { txo: i16, /// Policy hash of the asset policy_id: Vec, - /// Policy name of the asset - policy_name: String, + /// Name of the asset, within the Policy. + asset_name: Vec, /// Value of the asset value: num_bigint::BigInt, } @@ -41,7 +41,7 @@ impl Params { #[allow(clippy::too_many_arguments)] pub(super) fn new( stake_address: &[u8], slot_no: u64, txn: i16, txo: i16, policy_id: &[u8], - policy_name: &str, value: i128, + asset_name: &[u8], value: i128, ) -> Self { Self { stake_address: stake_address.to_vec(), @@ -49,7 +49,7 @@ impl Params { txn, txo, policy_id: policy_id.to_vec(), - policy_name: policy_name.to_owned(), + asset_name: asset_name.to_vec(), value: value.into(), } } diff --git a/catalyst-gateway/bin/src/db/index/block/txo/insert_unstaked_txo_asset.rs b/catalyst-gateway/bin/src/db/index/block/txo/insert_unstaked_txo_asset.rs index 250ca8ae1c3..7436d36d200 100644 --- a/catalyst-gateway/bin/src/db/index/block/txo/insert_unstaked_txo_asset.rs +++ b/catalyst-gateway/bin/src/db/index/block/txo/insert_unstaked_txo_asset.rs @@ -24,7 +24,7 @@ pub(super) struct Params { /// Policy hash of the asset policy_id: Vec, /// Policy name of the asset - policy_name: String, + asset_name: Vec, /// Block Slot Number slot_no: num_bigint::BigInt, /// Transaction Offset inside the block. @@ -40,14 +40,14 @@ impl Params { /// values. #[allow(clippy::too_many_arguments)] pub(super) fn new( - txn_hash: &[u8], txo: i16, policy_id: &[u8], policy_name: &str, slot_no: u64, txn: i16, + txn_hash: &[u8], txo: i16, policy_id: &[u8], asset_name: &[u8], slot_no: u64, txn: i16, value: i128, ) -> Self { Self { txn_hash: txn_hash.to_vec(), txo, policy_id: policy_id.to_vec(), - policy_name: policy_name.to_owned(), + asset_name: asset_name.to_vec(), slot_no: slot_no.into(), txn, value: value.into(), diff --git a/catalyst-gateway/bin/src/db/index/block/txo/mod.rs b/catalyst-gateway/bin/src/db/index/block/txo/mod.rs index 66bd950822f..f3f08440e60 100644 --- a/catalyst-gateway/bin/src/db/index/block/txo/mod.rs +++ b/catalyst-gateway/bin/src/db/index/block/txo/mod.rs @@ -181,7 +181,7 @@ impl TxoInsertQuery { let policy_id = asset.policy().to_vec(); for policy_asset in asset.assets() { if policy_asset.is_output() { - let policy_name = policy_asset.to_ascii_name().unwrap_or_default(); + let asset_name = policy_asset.name(); let value = policy_asset.any_coin(); if staked { @@ -191,19 +191,13 @@ impl TxoInsertQuery { txn, txo_index, &policy_id, - &policy_name, + asset_name, value, ); self.staked_txo_asset.push(params); } else { let params = insert_unstaked_txo_asset::Params::new( - txn_hash, - txo_index, - &policy_id, - &policy_name, - slot_no, - txn, - value, + txn_hash, txo_index, &policy_id, asset_name, slot_no, txn, value, ); self.unstaked_txo_asset.push(params); } diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_assets_by_stake_address.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_assets_by_stake_address.cql index 128bc81d782..82d4c605818 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_assets_by_stake_address.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_assets_by_stake_address.cql @@ -3,7 +3,7 @@ SELECT txo, slot_no, policy_id, - policy_name, + asset_name, value FROM txo_assets_by_stake WHERE stake_address = :stake_address diff --git a/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_assets_by_stake_address.rs b/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_assets_by_stake_address.rs index 03d1a16a7dc..09bc8f2df03 100644 --- a/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_assets_by_stake_address.rs +++ b/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_assets_by_stake_address.rs @@ -54,7 +54,7 @@ mod result { /// Asset hash. pub policy_id: Vec, /// Asset name. - pub policy_name: String, + pub asset_name: Vec, /// Asset value. pub value: num_bigint::BigInt, } diff --git a/catalyst-gateway/bin/src/db/index/schema/cql/txo_assets_by_stake_table.cql b/catalyst-gateway/bin/src/db/index/schema/cql/txo_assets_by_stake_table.cql index 19a686f2d53..da85a1d9323 100644 --- a/catalyst-gateway/bin/src/db/index/schema/cql/txo_assets_by_stake_table.cql +++ b/catalyst-gateway/bin/src/db/index/schema/cql/txo_assets_by_stake_table.cql @@ -7,11 +7,11 @@ CREATE TABLE IF NOT EXISTS txo_assets_by_stake ( txn smallint, -- Which Transaction in the Slot is the TXO. txo smallint, -- offset in the txo list of the transaction the txo is in. policy_id blob, -- asset policy hash (id) (28 byte binary hash) - policy_name text, -- name of the policy (UTF8) TODO: https://github.com/input-output-hk/catalyst-voices/issues/1121 + asset_name blob, -- name of the asset policy (UTF8) (32 bytes) -- None Key Data of the asset. value varint, -- Value of the asset (i128) - PRIMARY KEY (stake_address, slot_no, txn, txo, policy_id, policy_name) + PRIMARY KEY (stake_address, slot_no, txn, txo, policy_id, asset_name) ); diff --git a/catalyst-gateway/bin/src/db/index/schema/cql/unstaked_txo_assets_by_txn_hash.cql b/catalyst-gateway/bin/src/db/index/schema/cql/unstaked_txo_assets_by_txn_hash.cql index 047567d895d..4628e236ec4 100644 --- a/catalyst-gateway/bin/src/db/index/schema/cql/unstaked_txo_assets_by_txn_hash.cql +++ b/catalyst-gateway/bin/src/db/index/schema/cql/unstaked_txo_assets_by_txn_hash.cql @@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS unstaked_txo_assets_by_txn_hash ( txn_hash blob, -- 32 byte hash of this transaction. txo smallint, -- offset in the txo list of the transaction the txo is in. policy_id blob, -- asset policy hash (id) (28 byte binary hash) - policy_name text, -- name of the policy (UTF8) + asset_name blob, -- name of the policy (UTF8) (32 bytes) -- Secondary Location information for the transaction. slot_no varint, -- slot number the txo was created in. @@ -13,5 +13,5 @@ CREATE TABLE IF NOT EXISTS unstaked_txo_assets_by_txn_hash ( -- Value of the asset. value varint, -- Value of the asset (u64) - PRIMARY KEY (txn_hash, txo, policy_id, policy_name) + PRIMARY KEY (txn_hash, txo, policy_id, asset_name) ); diff --git a/catalyst-gateway/bin/src/db/index/schema/mod.rs b/catalyst-gateway/bin/src/db/index/schema/mod.rs index 8a219105fe1..e262e2a0a76 100644 --- a/catalyst-gateway/bin/src/db/index/schema/mod.rs +++ b/catalyst-gateway/bin/src/db/index/schema/mod.rs @@ -17,7 +17,7 @@ use crate::{settings::cassandra_db, utils::blake2b_hash::generate_uuid_string_fr /// change accidentally, and is NOT to be used directly to set the schema version of the /// table namespaces. #[allow(dead_code)] -const SCHEMA_VERSION: &str = "08193dfe-698a-8177-bdf8-20c5691a06e7"; +const SCHEMA_VERSION: &str = "75ae6ac9-ddd8-8472-8a7a-8676d04f8679"; /// Keyspace Create (Templated) const CREATE_NAMESPACE_CQL: &str = include_str!("./cql/namespace.cql"); diff --git a/catalyst-gateway/bin/src/service/api/cardano/staking/assets_get.rs b/catalyst-gateway/bin/src/service/api/cardano/staking/assets_get.rs index cd0d2d04e37..f92fa93d5d9 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/staking/assets_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/staking/assets_get.rs @@ -27,7 +27,7 @@ use crate::{ stake_info::{FullStakeInfo, StakeInfo, StakedNativeTokenInfo}, }, responses::WithErrorResponses, - types::cardano::cip19_stake_address::Cip19StakeAddress, + types::cardano::{asset_name::AssetName, cip19_stake_address::Cip19StakeAddress}, }, }; @@ -79,8 +79,7 @@ struct TxoAssetInfo { /// Asset hash. id: Vec, /// Asset name. - // TODO: https://github.com/input-output-hk/catalyst-voices/issues/1121 - name: String, + name: AssetName, /// Asset amount. amount: num_bigint::BigInt, } @@ -100,8 +99,7 @@ struct TxoInfo { /// Whether the TXO was spent. spent_slot_no: Option, /// TXO assets. - // TODO: https://github.com/input-output-hk/catalyst-voices/issues/1121 - assets: HashMap, TxoAssetInfo>, + assets: HashMap, Vec>, } /// Calculate the stake info for a given stake address. @@ -186,12 +184,18 @@ async fn get_txo_by_txn( let entry = txo_info .assets .entry(row.policy_id.clone()) - .or_insert(TxoAssetInfo { - id: row.policy_id, - name: row.policy_name, - amount: num_bigint::BigInt::ZERO, - }); - entry.amount += row.value; + .or_insert_with(Vec::new); + + match entry.iter_mut().find(|item| item.id == row.policy_id) { + Some(item) => item.amount += row.value, + None => { + entry.push(TxoAssetInfo { + id: row.policy_id, + name: row.asset_name.into(), + amount: row.value, + }); + }, + } } let mut txos_by_txn = HashMap::new(); @@ -274,10 +278,10 @@ fn build_stake_info( stake_info.ada_amount += i64::try_from(txo_info.value).map_err(|err| anyhow!(err))?; - for asset in txo_info.assets.into_values() { + for asset in txo_info.assets.into_values().flatten() { stake_info.native_tokens.push(StakedNativeTokenInfo { policy_hash: asset.id.try_into()?, - asset_name: asset.name.into(), + asset_name: asset.name, amount: asset.amount.try_into()?, }); } diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/asset_name.rs b/catalyst-gateway/bin/src/service/common/types/cardano/asset_name.rs index 3f8e1f72cf5..0594a0f968a 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/asset_name.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/asset_name.rs @@ -65,7 +65,6 @@ impl Example for AssetName { } } -// TODO: https://github.com/input-output-hk/catalyst-voices/issues/1121 impl From> for AssetName { fn from(value: Vec) -> Self { match String::from_utf8(value.clone()) { diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/cip19_stake_address.rs b/catalyst-gateway/bin/src/service/common/types/cardano/cip19_stake_address.rs index 3ae12fbc3b1..83f01951003 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/cip19_stake_address.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/cip19_stake_address.rs @@ -42,7 +42,7 @@ pub(crate) const PATTERN: &str = concatcp!( /// Length of the encoded address. const ENCODED_ADDR_LEN: usize = 53; /// Length of the decoded address. -const DECODED_ADDR_LEN: usize = 28; +const DECODED_ADDR_LEN: usize = 29; /// Minimum length pub(crate) const MIN_LENGTH: usize = PROD_STAKE.len() + 1 + ENCODED_ADDR_LEN; /// Minimum length @@ -150,3 +150,34 @@ impl Example for Cip19StakeAddress { Self(EXAMPLE.to_owned()) } } + +#[cfg(test)] +mod tests { + use super::*; + + // cspell: disable + const VALID_PROD_STAKE_ADDRESS: &str = + "stake1u94ullc9nj9gawc08990nx8hwgw80l9zpmr8re44kydqy9cdjq6rq"; + const VALID_TEST_STAKE_ADDRESS: &str = + "stake_test1uqehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gssrtvn"; + const INVALID_STAKE_ADDRESS: &str = + "invalid1u9nlq5nmuzthw3vhgakfpxyq4r0zl2c0p8uqy24gpyjsa6c3df4h6"; + // cspell: enable + + #[test] + fn test_valid_stake_address_from_string() { + let stake_address_prod = Cip19StakeAddress::try_from(VALID_PROD_STAKE_ADDRESS.to_string()); + let stake_address_test = Cip19StakeAddress::try_from(VALID_TEST_STAKE_ADDRESS.to_string()); + + assert!(stake_address_prod.is_ok()); + assert!(stake_address_test.is_ok()); + assert_eq!(stake_address_prod.unwrap().0, VALID_PROD_STAKE_ADDRESS); + assert_eq!(stake_address_test.unwrap().0, VALID_TEST_STAKE_ADDRESS); + } + + #[test] + fn test_invalid_stake_address_from_string() { + let stake_address = Cip19StakeAddress::try_from(INVALID_STAKE_ADDRESS.to_string()); + assert!(stake_address.is_err()); + } +} diff --git a/catalyst-gateway/tests/api_tests/api_tests/get_cardano_assets.hurl b/catalyst-gateway/tests/api_tests/api_tests/get_cardano_assets.hurl new file mode 100644 index 00000000000..92bfc53e2ef --- /dev/null +++ b/catalyst-gateway/tests/api_tests/api_tests/get_cardano_assets.hurl @@ -0,0 +1,15 @@ +# Get staked ADA amount: zero assets +GET http://localhost:3030/api/draft/cardano/assets/stake_test1ursne3ndzr4kz8gmhmstu5026erayrnqyj46nqkkfcn0ufss2t7vt +HTTP 200 +{"persistent":{"ada_amount":9809147618,"native_tokens":[],"slot_number":76323283},"volatile":{"ada_amount":0,"native_tokens":[],"slot_number":0}} + +# Get staked ADA amount: single asset +GET http://localhost:3030/api/draft/cardano/assets/stake_test1uq7cnze6az9f8ffjrvkxx4ad77jz088frkhzupxcc7y4x8q5x808s +HTTP 200 +{"persistent":{"ada_amount":8870859858,"native_tokens":[{"amount":3,"asset_name":"GoldRelic","policy_hash":"0x2862c9b33e98096107e2d8b8c072070834db9c91c0d2f3743e75df65"}],"slot_number":76572358},"volatile":{"ada_amount":0,"native_tokens":[],"slot_number":0}} + +# Get staked ADA amount: multiple assets +GET http://localhost:3030/api/draft/cardano/assets/stake_test1ur66dds0pkf3j5tu7py9tqf7savpv7pgc5g3dd74xy0x2vsldf2mx +HTTP 200 +[Asserts] +jsonpath "$.persistent.native_tokens" count == 9 \ No newline at end of file