From 54036632676a9e718b6da6093aea2254ac1326c1 Mon Sep 17 00:00:00 2001 From: cong-or Date: Fri, 27 Sep 2024 21:09:39 +0000 Subject: [PATCH 01/37] feat(barebones): scylla queries --- .../get_latest_registration_w_stake_addr.cql | 4 + .../bin/src/db/index/queries/mod.rs | 13 ++- .../get_latest_registration_w_stake_addr.rs | 84 +++++++++++++++++++ .../src/db/index/queries/registrations/mod.rs | 2 + .../bin/src/service/api/cardano/mod.rs | 13 +++ .../service/api/cardano/registration_get.rs | 49 +++++------ .../bin/src/service/api/health/mod.rs | 17 +++- .../objects/cardano/registration_info.rs | 30 +++---- utilities/local-scylla/justfile | 2 +- 9 files changed, 166 insertions(+), 48 deletions(-) create mode 100644 catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql create mode 100644 catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs create mode 100644 catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql new file mode 100644 index 0000000000..33826cad0c --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql @@ -0,0 +1,4 @@ +SELECT + stake_address, + MAX(slot_no) +FROM cip36_registration; diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index e34db0647e..10393ad6be 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -2,12 +2,14 @@ //! //! This improves query execution time. +mod registrations; pub(crate) mod staked_ada; use std::{fmt::Debug, sync::Arc}; use anyhow::{bail, Context}; use crossbeam_skiplist::SkipMap; +use registrations::get_latest_registration_w_stake_addr::GetLatestRegistrationQuery; use scylla::{ batch::Batch, prepared_statement::PreparedStatement, serialize::row::SerializeRow, transport::iterator::RowIterator, QueryResult, Session, @@ -57,6 +59,8 @@ pub(crate) enum PreparedSelectQuery { GetTxoByStakeAddress, /// Get TXI by transaction hash query. GetTxiByTransactionHash, + /// Get latest Registration + GetLatestRegistration, } /// All prepared queries for a session. @@ -86,6 +90,8 @@ pub(crate) struct PreparedQueries { txo_by_stake_address_query: PreparedStatement, /// Get TXI by transaction hash. txi_by_txn_hash_query: PreparedStatement, + /// Get latest registration + latest_registration_query: PreparedStatement, } /// An individual query response that can fail @@ -110,6 +116,7 @@ impl PreparedQueries { UpdateTxoSpentQuery::prepare_batch(session.clone(), cfg).await; let txo_by_stake_address_query = GetTxoByStakeAddressQuery::prepare(session.clone()).await; let txi_by_txn_hash_query = GetTxiByTxnHashesQuery::prepare(session.clone()).await; + let latest_registration_query = GetLatestRegistrationQuery::prepare(session.clone()).await; let ( txo_insert_queries, @@ -137,6 +144,7 @@ impl PreparedQueries { txo_spent_update_queries: txo_spent_update_queries?, txo_by_stake_address_query: txo_by_stake_address_query?, txi_by_txn_hash_query: txi_by_txn_hash_query?, + latest_registration_query: latest_registration_query?, }) } @@ -190,10 +198,13 @@ impl PreparedQueries { pub(crate) async fn execute_iter

( &self, session: Arc, select_query: PreparedSelectQuery, params: P, ) -> anyhow::Result - where P: SerializeRow { + where + P: SerializeRow, + { let prepared_stmt = match select_query { PreparedSelectQuery::GetTxoByStakeAddress => &self.txo_by_stake_address_query, PreparedSelectQuery::GetTxiByTransactionHash => &self.txi_by_txn_hash_query, + PreparedSelectQuery::GetLatestRegistration => &self.latest_registration_query, }; session diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs new file mode 100644 index 0000000000..7e63dc707b --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs @@ -0,0 +1,84 @@ +//! Get TXI by Transaction hash query + +use std::sync::Arc; + +use scylla::{ + prepared_statement::PreparedStatement, transport::iterator::TypedRowIterator, SerializeRow, + Session, +}; +use tracing::error; + +use crate::db::index::{ + queries::{PreparedQueries, PreparedSelectQuery}, + session::CassandraSession, +}; + +/// Get TXI query string. +const GET_LATEST_REGISTRATION_QUERY: &str = + include_str!("../cql/get_latest_registration_w_stake_addr.cql"); + +/// Get TXI query parameters. +#[derive(SerializeRow)] +pub(crate) struct GetLatestRegistration { + /// Transaction hashes. + txn_hashes: Vec>, +} + +impl GetLatestRegistration { + /// Create a new instance of [`GetTxiByTxnHashesQueryParams`] + pub(crate) fn new(txn_hashes: Vec>) -> Self { + Self { txn_hashes } + } +} + +/// Get TXI Query Result +// TODO: https://github.com/input-output-hk/catalyst-voices/issues/828 +// The macro uses expect to signal an error in deserializing values. +#[allow(clippy::expect_used)] +mod result { + use scylla::FromRow; + + /// Get TXI query result. + #[derive(FromRow)] + pub(crate) struct GetLatestRegistrationQuery { + /// TXI transaction hash. + pub txn_hash: Vec, + /// TXI original TXO index. + pub txo: i16, + /// TXI slot number. + pub slot_no: num_bigint::BigInt, + } +} +/// Get latest registration query. +pub(crate) struct GetLatestRegistrationQuery; + +impl GetLatestRegistrationQuery { + /// Prepares a get txi query. + pub(crate) async fn prepare(session: Arc) -> anyhow::Result { + let get_latest_registration_query = PreparedQueries::prepare( + session, + GET_LATEST_REGISTRATION_QUERY, + scylla::statement::Consistency::All, + true, + ) + .await; + + if let Err(ref error) = get_latest_registration_query { + error!(error=%error, "Failed to prepare get latest registration query."); + }; + + get_latest_registration_query + } + + /// Executes a get txi by transaction hashes query. + pub(crate) async fn execute( + session: &CassandraSession, params: GetLatestRegistration, + ) -> anyhow::Result> { + let iter = session + .execute_iter(PreparedSelectQuery::GetLatestRegistration, params) + .await? + .into_typed::(); + + Ok(iter) + } +} diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs new file mode 100644 index 0000000000..e789634356 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs @@ -0,0 +1,2 @@ +//! Registration related queries. +pub(crate) mod get_latest_registration_w_stake_addr; diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index 9e83da3c47..2f86a4ab8e 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -133,4 +133,17 @@ impl CardanoApi { ) -> date_time_to_slot_number_get::AllResponses { date_time_to_slot_number_get::endpoint(date_time.0, network.0).await } + + #[oai( + path = "/cip36/latest_registration", + method = "get", + operation_id = "latestRegistration" + )] + /// Cip36 registrations + /// + /// This endpoint gets the latest registraton + #[allow(clippy::unused_async)] + async fn latest_registration_cip36(&self) -> registration_get::AllResponses { + registration_get::latest_registration(true) + } } diff --git a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs index 00c4e89d1b..3e855da378 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs @@ -1,16 +1,20 @@ //! Implementation of the GET `/registration` endpoint -use poem_openapi::{payload::Json, ApiResponse}; +use poem_openapi::{payload::Json, types::Example, ApiResponse}; +use tracing::error; use super::types::SlotNumber; -use crate::service::{ - common::{ - objects::cardano::{ - network::Network, registration_info::RegistrationInfo, stake_address::StakeAddress, +use crate::{ + db::index::session::CassandraSession, + service::{ + common::{ + objects::cardano::{ + network::Network, registration_info::RegistrationInfo, stake_address::StakeAddress, + }, + responses::WithErrorResponses, }, - responses::WithErrorResponses, + utilities::check_network, }, - utilities::check_network, }; /// Endpoint responses @@ -41,22 +45,19 @@ pub(crate) async fn endpoint( Err(err) => return AllResponses::handle_error(&err), }; - let _unused = " - // get the total utxo amount from the database - match EventDB::get_registration_info(stake_credential, network.into(), date_time).await { - Ok((tx_id, payment_address, voting_info, nonce)) => { - Responses::Ok(Json(RegistrationInfo::new( - tx_id, - &payment_address, - voting_info, - nonce, - ))) - .into() - }, - Err(err) if err.is::() => Responses::NotFound.into(), - Err(err) => AllResponses::handle_error(&err), - } - "; - Responses::NotFound.into() } + +/// Get latest registration given a stake key hash or stake address +pub(crate) fn latest_registration(persistent: bool) -> AllResponses { + let Some(_session) = CassandraSession::get(persistent) else { + error!("Failed to acquire db session"); + return Responses::NotFound.into(); + }; + + GetLatestRegistrationQuery::execute(); + + let r = RegistrationInfo::example(); + + Responses::Ok(Json(r)).into() +} diff --git a/catalyst-gateway/bin/src/service/api/health/mod.rs b/catalyst-gateway/bin/src/service/api/health/mod.rs index 24c9b20d96..bf0f3d8951 100644 --- a/catalyst-gateway/bin/src/service/api/health/mod.rs +++ b/catalyst-gateway/bin/src/service/api/health/mod.rs @@ -1,10 +1,11 @@ //! Health Endpoints use poem_openapi::{param::Query, OpenApi}; +use tracing::info; -use crate::service::common::tags::ApiTags; +use crate::{db::index::session::CassandraSession, service::common::tags::ApiTags}; mod inspection_get; -mod live_get; +pub mod live_get; mod ready_get; mod started_get; pub(crate) use started_get::started; @@ -51,7 +52,7 @@ impl HealthApi { /// /// *This endpoint is for internal use of the service deployment infrastructure. /// It may not be exposed publicly. Refer to []* - async fn live_get(&self) -> live_get::AllResponses { + pub async fn live_get(&self) -> live_get::AllResponses { live_get::endpoint().await } @@ -65,6 +66,14 @@ impl HealthApi { &self, log_level: Query>, query_inspection: Query>, ) -> inspection_get::AllResponses { - inspection_get::endpoint(log_level.0, query_inspection.0).await + info!("deep!"); + + match CassandraSession::get(true) { + Some(_s) => { + info!("it worked "); + inspection_get::endpoint(log_level.0, query_inspection.0).await + }, + None => inspection_get::endpoint(log_level.0, query_inspection.0).await, + } } } diff --git a/catalyst-gateway/bin/src/service/common/objects/cardano/registration_info.rs b/catalyst-gateway/bin/src/service/common/objects/cardano/registration_info.rs index 6a855cc799..0681096785 100644 --- a/catalyst-gateway/bin/src/service/common/objects/cardano/registration_info.rs +++ b/catalyst-gateway/bin/src/service/common/objects/cardano/registration_info.rs @@ -75,24 +75,18 @@ impl RegistrationInfo { nonce: Nonce, ) -> Self { let voting_info = match voting_info { - PublicVotingInfo::Direct(voting_key) => { - VotingInfo::Direct(DirectVoter { - voting_key: to_hex_with_prefix(voting_key.bytes()), - }) - }, - PublicVotingInfo::Delegated(delegations) => { - VotingInfo::Delegated(Delegations { - delegations: delegations - .into_iter() - .map(|(voting_key, power)| { - Delegation { - voting_key: to_hex_with_prefix(voting_key.bytes()), - power, - } - }) - .collect(), - }) - }, + PublicVotingInfo::Direct(voting_key) => VotingInfo::Direct(DirectVoter { + voting_key: to_hex_with_prefix(voting_key.bytes()), + }), + PublicVotingInfo::Delegated(delegations) => VotingInfo::Delegated(Delegations { + delegations: delegations + .into_iter() + .map(|(voting_key, power)| Delegation { + voting_key: to_hex_with_prefix(voting_key.bytes()), + power, + }) + .collect(), + }), }; Self { tx_hash: tx_hash.into(), diff --git a/utilities/local-scylla/justfile b/utilities/local-scylla/justfile index 2cbf82e207..722cbe36a7 100644 --- a/utilities/local-scylla/justfile +++ b/utilities/local-scylla/justfile @@ -21,7 +21,7 @@ scylla-dev-db-stop: # Local scylla dev DB CLUSTER - Starts with pre-existing data. scylla-dev-db-cluster: HOST_IP="{{host_ip}}" \ - docker compose up + docker compose up -d # Stop the scylla development DB CLUSTER scylla-dev-db-stop-cluster: From 8f416142a2c6cd18da0361c0541fe235f4485194 Mon Sep 17 00:00:00 2001 From: cong-or Date: Sat, 28 Sep 2024 21:49:45 +0000 Subject: [PATCH 02/37] feat(barebones): scylla queries --- .../get_latest_registration_w_stake_addr.cql | 9 ++-- .../bin/src/db/index/queries/mod.rs | 7 ++- .../get_latest_registration_w_stake_addr.rs | 33 ++++++------ .../bin/src/service/api/cardano/mod.rs | 2 +- .../service/api/cardano/registration_get.rs | 52 +++++++++++++++++-- .../objects/cardano/registration_info.rs | 30 ++++++----- 6 files changed, 89 insertions(+), 44 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql index 33826cad0c..2281f6a4d6 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql @@ -1,4 +1,5 @@ -SELECT - stake_address, - MAX(slot_no) -FROM cip36_registration; +SELECT * +FROM cip36_registration +WHERE stake_address = :stake_address +ORDER BY nonce DESC +LIMIT 1; \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index 10393ad6be..907f25bfc6 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -2,7 +2,7 @@ //! //! This improves query execution time. -mod registrations; +pub(crate) mod registrations; pub(crate) mod staked_ada; use std::{fmt::Debug, sync::Arc}; @@ -54,6 +54,7 @@ pub(crate) enum PreparedQuery { } /// All prepared SELECT query statements. +#[allow(clippy::enum_variant_names)] pub(crate) enum PreparedSelectQuery { /// Get TXO by stake address query. GetTxoByStakeAddress, @@ -198,9 +199,7 @@ impl PreparedQueries { pub(crate) async fn execute_iter

( &self, session: Arc, select_query: PreparedSelectQuery, params: P, ) -> anyhow::Result - where - P: SerializeRow, - { + where P: SerializeRow { let prepared_stmt = match select_query { PreparedSelectQuery::GetTxoByStakeAddress => &self.txo_by_stake_address_query, PreparedSelectQuery::GetTxiByTransactionHash => &self.txi_by_txn_hash_query, diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs index 7e63dc707b..1b07d44c71 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs @@ -13,40 +13,37 @@ use crate::db::index::{ session::CassandraSession, }; -/// Get TXI query string. +/// Get latest registration query string. const GET_LATEST_REGISTRATION_QUERY: &str = include_str!("../cql/get_latest_registration_w_stake_addr.cql"); -/// Get TXI query parameters. +/// Get latest registration #[derive(SerializeRow)] -pub(crate) struct GetLatestRegistration { - /// Transaction hashes. - txn_hashes: Vec>, +pub(crate) struct GetLatestRegistrationParams { + /// Stake address. + stake_address: Vec, } -impl GetLatestRegistration { - /// Create a new instance of [`GetTxiByTxnHashesQueryParams`] - pub(crate) fn new(txn_hashes: Vec>) -> Self { - Self { txn_hashes } +impl GetLatestRegistrationParams { + /// Create a new instance of [`GetLatestRegistrationParams`] + pub(crate) fn new(stake_addr: Vec) -> GetLatestRegistrationParams { + Self { + stake_address: stake_addr, + } } } -/// Get TXI Query Result -// TODO: https://github.com/input-output-hk/catalyst-voices/issues/828 -// The macro uses expect to signal an error in deserializing values. +/// A #[allow(clippy::expect_used)] +#[allow(dead_code)] mod result { use scylla::FromRow; /// Get TXI query result. #[derive(FromRow)] pub(crate) struct GetLatestRegistrationQuery { - /// TXI transaction hash. - pub txn_hash: Vec, - /// TXI original TXO index. - pub txo: i16, /// TXI slot number. - pub slot_no: num_bigint::BigInt, + pub stake_address: Vec, } } /// Get latest registration query. @@ -72,7 +69,7 @@ impl GetLatestRegistrationQuery { /// Executes a get txi by transaction hashes query. pub(crate) async fn execute( - session: &CassandraSession, params: GetLatestRegistration, + session: &CassandraSession, params: GetLatestRegistrationParams, ) -> anyhow::Result> { let iter = session .execute_iter(PreparedSelectQuery::GetLatestRegistration, params) diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index 2f86a4ab8e..2aa1dfa8e8 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -144,6 +144,6 @@ impl CardanoApi { /// This endpoint gets the latest registraton #[allow(clippy::unused_async)] async fn latest_registration_cip36(&self) -> registration_get::AllResponses { - registration_get::latest_registration(true) + registration_get::latest_registration(true).await } } diff --git a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs index 3e855da378..92185b3b3c 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs @@ -1,11 +1,17 @@ //! Implementation of the GET `/registration` endpoint +use futures::StreamExt; use poem_openapi::{payload::Json, types::Example, ApiResponse}; -use tracing::error; +use tracing::{error, info}; use super::types::SlotNumber; use crate::{ - db::index::session::CassandraSession, + db::index::{ + queries::registrations::get_latest_registration_w_stake_addr::{ + GetLatestRegistrationParams, GetLatestRegistrationQuery, + }, + session::CassandraSession, + }, service::{ common::{ objects::cardano::{ @@ -49,13 +55,49 @@ pub(crate) async fn endpoint( } /// Get latest registration given a stake key hash or stake address -pub(crate) fn latest_registration(persistent: bool) -> AllResponses { - let Some(_session) = CassandraSession::get(persistent) else { +pub(crate) async fn latest_registration(persistent: bool) -> AllResponses { + let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); return Responses::NotFound.into(); }; - GetLatestRegistrationQuery::execute(); + info!("here!!!!!!"); + + let stake_addr = + match hex::decode("0x12a5b7212903f01722323d229a08ad91e8b01659aad3284c0aad556cff992f93") { + Ok(k) => k, + Err(_err) => return Responses::NotFound.into(), + }; + + info!("here!2!!!!!"); + + let mut registrations_iter = match GetLatestRegistrationQuery::execute( + &session, + GetLatestRegistrationParams::new(stake_addr), + ) + .await + { + Ok(latest) => latest, + Err(err) => { + error!("Failed to get latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let Some(r) = registrations_iter.next().await else { + error!("Failed to get latest registration "); + return Responses::NotFound.into(); + }; + + let row = match r { + Ok(row) => row, + Err(err) => { + error!("Failed to get latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + info!("got it {:?}", row.stake_address); let r = RegistrationInfo::example(); diff --git a/catalyst-gateway/bin/src/service/common/objects/cardano/registration_info.rs b/catalyst-gateway/bin/src/service/common/objects/cardano/registration_info.rs index 0681096785..6a855cc799 100644 --- a/catalyst-gateway/bin/src/service/common/objects/cardano/registration_info.rs +++ b/catalyst-gateway/bin/src/service/common/objects/cardano/registration_info.rs @@ -75,18 +75,24 @@ impl RegistrationInfo { nonce: Nonce, ) -> Self { let voting_info = match voting_info { - PublicVotingInfo::Direct(voting_key) => VotingInfo::Direct(DirectVoter { - voting_key: to_hex_with_prefix(voting_key.bytes()), - }), - PublicVotingInfo::Delegated(delegations) => VotingInfo::Delegated(Delegations { - delegations: delegations - .into_iter() - .map(|(voting_key, power)| Delegation { - voting_key: to_hex_with_prefix(voting_key.bytes()), - power, - }) - .collect(), - }), + PublicVotingInfo::Direct(voting_key) => { + VotingInfo::Direct(DirectVoter { + voting_key: to_hex_with_prefix(voting_key.bytes()), + }) + }, + PublicVotingInfo::Delegated(delegations) => { + VotingInfo::Delegated(Delegations { + delegations: delegations + .into_iter() + .map(|(voting_key, power)| { + Delegation { + voting_key: to_hex_with_prefix(voting_key.bytes()), + power, + } + }) + .collect(), + }) + }, }; Self { tx_hash: tx_hash.into(), From f29351484e551434dbe378ea242524079fcec2bb Mon Sep 17 00:00:00 2001 From: cong-or Date: Sun, 29 Sep 2024 13:49:25 +0000 Subject: [PATCH 03/37] feat(barebones): scylla queries --- .../get_latest_registration_w_stake_addr.cql | 4 +-- .../get_latest_registration_w_stake_addr.rs | 4 +-- .../service/api/cardano/registration_get.rs | 28 ++++++++----------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql index 2281f6a4d6..46f304ce6a 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql @@ -1,5 +1,5 @@ -SELECT * +SELECT stake_address FROM cip36_registration WHERE stake_address = :stake_address ORDER BY nonce DESC -LIMIT 1; \ No newline at end of file +LIMIT 1; \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs index 1b07d44c71..5743bff72d 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs @@ -21,7 +21,7 @@ const GET_LATEST_REGISTRATION_QUERY: &str = #[derive(SerializeRow)] pub(crate) struct GetLatestRegistrationParams { /// Stake address. - stake_address: Vec, + pub stake_address: Vec, } impl GetLatestRegistrationParams { @@ -42,7 +42,7 @@ mod result { /// Get TXI query result. #[derive(FromRow)] pub(crate) struct GetLatestRegistrationQuery { - /// TXI slot number. + /// Full Stake Address (not hashed, 32 byte ED25519 Public key). pub stake_address: Vec, } } diff --git a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs index 92185b3b3c..8f5a74ef2c 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs @@ -64,13 +64,11 @@ pub(crate) async fn latest_registration(persistent: bool) -> AllResponses { info!("here!!!!!!"); let stake_addr = - match hex::decode("0x12a5b7212903f01722323d229a08ad91e8b01659aad3284c0aad556cff992f93") { + match hex::decode("76e7ac0e460b6cdecea4be70479dab13c4adbd117421259a9b36caac007394de") { Ok(k) => k, Err(_err) => return Responses::NotFound.into(), }; - info!("here!2!!!!!"); - let mut registrations_iter = match GetLatestRegistrationQuery::execute( &session, GetLatestRegistrationParams::new(stake_addr), @@ -84,20 +82,18 @@ pub(crate) async fn latest_registration(persistent: bool) -> AllResponses { }, }; - let Some(r) = registrations_iter.next().await else { - error!("Failed to get latest registration "); - return Responses::NotFound.into(); - }; - - let row = match r { - Ok(row) => row, - Err(err) => { - error!("Failed to get latest registration {:?}", err); - return Responses::NotFound.into(); - }, - }; + while let Some(row_res) = registrations_iter.next().await { + info!("made!"); + let row = match row_res { + Ok(r) => r, + Err(err) => { + error!("Failed to get latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; - info!("got it {:?}", row.stake_address); + info!("are! {:?}", row.stake_address); + } let r = RegistrationInfo::example(); From 5df6a3a69fb9d44af98d43bcb7d831987e710942 Mon Sep 17 00:00:00 2001 From: cong-or Date: Sun, 29 Sep 2024 21:15:06 +0000 Subject: [PATCH 04/37] feat(barebones): scylla queries --- .../get_latest_registration_w_stake_addr.cql | 12 +++- .../get_latest_registration_w_stake_addr.rs | 20 +++++- .../bin/src/service/api/cardano/mod.rs | 5 +- .../service/api/cardano/registration_get.rs | 70 ++----------------- 4 files changed, 38 insertions(+), 69 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql index 46f304ce6a..30415d0b46 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql @@ -1,5 +1,13 @@ -SELECT stake_address +SELECT stake_address, + nonce, + slot_no, + txn, + vote_key, + payment_address, + is_payable, + raw_nonce, + cip36 FROM cip36_registration WHERE stake_address = :stake_address ORDER BY nonce DESC -LIMIT 1; \ No newline at end of file +LIMIT 1; \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs index 5743bff72d..7fdd888505 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs @@ -33,17 +33,33 @@ impl GetLatestRegistrationParams { } } -/// A +/// Get latest registration given stake addr or vote key #[allow(clippy::expect_used)] #[allow(dead_code)] mod result { use scylla::FromRow; - /// Get TXI query result. + /// Get Latest registration query result. #[derive(FromRow)] pub(crate) struct GetLatestRegistrationQuery { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). pub stake_address: Vec, + /// Nonce value after normalization. + pub nonce: num_bigint::BigInt, + /// Slot Number the cert is in. + pub slot_no: num_bigint::BigInt, + /// Transaction Index. + pub txn: i16, + /// Voting Public Key + pub vote_key: Vec, + /// Full Payment Address (not hashed, 32 byte ED25519 Public key). + pub payment_address: Vec, + /// Is the stake address a script or not. + pub is_payable: bool, + /// Raw nonce value. + pub raw_nonce: num_bigint::BigInt, + /// Is the Registration CIP36 format, or CIP15 + pub cip36: bool, } } /// Get latest registration query. diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index 2aa1dfa8e8..bb5e416891 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -13,6 +13,7 @@ use crate::service::{ utilities::middleware::schema_validation::schema_version_validation, }; +mod cip36; mod date_time_to_slot_number_get; mod registration_get; mod staked_ada_get; @@ -143,7 +144,7 @@ impl CardanoApi { /// /// This endpoint gets the latest registraton #[allow(clippy::unused_async)] - async fn latest_registration_cip36(&self) -> registration_get::AllResponses { - registration_get::latest_registration(true).await + async fn latest_registration_cip36(&self) -> cip36::AllResponses { + cip36::latest_registration(true).await } } diff --git a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs index 8f5a74ef2c..27f2f1b072 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs @@ -1,26 +1,16 @@ //! Implementation of the GET `/registration` endpoint -use futures::StreamExt; -use poem_openapi::{payload::Json, types::Example, ApiResponse}; -use tracing::{error, info}; +use poem_openapi::{payload::Json, ApiResponse}; use super::types::SlotNumber; -use crate::{ - db::index::{ - queries::registrations::get_latest_registration_w_stake_addr::{ - GetLatestRegistrationParams, GetLatestRegistrationQuery, +use crate::service::{ + common::{ + objects::cardano::{ + network::Network, registration_info::RegistrationInfo, stake_address::StakeAddress, }, - session::CassandraSession, - }, - service::{ - common::{ - objects::cardano::{ - network::Network, registration_info::RegistrationInfo, stake_address::StakeAddress, - }, - responses::WithErrorResponses, - }, - utilities::check_network, + responses::WithErrorResponses, }, + utilities::check_network, }; /// Endpoint responses @@ -53,49 +43,3 @@ pub(crate) async fn endpoint( Responses::NotFound.into() } - -/// Get latest registration given a stake key hash or stake address -pub(crate) async fn latest_registration(persistent: bool) -> AllResponses { - let Some(session) = CassandraSession::get(persistent) else { - error!("Failed to acquire db session"); - return Responses::NotFound.into(); - }; - - info!("here!!!!!!"); - - let stake_addr = - match hex::decode("76e7ac0e460b6cdecea4be70479dab13c4adbd117421259a9b36caac007394de") { - Ok(k) => k, - Err(_err) => return Responses::NotFound.into(), - }; - - let mut registrations_iter = match GetLatestRegistrationQuery::execute( - &session, - GetLatestRegistrationParams::new(stake_addr), - ) - .await - { - Ok(latest) => latest, - Err(err) => { - error!("Failed to get latest registration {:?}", err); - return Responses::NotFound.into(); - }, - }; - - while let Some(row_res) = registrations_iter.next().await { - info!("made!"); - let row = match row_res { - Ok(r) => r, - Err(err) => { - error!("Failed to get latest registration {:?}", err); - return Responses::NotFound.into(); - }, - }; - - info!("are! {:?}", row.stake_address); - } - - let r = RegistrationInfo::example(); - - Responses::Ok(Json(r)).into() -} From b307125551d7c77f3921cc00cf821f7c3e4f524b Mon Sep 17 00:00:00 2001 From: cong-or Date: Sun, 29 Sep 2024 21:15:57 +0000 Subject: [PATCH 05/37] feat(barebones): scylla queries --- .../bin/src/service/api/cardano/cip36.rs | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 catalyst-gateway/bin/src/service/api/cardano/cip36.rs diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs new file mode 100644 index 0000000000..03cad2fc30 --- /dev/null +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -0,0 +1,107 @@ +//! Implementation of the GET `/registration/cip36` endpoint + +use futures::StreamExt; +use poem_openapi::{payload::Json, ApiResponse, Object}; +use tracing::error; + +use crate::{ + db::index::{ + queries::registrations::get_latest_registration_w_stake_addr::{ + GetLatestRegistrationParams, GetLatestRegistrationQuery, + }, + session::CassandraSession, + }, + service::common::responses::WithErrorResponses, +}; + +/// Endpoint responses +#[derive(ApiResponse)] +#[allow(dead_code)] +pub(crate) enum Responses { + /// The registration information for the stake address queried. + #[oai(status = 200)] + Ok(Json), + /// No valid registration found for the provided stake address + /// and provided slot number. + #[oai(status = 404)] + NotFound, +} + +/// User's cardano stake info. +#[derive(Object, Default)] +pub(crate) struct Cip36Info { + /// Full Stake Address (not hashed, 32 byte ED25519 Public key). + pub stake_address: Vec, + /// Nonce value after normalization. + pub nonce: i64, + /// Slot Number the cert is in. + pub slot_no: i64, + /// Transaction Index. + pub txn: i16, + /// Voting Public Key + pub vote_key: Vec, + /// Full Payment Address (not hashed, 32 byte ED25519 Public key). + pub payment_address: Vec, + /// Is the stake address a script or not. + pub is_payable: bool, + /// Raw nonce value. + pub raw_nonce: i64, + /// Is the Registration CIP36 format, or CIP15 + pub cip36: bool, +} + +/// All responses +pub(crate) type AllResponses = WithErrorResponses; + +/// Get latest registration given a stake key hash or stake address +pub(crate) async fn latest_registration(persistent: bool) -> AllResponses { + let Some(session) = CassandraSession::get(persistent) else { + error!("Failed to acquire db session"); + return Responses::NotFound.into(); + }; + + let stake_addr = + match hex::decode("76e7ac0e460b6cdecea4be70479dab13c4adbd117421259a9b36caac007394de") { + Ok(k) => k, + Err(_err) => return Responses::NotFound.into(), + }; + + let mut registrations_iter = match GetLatestRegistrationQuery::execute( + &session, + GetLatestRegistrationParams::new(stake_addr), + ) + .await + { + Ok(latest) => latest, + Err(err) => { + error!("Failed to get latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + if let Some(row_res) = registrations_iter.next().await { + let row = match row_res { + Ok(r) => r, + Err(err) => { + error!("Failed to get latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let cip36 = Cip36Info { + stake_address: row.stake_address, + nonce: 1, + slot_no: 1, + txn: row.txn, + vote_key: row.vote_key, + payment_address: row.payment_address, + is_payable: row.is_payable, + raw_nonce: 1, + cip36: row.cip36, + }; + + return Responses::Ok(Json(cip36)).into(); + } + + Responses::NotFound.into() +} From 30f3d3f7f2e644b3bda6ecc15d032751a15e019a Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 30 Sep 2024 13:24:25 +0000 Subject: [PATCH 06/37] feat(latest registration endpoints): given stake addr and stake key hash --- .../bin/src/service/api/cardano/cip36.rs | 83 ++++++++++++++++--- .../bin/src/service/api/cardano/mod.rs | 27 ++++-- 2 files changed, 92 insertions(+), 18 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 03cad2fc30..222f70e284 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -16,18 +16,16 @@ use crate::{ /// Endpoint responses #[derive(ApiResponse)] -#[allow(dead_code)] pub(crate) enum Responses { - /// The registration information for the stake address queried. + /// The registration information for the stake address or stake key hashqueried. #[oai(status = 200)] Ok(Json), - /// No valid registration found for the provided stake address - /// and provided slot number. + /// No valid registration found for the provided stake address or stake key hash #[oai(status = 404)] NotFound, } -/// User's cardano stake info. +/// Cip36 info #[derive(Object, Default)] pub(crate) struct Cip36Info { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). @@ -53,19 +51,80 @@ pub(crate) struct Cip36Info { /// All responses pub(crate) type AllResponses = WithErrorResponses; -/// Get latest registration given a stake key hash or stake address -pub(crate) async fn latest_registration(persistent: bool) -> AllResponses { +/// Get latest registration given stake address +pub(crate) async fn get_latest_registration_from_stake_addr( + stake_addr: String, persistent: bool, +) -> AllResponses { + let stake_addr = match hex::decode(stake_addr) { + Ok(stake_addr) => stake_addr, + Err(err) => { + error!("Failed to decode stake addr {:?}", err); + return Responses::NotFound.into(); + }, + }; + let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); return Responses::NotFound.into(); }; - let stake_addr = - match hex::decode("76e7ac0e460b6cdecea4be70479dab13c4adbd117421259a9b36caac007394de") { - Ok(k) => k, - Err(_err) => return Responses::NotFound.into(), + let mut registrations_iter = match GetLatestRegistrationQuery::execute( + &session, + GetLatestRegistrationParams::new(stake_addr), + ) + .await + { + Ok(latest) => latest, + Err(err) => { + error!("Failed to query latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + if let Some(row_res) = registrations_iter.next().await { + let row = match row_res { + Ok(r) => r, + Err(err) => { + error!("Failed to get latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let cip36 = Cip36Info { + stake_address: row.stake_address, + nonce: 1, + slot_no: 1, + txn: row.txn, + vote_key: row.vote_key, + payment_address: row.payment_address, + is_payable: row.is_payable, + raw_nonce: 1, + cip36: row.cip36, }; + return Responses::Ok(Json(cip36)).into(); + } + + Responses::NotFound.into() +} + +/// Get latest registration given stake key hash +pub(crate) async fn get_latest_registration_from_stake_key_hash( + stake_addr: String, persistent: bool, +) -> AllResponses { + let stake_addr = match hex::decode(stake_addr) { + Ok(stake_addr) => stake_addr, + Err(err) => { + error!("Failed to decode stake addr {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let Some(session) = CassandraSession::get(persistent) else { + error!("Failed to acquire db session"); + return Responses::NotFound.into(); + }; + let mut registrations_iter = match GetLatestRegistrationQuery::execute( &session, GetLatestRegistrationParams::new(stake_addr), @@ -74,7 +133,7 @@ pub(crate) async fn latest_registration(persistent: bool) -> AllResponses { { Ok(latest) => latest, Err(err) => { - error!("Failed to get latest registration {:?}", err); + error!("Failed to query latest registration {:?}", err); return Responses::NotFound.into(); }, }; diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index bb5e416891..a32fa9de87 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -136,15 +136,30 @@ impl CardanoApi { } #[oai( - path = "/cip36/latest_registration", + path = "/cip36/latest_registration/stake_addr", method = "get", - operation_id = "latestRegistration" + operation_id = "latestRegistrationGivenStakeAddr" )] /// Cip36 registrations /// - /// This endpoint gets the latest registraton - #[allow(clippy::unused_async)] - async fn latest_registration_cip36(&self) -> cip36::AllResponses { - cip36::latest_registration(true).await + /// This endpoint gets the latest registraton given a stake addr + async fn latest_registration_cip36_given_stake_addr( + &self, stake_addr: Query, + ) -> cip36::AllResponses { + cip36::get_latest_registration_from_stake_addr(stake_addr.0, true).await + } + + #[oai( + path = "/cip36/latest_registration/stake_hash", + method = "get", + operation_id = "latestRegistrationGivenStakeHash" + )] + /// Cip36 registrations + /// + /// This endpoint gets the latest registraton given a stake key hash + async fn latest_registration_cip36_given_stake_key_hash( + &self, stake_key_hash: Query, + ) -> cip36::AllResponses { + cip36::get_latest_registration_from_stake_key_hash(stake_key_hash.0, true).await } } From 649b0febf4a079ee138b6d6521200088f0aef9e3 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 30 Sep 2024 13:27:55 +0000 Subject: [PATCH 07/37] feat(latest registration endpoints): given stake addr and stake key hash --- catalyst-gateway/bin/src/service/api/health/mod.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/health/mod.rs b/catalyst-gateway/bin/src/service/api/health/mod.rs index bf0f3d8951..ba6bd6e7c9 100644 --- a/catalyst-gateway/bin/src/service/api/health/mod.rs +++ b/catalyst-gateway/bin/src/service/api/health/mod.rs @@ -66,13 +66,8 @@ impl HealthApi { &self, log_level: Query>, query_inspection: Query>, ) -> inspection_get::AllResponses { - info!("deep!"); - match CassandraSession::get(true) { - Some(_s) => { - info!("it worked "); - inspection_get::endpoint(log_level.0, query_inspection.0).await - }, + Some(_s) => inspection_get::endpoint(log_level.0, query_inspection.0).await, None => inspection_get::endpoint(log_level.0, query_inspection.0).await, } } From 221c07d88face1ebe910f45dc69e4f223c5ddeb7 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 30 Sep 2024 13:29:33 +0000 Subject: [PATCH 08/37] feat(latest registration endpoints): given stake addr and stake key hash --- catalyst-gateway/bin/src/service/api/health/mod.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/health/mod.rs b/catalyst-gateway/bin/src/service/api/health/mod.rs index ba6bd6e7c9..a359c2bb22 100644 --- a/catalyst-gateway/bin/src/service/api/health/mod.rs +++ b/catalyst-gateway/bin/src/service/api/health/mod.rs @@ -1,8 +1,7 @@ //! Health Endpoints use poem_openapi::{param::Query, OpenApi}; -use tracing::info; -use crate::{db::index::session::CassandraSession, service::common::tags::ApiTags}; +use crate::service::common::tags::ApiTags; mod inspection_get; pub mod live_get; @@ -66,9 +65,6 @@ impl HealthApi { &self, log_level: Query>, query_inspection: Query>, ) -> inspection_get::AllResponses { - match CassandraSession::get(true) { - Some(_s) => inspection_get::endpoint(log_level.0, query_inspection.0).await, - None => inspection_get::endpoint(log_level.0, query_inspection.0).await, - } + inspection_get::endpoint(log_level.0, query_inspection.0).await } } From 3a6150b0cd21e963ecad416b316b5b5607a817b4 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 30 Sep 2024 13:30:37 +0000 Subject: [PATCH 09/37] feat(latest registration endpoints): given stake addr and stake key hash --- catalyst-gateway/bin/src/service/api/health/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/health/mod.rs b/catalyst-gateway/bin/src/service/api/health/mod.rs index a359c2bb22..24c9b20d96 100644 --- a/catalyst-gateway/bin/src/service/api/health/mod.rs +++ b/catalyst-gateway/bin/src/service/api/health/mod.rs @@ -4,7 +4,7 @@ use poem_openapi::{param::Query, OpenApi}; use crate::service::common::tags::ApiTags; mod inspection_get; -pub mod live_get; +mod live_get; mod ready_get; mod started_get; pub(crate) use started_get::started; @@ -51,7 +51,7 @@ impl HealthApi { /// /// *This endpoint is for internal use of the service deployment infrastructure. /// It may not be exposed publicly. Refer to []* - pub async fn live_get(&self) -> live_get::AllResponses { + async fn live_get(&self) -> live_get::AllResponses { live_get::endpoint().await } From 375801f0d084b5cccdfc5929628974dd68c68011 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 30 Sep 2024 13:32:04 +0000 Subject: [PATCH 10/37] feat(latest registration endpoints): given stake addr and stake key hash --- catalyst-gateway/bin/src/db/index/queries/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index 907f25bfc6..e30fffb66b 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -54,7 +54,6 @@ pub(crate) enum PreparedQuery { } /// All prepared SELECT query statements. -#[allow(clippy::enum_variant_names)] pub(crate) enum PreparedSelectQuery { /// Get TXO by stake address query. GetTxoByStakeAddress, @@ -199,7 +198,9 @@ impl PreparedQueries { pub(crate) async fn execute_iter

( &self, session: Arc, select_query: PreparedSelectQuery, params: P, ) -> anyhow::Result - where P: SerializeRow { + where + P: SerializeRow, + { let prepared_stmt = match select_query { PreparedSelectQuery::GetTxoByStakeAddress => &self.txo_by_stake_address_query, PreparedSelectQuery::GetTxiByTransactionHash => &self.txi_by_txn_hash_query, From 883b71e73b4e850ac5adafd726a865f541fc6a2d Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 30 Sep 2024 19:03:23 +0000 Subject: [PATCH 11/37] feat(latest registration endpoints): given stake addr and stake key hash --- .../get_latest_registration_w_stake_addr.cql | 4 +- .../bin/src/db/index/queries/mod.rs | 16 +++---- .../get_latest_registration_w_stake_addr.rs | 5 +-- .../queries/staked_ada/get_txi_by_txn_hash.rs | 2 +- .../staked_ada/get_txo_by_stake_address.rs | 2 +- .../bin/src/service/api/cardano/cip36.rs | 45 ++++++++++++++----- .../bin/src/service/api/cardano/mod.rs | 2 +- 7 files changed, 47 insertions(+), 29 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql index 30415d0b46..c98be51da7 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql @@ -5,9 +5,7 @@ SELECT stake_address, vote_key, payment_address, is_payable, - raw_nonce, cip36 FROM cip36_registration WHERE stake_address = :stake_address -ORDER BY nonce DESC -LIMIT 1; \ No newline at end of file +LIMIT 1; \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index e30fffb66b..a1642e4dc9 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -56,11 +56,11 @@ pub(crate) enum PreparedQuery { /// All prepared SELECT query statements. pub(crate) enum PreparedSelectQuery { /// Get TXO by stake address query. - GetTxoByStakeAddress, + TxoByStakeAddress, /// Get TXI by transaction hash query. - GetTxiByTransactionHash, + TxiByTransactionHash, /// Get latest Registration - GetLatestRegistration, + LatestRegistration, } /// All prepared queries for a session. @@ -198,13 +198,11 @@ impl PreparedQueries { pub(crate) async fn execute_iter

( &self, session: Arc, select_query: PreparedSelectQuery, params: P, ) -> anyhow::Result - where - P: SerializeRow, - { + where P: SerializeRow { let prepared_stmt = match select_query { - PreparedSelectQuery::GetTxoByStakeAddress => &self.txo_by_stake_address_query, - PreparedSelectQuery::GetTxiByTransactionHash => &self.txi_by_txn_hash_query, - PreparedSelectQuery::GetLatestRegistration => &self.latest_registration_query, + PreparedSelectQuery::TxoByStakeAddress => &self.txo_by_stake_address_query, + PreparedSelectQuery::TxiByTransactionHash => &self.txi_by_txn_hash_query, + PreparedSelectQuery::LatestRegistration => &self.latest_registration_query, }; session diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs index 7fdd888505..18ab452f77 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs @@ -35,7 +35,6 @@ impl GetLatestRegistrationParams { /// Get latest registration given stake addr or vote key #[allow(clippy::expect_used)] -#[allow(dead_code)] mod result { use scylla::FromRow; @@ -56,8 +55,6 @@ mod result { pub payment_address: Vec, /// Is the stake address a script or not. pub is_payable: bool, - /// Raw nonce value. - pub raw_nonce: num_bigint::BigInt, /// Is the Registration CIP36 format, or CIP15 pub cip36: bool, } @@ -88,7 +85,7 @@ impl GetLatestRegistrationQuery { session: &CassandraSession, params: GetLatestRegistrationParams, ) -> anyhow::Result> { let iter = session - .execute_iter(PreparedSelectQuery::GetLatestRegistration, params) + .execute_iter(PreparedSelectQuery::LatestRegistration, params) .await? .into_typed::(); diff --git a/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_txi_by_txn_hash.rs b/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_txi_by_txn_hash.rs index 7c3cc6af04..3da06a7315 100644 --- a/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_txi_by_txn_hash.rs +++ b/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_txi_by_txn_hash.rs @@ -74,7 +74,7 @@ impl GetTxiByTxnHashesQuery { session: &CassandraSession, params: GetTxiByTxnHashesQueryParams, ) -> anyhow::Result> { let iter = session - .execute_iter(PreparedSelectQuery::GetTxiByTransactionHash, params) + .execute_iter(PreparedSelectQuery::TxiByTransactionHash, params) .await? .into_typed::(); diff --git a/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_txo_by_stake_address.rs b/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_txo_by_stake_address.rs index 2beee7e646..1d488f9b28 100644 --- a/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_txo_by_stake_address.rs +++ b/catalyst-gateway/bin/src/db/index/queries/staked_ada/get_txo_by_stake_address.rs @@ -85,7 +85,7 @@ impl GetTxoByStakeAddressQuery { session: &CassandraSession, params: GetTxoByStakeAddressQueryParams, ) -> anyhow::Result> { let iter = session - .execute_iter(PreparedSelectQuery::GetTxoByStakeAddress, params) + .execute_iter(PreparedSelectQuery::TxoByStakeAddress, params) .await? .into_typed::(); diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 222f70e284..338b808f07 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -31,9 +31,9 @@ pub(crate) struct Cip36Info { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). pub stake_address: Vec, /// Nonce value after normalization. - pub nonce: i64, + pub nonce: u64, /// Slot Number the cert is in. - pub slot_no: i64, + pub slot_no: u64, /// Transaction Index. pub txn: i16, /// Voting Public Key @@ -42,8 +42,6 @@ pub(crate) struct Cip36Info { pub payment_address: Vec, /// Is the stake address a script or not. pub is_payable: bool, - /// Raw nonce value. - pub raw_nonce: i64, /// Is the Registration CIP36 format, or CIP15 pub cip36: bool, } @@ -90,15 +88,28 @@ pub(crate) async fn get_latest_registration_from_stake_addr( }, }; + let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { + *nonce + } else { + error!("Issue downcasting nonce"); + return Responses::NotFound.into(); + }; + + let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() { + *slot_no + } else { + error!("Issue downcasting slot no"); + return Responses::NotFound.into(); + }; + let cip36 = Cip36Info { stake_address: row.stake_address, - nonce: 1, - slot_no: 1, + nonce, + slot_no, txn: row.txn, vote_key: row.vote_key, payment_address: row.payment_address, is_payable: row.is_payable, - raw_nonce: 1, cip36: row.cip36, }; @@ -147,15 +158,29 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( }, }; + let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { + *nonce + } else { + error!("Issue downcasting nonce"); + return Responses::NotFound.into(); + }; + + let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() { + *slot_no + } else { + error!("Issue downcasting slot no"); + return Responses::NotFound.into(); + }; + let cip36 = Cip36Info { stake_address: row.stake_address, - nonce: 1, - slot_no: 1, + nonce, + slot_no, txn: row.txn, vote_key: row.vote_key, payment_address: row.payment_address, is_payable: row.is_payable, - raw_nonce: 1, + cip36: row.cip36, }; diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index a32fa9de87..909b1580cf 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -150,7 +150,7 @@ impl CardanoApi { } #[oai( - path = "/cip36/latest_registration/stake_hash", + path = "/cip36/latest_registration/stake_key_hash", method = "get", operation_id = "latestRegistrationGivenStakeHash" )] From 01e38b3ec64d70f58b3ad9c516052aa5804eff09 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 30 Sep 2024 20:50:01 +0000 Subject: [PATCH 12/37] feat(latest registration endpoints): given stake addr and stake key hash --- .../cql/get_stake_addr_w_stake_key_hash.cql | 4 + .../bin/src/db/index/queries/mod.rs | 12 +- .../get_latest_registration_w_stake_addr.rs | 2 +- .../get_latest_registration_w_stake_hash.rs | 78 ++++++++++++ .../src/db/index/queries/registrations/mod.rs | 1 + .../bin/src/service/api/cardano/cip36.rs | 111 +++++++++++------- .../bin/src/service/api/cardano/mod.rs | 3 + 7 files changed, 166 insertions(+), 45 deletions(-) create mode 100644 catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_stake_key_hash.cql create mode 100644 catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_hash.rs diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_stake_key_hash.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_stake_key_hash.cql new file mode 100644 index 0000000000..a379a6a2b1 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_stake_key_hash.cql @@ -0,0 +1,4 @@ +SELECT stake_address +FROM stake_registration +WHERE stake_hash = :stake_hash +LIMIT 1; \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index a1642e4dc9..dbdb5748cb 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -9,7 +9,10 @@ use std::{fmt::Debug, sync::Arc}; use anyhow::{bail, Context}; use crossbeam_skiplist::SkipMap; -use registrations::get_latest_registration_w_stake_addr::GetLatestRegistrationQuery; +use registrations::{ + get_latest_registration_w_stake_addr::GetLatestRegistrationQuery, + get_latest_registration_w_stake_hash::GetStakeAddrQuery, +}; use scylla::{ batch::Batch, prepared_statement::PreparedStatement, serialize::row::SerializeRow, transport::iterator::RowIterator, QueryResult, Session, @@ -61,6 +64,8 @@ pub(crate) enum PreparedSelectQuery { TxiByTransactionHash, /// Get latest Registration LatestRegistration, + /// Get latest Registration + StakeAddrFromStakeHash, } /// All prepared queries for a session. @@ -92,6 +97,8 @@ pub(crate) struct PreparedQueries { txi_by_txn_hash_query: PreparedStatement, /// Get latest registration latest_registration_query: PreparedStatement, + /// stake addr + stake_addr_query: PreparedStatement, } /// An individual query response that can fail @@ -117,6 +124,7 @@ impl PreparedQueries { let txo_by_stake_address_query = GetTxoByStakeAddressQuery::prepare(session.clone()).await; let txi_by_txn_hash_query = GetTxiByTxnHashesQuery::prepare(session.clone()).await; let latest_registration_query = GetLatestRegistrationQuery::prepare(session.clone()).await; + let stake_addr = GetStakeAddrQuery::prepare(session.clone()).await; let ( txo_insert_queries, @@ -145,6 +153,7 @@ impl PreparedQueries { txo_by_stake_address_query: txo_by_stake_address_query?, txi_by_txn_hash_query: txi_by_txn_hash_query?, latest_registration_query: latest_registration_query?, + stake_addr_query: stake_addr?, }) } @@ -203,6 +212,7 @@ impl PreparedQueries { PreparedSelectQuery::TxoByStakeAddress => &self.txo_by_stake_address_query, PreparedSelectQuery::TxiByTransactionHash => &self.txi_by_txn_hash_query, PreparedSelectQuery::LatestRegistration => &self.latest_registration_query, + PreparedSelectQuery::StakeAddrFromStakeHash => &self.stake_addr_query, }; session diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs index 18ab452f77..d80d853de5 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs @@ -63,7 +63,7 @@ mod result { pub(crate) struct GetLatestRegistrationQuery; impl GetLatestRegistrationQuery { - /// Prepares a get txi query. + /// Prepares a get latest registration query. pub(crate) async fn prepare(session: Arc) -> anyhow::Result { let get_latest_registration_query = PreparedQueries::prepare( session, diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_hash.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_hash.rs new file mode 100644 index 0000000000..235b2fea1b --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_hash.rs @@ -0,0 +1,78 @@ +//! Get TXI by Transaction hash query + +use std::sync::Arc; + +use scylla::{ + prepared_statement::PreparedStatement, transport::iterator::TypedRowIterator, SerializeRow, + Session, +}; +use tracing::error; + +use crate::db::index::{ + queries::{PreparedQueries, PreparedSelectQuery}, + session::CassandraSession, +}; + +/// Get latest registration query string. +const GET_STAKE_ADDR_FROM_STAKE_HASH: &str = + include_str!("../cql/get_stake_addr_w_stake_key_hash.cql"); + +/// Get stake addr +#[derive(SerializeRow)] +pub(crate) struct GetStakeAddrParams { + /// Stake hash. + pub stake_hash: Vec, +} + +impl GetStakeAddrParams { + /// Create a new instance of [`GetStakeAddrParams`] + pub(crate) fn new(stake_hash: Vec) -> GetStakeAddrParams { + Self { stake_hash } + } +} + +/// Get latest registration given stake addr or vote key +#[allow(clippy::expect_used)] +mod result { + use scylla::FromRow; + + /// Get Latest registration query result. + #[derive(FromRow)] + pub(crate) struct GetStakeAddrQuery { + /// Full Stake Address (not hashed, 32 byte ED25519 Public key). + pub stake_address: Vec, + } +} +/// Get latest registration query. +pub(crate) struct GetStakeAddrQuery; + +impl GetStakeAddrQuery { + /// Prepares a get txi query. + pub(crate) async fn prepare(session: Arc) -> anyhow::Result { + let get_stake_addr_query = PreparedQueries::prepare( + session, + GET_STAKE_ADDR_FROM_STAKE_HASH, + scylla::statement::Consistency::All, + true, + ) + .await; + + if let Err(ref error) = get_stake_addr_query { + error!(error=%error, "Failed to prepare get stake addr query."); + }; + + get_stake_addr_query + } + + /// Executes a get txi by transaction hashes query. + pub(crate) async fn execute( + session: &CassandraSession, params: GetStakeAddrParams, + ) -> anyhow::Result> { + let iter = session + .execute_iter(PreparedSelectQuery::StakeAddrFromStakeHash, params) + .await? + .into_typed::(); + + Ok(iter) + } +} diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs index e789634356..83b12c0787 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs @@ -1,2 +1,3 @@ //! Registration related queries. pub(crate) mod get_latest_registration_w_stake_addr; +pub(crate) mod get_latest_registration_w_stake_hash; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 338b808f07..05133aefde 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -6,8 +6,11 @@ use tracing::error; use crate::{ db::index::{ - queries::registrations::get_latest_registration_w_stake_addr::{ - GetLatestRegistrationParams, GetLatestRegistrationQuery, + queries::registrations::{ + get_latest_registration_w_stake_addr::{ + GetLatestRegistrationParams, GetLatestRegistrationQuery, + }, + get_latest_registration_w_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, }, session::CassandraSession, }, @@ -61,6 +64,8 @@ pub(crate) async fn get_latest_registration_from_stake_addr( }, }; + error!("go on!"); + let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); return Responses::NotFound.into(); @@ -121,10 +126,10 @@ pub(crate) async fn get_latest_registration_from_stake_addr( /// Get latest registration given stake key hash pub(crate) async fn get_latest_registration_from_stake_key_hash( - stake_addr: String, persistent: bool, + stake_hash: String, persistent: bool, ) -> AllResponses { - let stake_addr = match hex::decode(stake_addr) { - Ok(stake_addr) => stake_addr, + let stake_hash = match hex::decode(stake_hash) { + Ok(stake_hash) => stake_hash, Err(err) => { error!("Failed to decode stake addr {:?}", err); return Responses::NotFound.into(); @@ -136,21 +141,17 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( return Responses::NotFound.into(); }; - let mut registrations_iter = match GetLatestRegistrationQuery::execute( - &session, - GetLatestRegistrationParams::new(stake_addr), - ) - .await - { - Ok(latest) => latest, - Err(err) => { - error!("Failed to query latest registration {:?}", err); - return Responses::NotFound.into(); - }, - }; + let mut stake_addr_iter = + match GetStakeAddrQuery::execute(&session, GetStakeAddrParams::new(stake_hash)).await { + Ok(latest) => latest, + Err(err) => { + error!("Failed to query stake addr from stake hash {:?}", err); + return Responses::NotFound.into(); + }, + }; - if let Some(row_res) = registrations_iter.next().await { - let row = match row_res { + if let Some(row_stake_addr) = stake_addr_iter.next().await { + let row = match row_stake_addr { Ok(r) => r, Err(err) => { error!("Failed to get latest registration {:?}", err); @@ -158,33 +159,57 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( }, }; - let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { - *nonce - } else { - error!("Issue downcasting nonce"); - return Responses::NotFound.into(); - }; - - let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() { - *slot_no - } else { - error!("Issue downcasting slot no"); - return Responses::NotFound.into(); + let mut registrations_iter = match GetLatestRegistrationQuery::execute( + &session, + GetLatestRegistrationParams::new(row.stake_address), + ) + .await + { + Ok(latest) => latest, + Err(err) => { + error!("Failed to query latest registration {:?}", err); + return Responses::NotFound.into(); + }, }; - let cip36 = Cip36Info { - stake_address: row.stake_address, - nonce, - slot_no, - txn: row.txn, - vote_key: row.vote_key, - payment_address: row.payment_address, - is_payable: row.is_payable, - - cip36: row.cip36, - }; + if let Some(row_latest_registration) = registrations_iter.next().await { + let row = match row_latest_registration { + Ok(r) => r, + Err(err) => { + error!("Failed to get latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { + *nonce + } else { + error!("Issue downcasting nonce"); + return Responses::NotFound.into(); + }; - return Responses::Ok(Json(cip36)).into(); + let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() + { + *slot_no + } else { + error!("Issue downcasting slot no"); + return Responses::NotFound.into(); + }; + + let cip36 = Cip36Info { + stake_address: row.stake_address, + nonce, + slot_no, + txn: row.txn, + vote_key: row.vote_key, + payment_address: row.payment_address, + is_payable: row.is_payable, + + cip36: row.cip36, + }; + + return Responses::Ok(Json(cip36)).into(); + } } Responses::NotFound.into() diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index 909b1580cf..f541e05c6e 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -3,6 +3,7 @@ use poem_openapi::{ param::{Path, Query}, OpenApi, }; +use tracing::error; use types::{DateTime, SlotNumber}; use crate::service::{ @@ -146,6 +147,8 @@ impl CardanoApi { async fn latest_registration_cip36_given_stake_addr( &self, stake_addr: Query, ) -> cip36::AllResponses { + error!("zzz!"); + cip36::get_latest_registration_from_stake_addr(stake_addr.0, true).await } From c4e9741a4a44780ecc3750e91f3ba18323ca2209 Mon Sep 17 00:00:00 2001 From: cong-or Date: Tue, 1 Oct 2024 11:56:53 +0000 Subject: [PATCH 13/37] feat(latest registration endpoints): given stake addr and stake key hash --- .../cql/get_stake_addr_w_stake_key_hash.cql | 3 +- .../queries/cql/get_stake_addr_w_vote_key.cql | 4 + .../bin/src/db/index/queries/mod.rs | 24 ++-- ...ake_addr.rs => get_latest_w_stake_addr.rs} | 0 ...ake_hash.rs => get_latest_w_stake_hash.rs} | 0 .../registrations/get_latest_w_vote_key.rs | 77 +++++++++++++ .../src/db/index/queries/registrations/mod.rs | 5 +- .../bin/src/service/api/cardano/cip36.rs | 104 +++++++++++++++++- .../bin/src/service/api/cardano/mod.rs | 17 ++- 9 files changed, 213 insertions(+), 21 deletions(-) create mode 100644 catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_vote_key.cql rename catalyst-gateway/bin/src/db/index/queries/registrations/{get_latest_registration_w_stake_addr.rs => get_latest_w_stake_addr.rs} (100%) rename catalyst-gateway/bin/src/db/index/queries/registrations/{get_latest_registration_w_stake_hash.rs => get_latest_w_stake_hash.rs} (100%) create mode 100644 catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_stake_key_hash.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_stake_key_hash.cql index a379a6a2b1..7c552d6ded 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_stake_key_hash.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_stake_key_hash.cql @@ -1,4 +1,3 @@ SELECT stake_address FROM stake_registration -WHERE stake_hash = :stake_hash -LIMIT 1; \ No newline at end of file +WHERE stake_hash = :stake_hash; \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_vote_key.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_vote_key.cql new file mode 100644 index 0000000000..23d87485b8 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_stake_addr_w_vote_key.cql @@ -0,0 +1,4 @@ +SELECT stake_address +FROM cip36_registration_for_vote_key +WHERE vote_key = :vote_key +ALLOW FILTERING; diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index dbdb5748cb..6451e0faff 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -10,8 +10,9 @@ use std::{fmt::Debug, sync::Arc}; use anyhow::{bail, Context}; use crossbeam_skiplist::SkipMap; use registrations::{ - get_latest_registration_w_stake_addr::GetLatestRegistrationQuery, - get_latest_registration_w_stake_hash::GetStakeAddrQuery, + get_latest_w_stake_addr::GetLatestRegistrationQuery, + get_latest_w_stake_hash::GetStakeAddrQuery, + get_latest_w_vote_key::GetStakeAddrFromVoteKeyQuery, }; use scylla::{ batch::Batch, prepared_statement::PreparedStatement, serialize::row::SerializeRow, @@ -64,8 +65,10 @@ pub(crate) enum PreparedSelectQuery { TxiByTransactionHash, /// Get latest Registration LatestRegistration, - /// Get latest Registration + /// Get stake addr from stake hash StakeAddrFromStakeHash, + /// Get stake addr from vote key + StakeAddrFromVoteKey, } /// All prepared queries for a session. @@ -97,8 +100,10 @@ pub(crate) struct PreparedQueries { txi_by_txn_hash_query: PreparedStatement, /// Get latest registration latest_registration_query: PreparedStatement, - /// stake addr - stake_addr_query: PreparedStatement, + /// stake addr from stake hash + stake_addr_from_stake_hash_query: PreparedStatement, + /// stake addr from vote key + stake_addr_from_vote_key_query: PreparedStatement, } /// An individual query response that can fail @@ -124,7 +129,8 @@ impl PreparedQueries { let txo_by_stake_address_query = GetTxoByStakeAddressQuery::prepare(session.clone()).await; let txi_by_txn_hash_query = GetTxiByTxnHashesQuery::prepare(session.clone()).await; let latest_registration_query = GetLatestRegistrationQuery::prepare(session.clone()).await; - let stake_addr = GetStakeAddrQuery::prepare(session.clone()).await; + let stake_addr_from_stake_hash = GetStakeAddrQuery::prepare(session.clone()).await; + let stake_addr_from_vote_key = GetStakeAddrFromVoteKeyQuery::prepare(session.clone()).await; let ( txo_insert_queries, @@ -153,7 +159,8 @@ impl PreparedQueries { txo_by_stake_address_query: txo_by_stake_address_query?, txi_by_txn_hash_query: txi_by_txn_hash_query?, latest_registration_query: latest_registration_query?, - stake_addr_query: stake_addr?, + stake_addr_from_stake_hash_query: stake_addr_from_stake_hash?, + stake_addr_from_vote_key_query: stake_addr_from_vote_key?, }) } @@ -212,7 +219,8 @@ impl PreparedQueries { PreparedSelectQuery::TxoByStakeAddress => &self.txo_by_stake_address_query, PreparedSelectQuery::TxiByTransactionHash => &self.txi_by_txn_hash_query, PreparedSelectQuery::LatestRegistration => &self.latest_registration_query, - PreparedSelectQuery::StakeAddrFromStakeHash => &self.stake_addr_query, + PreparedSelectQuery::StakeAddrFromStakeHash => &self.stake_addr_from_stake_hash_query, + PreparedSelectQuery::StakeAddrFromVoteKey => &self.stake_addr_from_vote_key_query, }; session diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs similarity index 100% rename from catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_addr.rs rename to catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_hash.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_hash.rs similarity index 100% rename from catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_registration_w_stake_hash.rs rename to catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_hash.rs diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs new file mode 100644 index 0000000000..1bf1b3eb23 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs @@ -0,0 +1,77 @@ +//! Get TXI by Transaction hash query + +use std::sync::Arc; + +use scylla::{ + prepared_statement::PreparedStatement, transport::iterator::TypedRowIterator, SerializeRow, + Session, +}; +use tracing::error; + +use crate::db::index::{ + queries::{PreparedQueries, PreparedSelectQuery}, + session::CassandraSession, +}; + +/// Get stake addr from vote key query. +const GET_STAKE_ADDR_FROM_VOTE_KEY: &str = include_str!("../cql/get_stake_addr_w_vote_key.cql"); + +/// Get stake addr +#[derive(SerializeRow)] +pub(crate) struct GetStakeAddrFromVoteKeyParams { + /// Vote key. + pub vote_key: Vec, +} + +impl GetStakeAddrFromVoteKeyParams { + /// Create a new instance of [`GetStakeAddrParams`] + pub(crate) fn new(vote_key: Vec) -> GetStakeAddrFromVoteKeyParams { + Self { vote_key } + } +} + +/// Get stake addr given vote key +#[allow(clippy::expect_used)] +mod result { + use scylla::FromRow; + + /// Get Latest registration query result. + #[derive(FromRow)] + pub(crate) struct GetStakeAddrFromVoteKeyQuery { + /// Full Stake Address (not hashed, 32 byte ED25519 Public key). + pub stake_address: Vec, + } +} +/// Get latest registration query. +pub(crate) struct GetStakeAddrFromVoteKeyQuery; + +impl GetStakeAddrFromVoteKeyQuery { + /// Prepares a get stake addr from vote key query. + pub(crate) async fn prepare(session: Arc) -> anyhow::Result { + let get_stake_addr_query = PreparedQueries::prepare( + session, + GET_STAKE_ADDR_FROM_VOTE_KEY, + scylla::statement::Consistency::All, + true, + ) + .await; + + if let Err(ref error) = get_stake_addr_query { + error!(error=%error, "Failed to prepare get stake addr from vote key query."); + }; + + get_stake_addr_query + } + + /// Executes a get txi by transaction hashes query. + pub(crate) async fn execute( + session: &CassandraSession, params: GetStakeAddrFromVoteKeyParams, + ) -> anyhow::Result> { + let iter = session + .execute_iter(PreparedSelectQuery::StakeAddrFromVoteKey, params) + .await? + .into_typed::(); + + Ok(iter) + } +} diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs index 83b12c0787..7a565e7b52 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs @@ -1,3 +1,4 @@ //! Registration related queries. -pub(crate) mod get_latest_registration_w_stake_addr; -pub(crate) mod get_latest_registration_w_stake_hash; +pub(crate) mod get_latest_w_stake_addr; +pub(crate) mod get_latest_w_stake_hash; +pub(crate) mod get_latest_w_vote_key; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 05133aefde..195dd03993 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -7,10 +7,9 @@ use tracing::error; use crate::{ db::index::{ queries::registrations::{ - get_latest_registration_w_stake_addr::{ - GetLatestRegistrationParams, GetLatestRegistrationQuery, - }, - get_latest_registration_w_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, + get_latest_w_stake_addr::{GetLatestRegistrationParams, GetLatestRegistrationQuery}, + get_latest_w_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, + get_latest_w_vote_key::{GetStakeAddrFromVoteKeyParams, GetStakeAddrFromVoteKeyQuery}, }, session::CassandraSession, }, @@ -64,8 +63,6 @@ pub(crate) async fn get_latest_registration_from_stake_addr( }, }; - error!("go on!"); - let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); return Responses::NotFound.into(); @@ -214,3 +211,98 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( Responses::NotFound.into() } + +/// Get latest registration given vote key +pub(crate) async fn get_latest_registration_from_vote_key( + vote_key: String, persistent: bool, +) -> AllResponses { + let vote_key = match hex::decode(vote_key) { + Ok(vote_key) => vote_key, + Err(err) => { + error!("Failed to decode vote key {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let Some(session) = CassandraSession::get(persistent) else { + error!("Failed to acquire db session"); + return Responses::NotFound.into(); + }; + + let mut stake_addr_iter = match GetStakeAddrFromVoteKeyQuery::execute( + &session, + GetStakeAddrFromVoteKeyParams::new(vote_key), + ) + .await + { + Ok(latest) => latest, + Err(err) => { + error!("Failed to query stake addr from vote key {:?}", err); + return Responses::NotFound.into(); + }, + }; + + if let Some(row_stake_addr) = stake_addr_iter.next().await { + let row = match row_stake_addr { + Ok(r) => r, + Err(err) => { + error!("Failed to get latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let mut registrations_iter = match GetLatestRegistrationQuery::execute( + &session, + GetLatestRegistrationParams::new(row.stake_address), + ) + .await + { + Ok(latest) => latest, + Err(err) => { + error!("Failed to query latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + if let Some(row_latest_registration) = registrations_iter.next().await { + let row = match row_latest_registration { + Ok(r) => r, + Err(err) => { + error!("Failed to get latest registration {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { + *nonce + } else { + error!("Issue downcasting nonce"); + return Responses::NotFound.into(); + }; + + let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() + { + *slot_no + } else { + error!("Issue downcasting slot no"); + return Responses::NotFound.into(); + }; + + let cip36 = Cip36Info { + stake_address: row.stake_address, + nonce, + slot_no, + txn: row.txn, + vote_key: row.vote_key, + payment_address: row.payment_address, + is_payable: row.is_payable, + + cip36: row.cip36, + }; + + return Responses::Ok(Json(cip36)).into(); + } + } + + Responses::NotFound.into() +} diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index f541e05c6e..4fcfa26331 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -3,7 +3,6 @@ use poem_openapi::{ param::{Path, Query}, OpenApi, }; -use tracing::error; use types::{DateTime, SlotNumber}; use crate::service::{ @@ -147,8 +146,6 @@ impl CardanoApi { async fn latest_registration_cip36_given_stake_addr( &self, stake_addr: Query, ) -> cip36::AllResponses { - error!("zzz!"); - cip36::get_latest_registration_from_stake_addr(stake_addr.0, true).await } @@ -165,4 +162,18 @@ impl CardanoApi { ) -> cip36::AllResponses { cip36::get_latest_registration_from_stake_key_hash(stake_key_hash.0, true).await } + + #[oai( + path = "/cip36/latest_registration/vote_key", + method = "get", + operation_id = "latestRegistrationGivenVoteKey" + )] + /// Cip36 registrations + /// + /// This endpoint gets the latest registraton given a vote key + async fn latest_registration_cip36_given_vote_key( + &self, vote_key: Query, + ) -> cip36::AllResponses { + cip36::get_latest_registration_from_vote_key(vote_key.0, true).await + } } From c343d0e783bb799c70702269675ffcb4e6bb72f9 Mon Sep 17 00:00:00 2001 From: cong-or Date: Tue, 1 Oct 2024 12:17:56 +0000 Subject: [PATCH 14/37] feat(latest registration endpoints): given stake addr and stake key hash --- .../bin/src/service/api/cardano/cip36.rs | 43 +++++++++++++------ .../bin/src/service/api/cardano/mod.rs | 2 +- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 195dd03993..ac309e6b54 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -19,7 +19,7 @@ use crate::{ /// Endpoint responses #[derive(ApiResponse)] pub(crate) enum Responses { - /// The registration information for the stake address or stake key hashqueried. + /// Cip36 registration #[oai(status = 200)] Ok(Json), /// No valid registration found for the provided stake address or stake key hash @@ -27,6 +27,17 @@ pub(crate) enum Responses { NotFound, } +/// Endpoint responses +#[derive(ApiResponse)] +pub(crate) enum ResponsesVoteKey { + /// Cip36 registration + #[oai(status = 200)] + Ok(Json>), + /// No valid registration found for the provided stake address or stake key hash + #[oai(status = 404)] + NotFound, +} + /// Cip36 info #[derive(Object, Default)] pub(crate) struct Cip36Info { @@ -50,6 +61,8 @@ pub(crate) struct Cip36Info { /// All responses pub(crate) type AllResponses = WithErrorResponses; +/// All response vote key +pub(crate) type AllResponsesVoteKey = WithErrorResponses; /// Get latest registration given stake address pub(crate) async fn get_latest_registration_from_stake_addr( @@ -215,18 +228,18 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( /// Get latest registration given vote key pub(crate) async fn get_latest_registration_from_vote_key( vote_key: String, persistent: bool, -) -> AllResponses { +) -> AllResponsesVoteKey { let vote_key = match hex::decode(vote_key) { Ok(vote_key) => vote_key, Err(err) => { error!("Failed to decode vote key {:?}", err); - return Responses::NotFound.into(); + return ResponsesVoteKey::NotFound.into(); }, }; let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); - return Responses::NotFound.into(); + return ResponsesVoteKey::NotFound.into(); }; let mut stake_addr_iter = match GetStakeAddrFromVoteKeyQuery::execute( @@ -238,7 +251,7 @@ pub(crate) async fn get_latest_registration_from_vote_key( Ok(latest) => latest, Err(err) => { error!("Failed to query stake addr from vote key {:?}", err); - return Responses::NotFound.into(); + return ResponsesVoteKey::NotFound.into(); }, }; @@ -247,7 +260,7 @@ pub(crate) async fn get_latest_registration_from_vote_key( Ok(r) => r, Err(err) => { error!("Failed to get latest registration {:?}", err); - return Responses::NotFound.into(); + return ResponsesVoteKey::NotFound.into(); }, }; @@ -260,16 +273,18 @@ pub(crate) async fn get_latest_registration_from_vote_key( Ok(latest) => latest, Err(err) => { error!("Failed to query latest registration {:?}", err); - return Responses::NotFound.into(); + return ResponsesVoteKey::NotFound.into(); }, }; - if let Some(row_latest_registration) = registrations_iter.next().await { + // list of stake address registrations currently associated with a given voting key + let mut stake_addrs = Vec::new(); + while let Some(row_latest_registration) = registrations_iter.next().await { let row = match row_latest_registration { Ok(r) => r, Err(err) => { error!("Failed to get latest registration {:?}", err); - return Responses::NotFound.into(); + return ResponsesVoteKey::NotFound.into(); }, }; @@ -277,7 +292,7 @@ pub(crate) async fn get_latest_registration_from_vote_key( *nonce } else { error!("Issue downcasting nonce"); - return Responses::NotFound.into(); + return ResponsesVoteKey::NotFound.into(); }; let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() @@ -285,7 +300,7 @@ pub(crate) async fn get_latest_registration_from_vote_key( *slot_no } else { error!("Issue downcasting slot no"); - return Responses::NotFound.into(); + return ResponsesVoteKey::NotFound.into(); }; let cip36 = Cip36Info { @@ -300,9 +315,11 @@ pub(crate) async fn get_latest_registration_from_vote_key( cip36: row.cip36, }; - return Responses::Ok(Json(cip36)).into(); + stake_addrs.push(cip36); } + + return ResponsesVoteKey::Ok(Json(stake_addrs)).into(); } - Responses::NotFound.into() + ResponsesVoteKey::NotFound.into() } diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index 4fcfa26331..fadbbb8d58 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -173,7 +173,7 @@ impl CardanoApi { /// This endpoint gets the latest registraton given a vote key async fn latest_registration_cip36_given_vote_key( &self, vote_key: Query, - ) -> cip36::AllResponses { + ) -> cip36::AllResponsesVoteKey { cip36::get_latest_registration_from_vote_key(vote_key.0, true).await } } From e9797d7e935d23a7cc01b08d1f59b18f1db099d7 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 2 Oct 2024 10:56:40 +0000 Subject: [PATCH 15/37] feat(latest registration endpoints): given stake addr and stake key hash --- .../get_latest_registration_w_stake_addr.cql | 3 +- .../registrations/get_latest_w_stake_addr.rs | 2 +- .../bin/src/service/api/cardano/cip36.rs | 198 ++++++------------ .../bin/src/service/api/cardano/mod.rs | 2 +- 4 files changed, 64 insertions(+), 141 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql index c98be51da7..ce1b8619c0 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql @@ -7,5 +7,4 @@ SELECT stake_address, is_payable, cip36 FROM cip36_registration -WHERE stake_address = :stake_address -LIMIT 1; \ No newline at end of file +WHERE stake_address = :stake_address \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs index d80d853de5..d83f4336b7 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs @@ -80,7 +80,7 @@ impl GetLatestRegistrationQuery { get_latest_registration_query } - /// Executes a get txi by transaction hashes query. + /// Executes get registration info for given stake addr query. pub(crate) async fn execute( session: &CassandraSession, params: GetLatestRegistrationParams, ) -> anyhow::Result> { diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index ac309e6b54..512b20f293 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -1,5 +1,7 @@ //! Implementation of the GET `/registration/cip36` endpoint +use std::sync::Arc; + use futures::StreamExt; use poem_openapi::{payload::Json, ApiResponse, Object}; use tracing::error; @@ -27,17 +29,6 @@ pub(crate) enum Responses { NotFound, } -/// Endpoint responses -#[derive(ApiResponse)] -pub(crate) enum ResponsesVoteKey { - /// Cip36 registration - #[oai(status = 200)] - Ok(Json>), - /// No valid registration found for the provided stake address or stake key hash - #[oai(status = 404)] - NotFound, -} - /// Cip36 info #[derive(Object, Default)] pub(crate) struct Cip36Info { @@ -61,8 +52,6 @@ pub(crate) struct Cip36Info { /// All responses pub(crate) type AllResponses = WithErrorResponses; -/// All response vote key -pub(crate) type AllResponsesVoteKey = WithErrorResponses; /// Get latest registration given stake address pub(crate) async fn get_latest_registration_from_stake_addr( @@ -81,40 +70,44 @@ pub(crate) async fn get_latest_registration_from_stake_addr( return Responses::NotFound.into(); }; - let mut registrations_iter = match GetLatestRegistrationQuery::execute( - &session, - GetLatestRegistrationParams::new(stake_addr), - ) - .await + let registration = match latest_registration_from_stake_addr(stake_addr.clone(), session).await { - Ok(latest) => latest, + Ok(registrations) => registrations, Err(err) => { - error!("Failed to query latest registration {:?}", err); + error!( + "Failed to obtain registrations for given stake addr:{:?} {:?}", + hex::encode(stake_addr), + err + ); return Responses::NotFound.into(); }, }; - if let Some(row_res) = registrations_iter.next().await { - let row = match row_res { - Ok(r) => r, - Err(err) => { - error!("Failed to get latest registration {:?}", err); - return Responses::NotFound.into(); - }, - }; + Responses::Ok(Json(registration)).into() +} + +/// Get all cip36 registrations for a given stake address. +async fn latest_registration_from_stake_addr( + stake_addr: Vec, session: Arc, +) -> anyhow::Result { + let mut registrations_iter = + GetLatestRegistrationQuery::execute(&session, GetLatestRegistrationParams::new(stake_addr)) + .await?; + + let mut registrations = Vec::new(); + while let Some(row) = registrations_iter.next().await { + let row = row?; let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { *nonce } else { - error!("Issue downcasting nonce"); - return Responses::NotFound.into(); + continue; }; let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() { *slot_no } else { - error!("Issue downcasting slot no"); - return Responses::NotFound.into(); + continue; }; let cip36 = Cip36Info { @@ -128,10 +121,18 @@ pub(crate) async fn get_latest_registration_from_stake_addr( cip36: row.cip36, }; - return Responses::Ok(Json(cip36)).into(); + registrations.push(cip36); } - Responses::NotFound.into() + sort_latest_registration(registrations) +} + +/// Sort latest registrations for a given stake address sorting by slot no +fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { + registrations.sort_by_key(|k| k.slot_no); + registrations.into_iter().next().ok_or(anyhow::anyhow!( + "Can't sort latest registrations by slot no" + )) } /// Get latest registration given stake key hash @@ -151,6 +152,7 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( return Responses::NotFound.into(); }; + // Get stake addr assosciated with give stake hash let mut stake_addr_iter = match GetStakeAddrQuery::execute(&session, GetStakeAddrParams::new(stake_hash)).await { Ok(latest) => latest, @@ -169,57 +171,20 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( }, }; - let mut registrations_iter = match GetLatestRegistrationQuery::execute( - &session, - GetLatestRegistrationParams::new(row.stake_address), - ) - .await - { - Ok(latest) => latest, - Err(err) => { - error!("Failed to query latest registration {:?}", err); - return Responses::NotFound.into(); - }, - }; - - if let Some(row_latest_registration) = registrations_iter.next().await { - let row = match row_latest_registration { - Ok(r) => r, + let registration = + match latest_registration_from_stake_addr(row.stake_address.clone(), session).await { + Ok(registration) => registration, Err(err) => { - error!("Failed to get latest registration {:?}", err); + error!( + "Failed to obtain latest registration for given stake addr:{:?} {:?}", + hex::encode(row.stake_address), + err + ); return Responses::NotFound.into(); }, }; - let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { - *nonce - } else { - error!("Issue downcasting nonce"); - return Responses::NotFound.into(); - }; - - let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() - { - *slot_no - } else { - error!("Issue downcasting slot no"); - return Responses::NotFound.into(); - }; - - let cip36 = Cip36Info { - stake_address: row.stake_address, - nonce, - slot_no, - txn: row.txn, - vote_key: row.vote_key, - payment_address: row.payment_address, - is_payable: row.is_payable, - - cip36: row.cip36, - }; - - return Responses::Ok(Json(cip36)).into(); - } + return Responses::Ok(Json(registration)).into(); } Responses::NotFound.into() @@ -228,18 +193,18 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( /// Get latest registration given vote key pub(crate) async fn get_latest_registration_from_vote_key( vote_key: String, persistent: bool, -) -> AllResponsesVoteKey { +) -> AllResponses { let vote_key = match hex::decode(vote_key) { Ok(vote_key) => vote_key, Err(err) => { error!("Failed to decode vote key {:?}", err); - return ResponsesVoteKey::NotFound.into(); + return Responses::NotFound.into(); }, }; let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); - return ResponsesVoteKey::NotFound.into(); + return Responses::NotFound.into(); }; let mut stake_addr_iter = match GetStakeAddrFromVoteKeyQuery::execute( @@ -251,7 +216,7 @@ pub(crate) async fn get_latest_registration_from_vote_key( Ok(latest) => latest, Err(err) => { error!("Failed to query stake addr from vote key {:?}", err); - return ResponsesVoteKey::NotFound.into(); + return Responses::NotFound.into(); }, }; @@ -260,66 +225,25 @@ pub(crate) async fn get_latest_registration_from_vote_key( Ok(r) => r, Err(err) => { error!("Failed to get latest registration {:?}", err); - return ResponsesVoteKey::NotFound.into(); - }, - }; - - let mut registrations_iter = match GetLatestRegistrationQuery::execute( - &session, - GetLatestRegistrationParams::new(row.stake_address), - ) - .await - { - Ok(latest) => latest, - Err(err) => { - error!("Failed to query latest registration {:?}", err); - return ResponsesVoteKey::NotFound.into(); + return Responses::NotFound.into(); }, }; - // list of stake address registrations currently associated with a given voting key - let mut stake_addrs = Vec::new(); - while let Some(row_latest_registration) = registrations_iter.next().await { - let row = match row_latest_registration { - Ok(r) => r, + let registration = + match latest_registration_from_stake_addr(row.stake_address.clone(), session).await { + Ok(registration) => registration, Err(err) => { - error!("Failed to get latest registration {:?}", err); - return ResponsesVoteKey::NotFound.into(); + error!( + "Failed to obtain latest registration for given stake addr:{:?} {:?}", + hex::encode(row.stake_address), + err + ); + return Responses::NotFound.into(); }, }; - let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { - *nonce - } else { - error!("Issue downcasting nonce"); - return ResponsesVoteKey::NotFound.into(); - }; - - let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() - { - *slot_no - } else { - error!("Issue downcasting slot no"); - return ResponsesVoteKey::NotFound.into(); - }; - - let cip36 = Cip36Info { - stake_address: row.stake_address, - nonce, - slot_no, - txn: row.txn, - vote_key: row.vote_key, - payment_address: row.payment_address, - is_payable: row.is_payable, - - cip36: row.cip36, - }; - - stake_addrs.push(cip36); - } - - return ResponsesVoteKey::Ok(Json(stake_addrs)).into(); + return Responses::Ok(Json(registration)).into(); } - ResponsesVoteKey::NotFound.into() + Responses::NotFound.into() } diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index fadbbb8d58..4fcfa26331 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -173,7 +173,7 @@ impl CardanoApi { /// This endpoint gets the latest registraton given a vote key async fn latest_registration_cip36_given_vote_key( &self, vote_key: Query, - ) -> cip36::AllResponsesVoteKey { + ) -> cip36::AllResponses { cip36::get_latest_registration_from_vote_key(vote_key.0, true).await } } From ea81080f2550af1566a19cef77e0fc53cfc06ccb Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 2 Oct 2024 11:12:09 +0000 Subject: [PATCH 16/37] feat(latest registration endpoints): given stake addr and stake key hash --- .../get_latest_registration_w_stake_addr.cql | 10 ----- .../bin/src/db/index/queries/mod.rs | 24 ++++++----- .../registrations/get_latest_w_stake_addr.rs | 40 +++++++++---------- .../registrations/get_latest_w_stake_hash.rs | 8 ++-- .../registrations/get_latest_w_vote_key.rs | 4 +- .../bin/src/service/api/cardano/cip36.rs | 5 +-- 6 files changed, 42 insertions(+), 49 deletions(-) delete mode 100644 catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql deleted file mode 100644 index ce1b8619c0..0000000000 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_latest_registration_w_stake_addr.cql +++ /dev/null @@ -1,10 +0,0 @@ -SELECT stake_address, - nonce, - slot_no, - txn, - vote_key, - payment_address, - is_payable, - cip36 -FROM cip36_registration -WHERE stake_address = :stake_address \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index 6451e0faff..1353f387fc 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -10,8 +10,7 @@ use std::{fmt::Debug, sync::Arc}; use anyhow::{bail, Context}; use crossbeam_skiplist::SkipMap; use registrations::{ - get_latest_w_stake_addr::GetLatestRegistrationQuery, - get_latest_w_stake_hash::GetStakeAddrQuery, + get_latest_w_stake_addr::GetRegistrationQuery, get_latest_w_stake_hash::GetStakeAddrQuery, get_latest_w_vote_key::GetStakeAddrFromVoteKeyQuery, }; use scylla::{ @@ -63,8 +62,8 @@ pub(crate) enum PreparedSelectQuery { TxoByStakeAddress, /// Get TXI by transaction hash query. TxiByTransactionHash, - /// Get latest Registration - LatestRegistration, + /// Get Registration + RegistrationFromStakeAddr, /// Get stake addr from stake hash StakeAddrFromStakeHash, /// Get stake addr from vote key @@ -98,8 +97,8 @@ pub(crate) struct PreparedQueries { txo_by_stake_address_query: PreparedStatement, /// Get TXI by transaction hash. txi_by_txn_hash_query: PreparedStatement, - /// Get latest registration - latest_registration_query: PreparedStatement, + /// Get registrations + registration_from_stake_addr_query: PreparedStatement, /// stake addr from stake hash stake_addr_from_stake_hash_query: PreparedStatement, /// stake addr from vote key @@ -128,7 +127,8 @@ impl PreparedQueries { UpdateTxoSpentQuery::prepare_batch(session.clone(), cfg).await; let txo_by_stake_address_query = GetTxoByStakeAddressQuery::prepare(session.clone()).await; let txi_by_txn_hash_query = GetTxiByTxnHashesQuery::prepare(session.clone()).await; - let latest_registration_query = GetLatestRegistrationQuery::prepare(session.clone()).await; + let registration_from_stake_addr_query = + GetRegistrationQuery::prepare(session.clone()).await; let stake_addr_from_stake_hash = GetStakeAddrQuery::prepare(session.clone()).await; let stake_addr_from_vote_key = GetStakeAddrFromVoteKeyQuery::prepare(session.clone()).await; @@ -158,7 +158,7 @@ impl PreparedQueries { txo_spent_update_queries: txo_spent_update_queries?, txo_by_stake_address_query: txo_by_stake_address_query?, txi_by_txn_hash_query: txi_by_txn_hash_query?, - latest_registration_query: latest_registration_query?, + registration_from_stake_addr_query: registration_from_stake_addr_query?, stake_addr_from_stake_hash_query: stake_addr_from_stake_hash?, stake_addr_from_vote_key_query: stake_addr_from_vote_key?, }) @@ -214,11 +214,15 @@ impl PreparedQueries { pub(crate) async fn execute_iter

( &self, session: Arc, select_query: PreparedSelectQuery, params: P, ) -> anyhow::Result - where P: SerializeRow { + where + P: SerializeRow, + { let prepared_stmt = match select_query { PreparedSelectQuery::TxoByStakeAddress => &self.txo_by_stake_address_query, PreparedSelectQuery::TxiByTransactionHash => &self.txi_by_txn_hash_query, - PreparedSelectQuery::LatestRegistration => &self.latest_registration_query, + PreparedSelectQuery::RegistrationFromStakeAddr => { + &self.registration_from_stake_addr_query + }, PreparedSelectQuery::StakeAddrFromStakeHash => &self.stake_addr_from_stake_hash_query, PreparedSelectQuery::StakeAddrFromVoteKey => &self.stake_addr_from_vote_key_query, }; diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs index d83f4336b7..b6128285d7 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs @@ -13,34 +13,34 @@ use crate::db::index::{ session::CassandraSession, }; -/// Get latest registration query string. -const GET_LATEST_REGISTRATION_QUERY: &str = - include_str!("../cql/get_latest_registration_w_stake_addr.cql"); +/// Get registrations from stake addr query. +const GET_REGISTRATIONS_FROM_STAKE_ADDR_QUERY: &str = + include_str!("../cql/get_registrations_w_stake_addr.cql"); -/// Get latest registration +/// Get registration #[derive(SerializeRow)] -pub(crate) struct GetLatestRegistrationParams { +pub(crate) struct GetRegistrationParams { /// Stake address. pub stake_address: Vec, } -impl GetLatestRegistrationParams { +impl GetRegistrationParams { /// Create a new instance of [`GetLatestRegistrationParams`] - pub(crate) fn new(stake_addr: Vec) -> GetLatestRegistrationParams { + pub(crate) fn new(stake_addr: Vec) -> GetRegistrationParams { Self { stake_address: stake_addr, } } } -/// Get latest registration given stake addr or vote key +/// Get registration given stake addr or vote key #[allow(clippy::expect_used)] mod result { use scylla::FromRow; - /// Get Latest registration query result. + /// Get registration query result. #[derive(FromRow)] - pub(crate) struct GetLatestRegistrationQuery { + pub(crate) struct GetRegistrationQuery { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). pub stake_address: Vec, /// Nonce value after normalization. @@ -59,22 +59,22 @@ mod result { pub cip36: bool, } } -/// Get latest registration query. -pub(crate) struct GetLatestRegistrationQuery; +/// Get registration query. +pub(crate) struct GetRegistrationQuery; -impl GetLatestRegistrationQuery { - /// Prepares a get latest registration query. +impl GetRegistrationQuery { + /// Prepares a get registration query. pub(crate) async fn prepare(session: Arc) -> anyhow::Result { let get_latest_registration_query = PreparedQueries::prepare( session, - GET_LATEST_REGISTRATION_QUERY, + GET_REGISTRATIONS_FROM_STAKE_ADDR_QUERY, scylla::statement::Consistency::All, true, ) .await; if let Err(ref error) = get_latest_registration_query { - error!(error=%error, "Failed to prepare get latest registration query."); + error!(error=%error, "Failed to prepare get registration query."); }; get_latest_registration_query @@ -82,12 +82,12 @@ impl GetLatestRegistrationQuery { /// Executes get registration info for given stake addr query. pub(crate) async fn execute( - session: &CassandraSession, params: GetLatestRegistrationParams, - ) -> anyhow::Result> { + session: &CassandraSession, params: GetRegistrationParams, + ) -> anyhow::Result> { let iter = session - .execute_iter(PreparedSelectQuery::LatestRegistration, params) + .execute_iter(PreparedSelectQuery::RegistrationFromStakeAddr, params) .await? - .into_typed::(); + .into_typed::(); Ok(iter) } diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_hash.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_hash.rs index 235b2fea1b..ffa79e9d45 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_hash.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_hash.rs @@ -13,7 +13,7 @@ use crate::db::index::{ session::CassandraSession, }; -/// Get latest registration query string. +/// Get stake addr from stake hash query string. const GET_STAKE_ADDR_FROM_STAKE_HASH: &str = include_str!("../cql/get_stake_addr_w_stake_key_hash.cql"); @@ -31,19 +31,19 @@ impl GetStakeAddrParams { } } -/// Get latest registration given stake addr or vote key +/// Get stake addr from stake hash query string. #[allow(clippy::expect_used)] mod result { use scylla::FromRow; - /// Get Latest registration query result. + /// Get stake addr from stake hash query result. #[derive(FromRow)] pub(crate) struct GetStakeAddrQuery { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). pub stake_address: Vec, } } -/// Get latest registration query. +/// Get registration query. pub(crate) struct GetStakeAddrQuery; impl GetStakeAddrQuery { diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs index 1bf1b3eb23..6d427d2061 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs @@ -35,14 +35,14 @@ impl GetStakeAddrFromVoteKeyParams { mod result { use scylla::FromRow; - /// Get Latest registration query result. + /// Get stake addr from vote key query result. #[derive(FromRow)] pub(crate) struct GetStakeAddrFromVoteKeyQuery { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). pub stake_address: Vec, } } -/// Get latest registration query. +/// Get stake addr from vote key query. pub(crate) struct GetStakeAddrFromVoteKeyQuery; impl GetStakeAddrFromVoteKeyQuery { diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 512b20f293..970ebc547b 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -9,7 +9,7 @@ use tracing::error; use crate::{ db::index::{ queries::registrations::{ - get_latest_w_stake_addr::{GetLatestRegistrationParams, GetLatestRegistrationQuery}, + get_latest_w_stake_addr::{GetRegistrationParams, GetRegistrationQuery}, get_latest_w_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, get_latest_w_vote_key::{GetStakeAddrFromVoteKeyParams, GetStakeAddrFromVoteKeyQuery}, }, @@ -91,8 +91,7 @@ async fn latest_registration_from_stake_addr( stake_addr: Vec, session: Arc, ) -> anyhow::Result { let mut registrations_iter = - GetLatestRegistrationQuery::execute(&session, GetLatestRegistrationParams::new(stake_addr)) - .await?; + GetRegistrationQuery::execute(&session, GetRegistrationParams::new(stake_addr)).await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { From 1ad3ea139bdceb58cbb1c09e1e46f55892e85e31 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 2 Oct 2024 11:12:31 +0000 Subject: [PATCH 17/37] feat(latest registration endpoints): given stake addr and stake key hash --- .../queries/cql/get_registrations_w_stake_addr.cql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 catalyst-gateway/bin/src/db/index/queries/cql/get_registrations_w_stake_addr.cql diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_registrations_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_registrations_w_stake_addr.cql new file mode 100644 index 0000000000..ce1b8619c0 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_registrations_w_stake_addr.cql @@ -0,0 +1,10 @@ +SELECT stake_address, + nonce, + slot_no, + txn, + vote_key, + payment_address, + is_payable, + cip36 +FROM cip36_registration +WHERE stake_address = :stake_address \ No newline at end of file From 305bc0e9a11a80ce1470f26be4798f8efbeffecb Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 2 Oct 2024 11:14:21 +0000 Subject: [PATCH 18/37] feat(latest registration endpoints): given stake addr and stake key hash --- .../bin/src/db/index/queries/mod.rs | 4 +--- .../src/service/api/cardano/registration_get.rs | 17 +++++++++++++++++ utilities/local-scylla/justfile | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index 1353f387fc..febafd62bf 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -214,9 +214,7 @@ impl PreparedQueries { pub(crate) async fn execute_iter

( &self, session: Arc, select_query: PreparedSelectQuery, params: P, ) -> anyhow::Result - where - P: SerializeRow, - { + where P: SerializeRow { let prepared_stmt = match select_query { PreparedSelectQuery::TxoByStakeAddress => &self.txo_by_stake_address_query, PreparedSelectQuery::TxiByTransactionHash => &self.txi_by_txn_hash_query, diff --git a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs index 27f2f1b072..00c4e89d1b 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/registration_get.rs @@ -41,5 +41,22 @@ pub(crate) async fn endpoint( Err(err) => return AllResponses::handle_error(&err), }; + let _unused = " + // get the total utxo amount from the database + match EventDB::get_registration_info(stake_credential, network.into(), date_time).await { + Ok((tx_id, payment_address, voting_info, nonce)) => { + Responses::Ok(Json(RegistrationInfo::new( + tx_id, + &payment_address, + voting_info, + nonce, + ))) + .into() + }, + Err(err) if err.is::() => Responses::NotFound.into(), + Err(err) => AllResponses::handle_error(&err), + } + "; + Responses::NotFound.into() } diff --git a/utilities/local-scylla/justfile b/utilities/local-scylla/justfile index 722cbe36a7..2cbf82e207 100644 --- a/utilities/local-scylla/justfile +++ b/utilities/local-scylla/justfile @@ -21,7 +21,7 @@ scylla-dev-db-stop: # Local scylla dev DB CLUSTER - Starts with pre-existing data. scylla-dev-db-cluster: HOST_IP="{{host_ip}}" \ - docker compose up -d + docker compose up # Stop the scylla development DB CLUSTER scylla-dev-db-stop-cluster: From 526de9a89b12157d5f8f0b157ad5efe310f4e577 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 2 Oct 2024 12:33:47 +0000 Subject: [PATCH 19/37] refactor(stake addr voting key association): check pair is active stake addresses need to be individually checked to make sure they are still actively associated with the voting key, and have not been registered to another voting key. --- .../bin/src/service/api/cardano/cip36.rs | 219 ++++++++++-------- .../bin/src/service/api/cardano/mod.rs | 6 +- 2 files changed, 125 insertions(+), 100 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 970ebc547b..837c2f64db 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -24,7 +24,18 @@ pub(crate) enum Responses { /// Cip36 registration #[oai(status = 200)] Ok(Json), - /// No valid registration found for the provided stake address or stake key hash + /// No valid registration + #[oai(status = 404)] + NotFound, +} + +/// Endpoint responses +#[derive(ApiResponse)] +pub(crate) enum ResponsesVotingKey { + /// Cip36 registration + #[oai(status = 200)] + Ok(Json>), + /// No valid registration #[oai(status = 404)] NotFound, } @@ -52,89 +63,10 @@ pub(crate) struct Cip36Info { /// All responses pub(crate) type AllResponses = WithErrorResponses; +/// All responses voting key +pub(crate) type AllResponsesVotingKey = WithErrorResponses; -/// Get latest registration given stake address -pub(crate) async fn get_latest_registration_from_stake_addr( - stake_addr: String, persistent: bool, -) -> AllResponses { - let stake_addr = match hex::decode(stake_addr) { - Ok(stake_addr) => stake_addr, - Err(err) => { - error!("Failed to decode stake addr {:?}", err); - return Responses::NotFound.into(); - }, - }; - - let Some(session) = CassandraSession::get(persistent) else { - error!("Failed to acquire db session"); - return Responses::NotFound.into(); - }; - - let registration = match latest_registration_from_stake_addr(stake_addr.clone(), session).await - { - Ok(registrations) => registrations, - Err(err) => { - error!( - "Failed to obtain registrations for given stake addr:{:?} {:?}", - hex::encode(stake_addr), - err - ); - return Responses::NotFound.into(); - }, - }; - - Responses::Ok(Json(registration)).into() -} - -/// Get all cip36 registrations for a given stake address. -async fn latest_registration_from_stake_addr( - stake_addr: Vec, session: Arc, -) -> anyhow::Result { - let mut registrations_iter = - GetRegistrationQuery::execute(&session, GetRegistrationParams::new(stake_addr)).await?; - - let mut registrations = Vec::new(); - while let Some(row) = registrations_iter.next().await { - let row = row?; - - let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { - *nonce - } else { - continue; - }; - - let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() { - *slot_no - } else { - continue; - }; - - let cip36 = Cip36Info { - stake_address: row.stake_address, - nonce, - slot_no, - txn: row.txn, - vote_key: row.vote_key, - payment_address: row.payment_address, - is_payable: row.is_payable, - cip36: row.cip36, - }; - - registrations.push(cip36); - } - - sort_latest_registration(registrations) -} - -/// Sort latest registrations for a given stake address sorting by slot no -fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { - registrations.sort_by_key(|k| k.slot_no); - registrations.into_iter().next().ok_or(anyhow::anyhow!( - "Can't sort latest registrations by slot no" - )) -} - -/// Get latest registration given stake key hash +/// Get latest registration given a stake key hash. pub(crate) async fn get_latest_registration_from_stake_key_hash( stake_hash: String, persistent: bool, ) -> AllResponses { @@ -175,7 +107,7 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( Ok(registration) => registration, Err(err) => { error!( - "Failed to obtain latest registration for given stake addr:{:?} {:?}", + "Failed to obtain registration for given stake addr:{:?} {:?}", hex::encode(row.stake_address), err ); @@ -189,21 +121,21 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( Responses::NotFound.into() } -/// Get latest registration given vote key -pub(crate) async fn get_latest_registration_from_vote_key( +/// Returns the list of stake address registrations currently associated with a given voting key. +pub(crate) async fn get_asscociated_vote_key_registrations( vote_key: String, persistent: bool, -) -> AllResponses { +) -> AllResponsesVotingKey { let vote_key = match hex::decode(vote_key) { Ok(vote_key) => vote_key, Err(err) => { error!("Failed to decode vote key {:?}", err); - return Responses::NotFound.into(); + return ResponsesVotingKey::NotFound.into(); }, }; let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); - return Responses::NotFound.into(); + return ResponsesVotingKey::NotFound.into(); }; let mut stake_addr_iter = match GetStakeAddrFromVoteKeyQuery::execute( @@ -215,7 +147,7 @@ pub(crate) async fn get_latest_registration_from_vote_key( Ok(latest) => latest, Err(err) => { error!("Failed to query stake addr from vote key {:?}", err); - return Responses::NotFound.into(); + return ResponsesVotingKey::NotFound.into(); }, }; @@ -224,25 +156,118 @@ pub(crate) async fn get_latest_registration_from_vote_key( Ok(r) => r, Err(err) => { error!("Failed to get latest registration {:?}", err); - return Responses::NotFound.into(); + return ResponsesVotingKey::NotFound.into(); }, }; - let registration = - match latest_registration_from_stake_addr(row.stake_address.clone(), session).await { + let registrations = + match get_all_registrations_from_stake_addr(session, row.stake_address.clone()).await { Ok(registration) => registration, Err(err) => { error!( - "Failed to obtain latest registration for given stake addr:{:?} {:?}", + "Failed to obtain registrations for given stake addr:{:?} {:?}", hex::encode(row.stake_address), err ); - return Responses::NotFound.into(); + return ResponsesVotingKey::NotFound.into(); }, }; - return Responses::Ok(Json(registration)).into(); + let registrations = check_stake_addr_voting_key_association(registrations); + return ResponsesVotingKey::Ok(Json(registrations)).into(); } - Responses::NotFound.into() + ResponsesVotingKey::NotFound.into() +} + +/// Get latest registration given a stake address +pub(crate) async fn get_latest_registration_from_stake_addr( + stake_addr: String, persistent: bool, +) -> AllResponses { + let stake_addr = match hex::decode(stake_addr) { + Ok(stake_addr) => stake_addr, + Err(err) => { + error!("Failed to decode stake addr {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let Some(session) = CassandraSession::get(persistent) else { + error!("Failed to acquire db session"); + return Responses::NotFound.into(); + }; + + let registration = match latest_registration_from_stake_addr(stake_addr.clone(), session).await + { + Ok(registrations) => registrations, + Err(err) => { + error!( + "Failed to obtain registrations for given stake addr:{:?} {:?}", + hex::encode(stake_addr), + err + ); + return Responses::NotFound.into(); + }, + }; + + Responses::Ok(Json(registration)).into() +} + +/// Get latest registration given a stake addr +async fn latest_registration_from_stake_addr( + stake_addr: Vec, session: Arc, +) -> anyhow::Result { + sort_latest_registration(get_all_registrations_from_stake_addr(session, stake_addr).await?) +} + +/// Get all cip36 registrations for a given stake address. +async fn get_all_registrations_from_stake_addr( + session: Arc, stake_addr: Vec, +) -> Result, anyhow::Error> { + let mut registrations_iter = + GetRegistrationQuery::execute(&session, GetRegistrationParams::new(stake_addr)).await?; + let mut registrations = Vec::new(); + while let Some(row) = registrations_iter.next().await { + let row = row?; + + let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { + *nonce + } else { + continue; + }; + + let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() { + *slot_no + } else { + continue; + }; + + let cip36 = Cip36Info { + stake_address: row.stake_address, + nonce, + slot_no, + txn: row.txn, + vote_key: row.vote_key, + payment_address: row.payment_address, + is_payable: row.is_payable, + cip36: row.cip36, + }; + + registrations.push(cip36); + } + Ok(registrations) +} + +/// Sort latest registrations for a given stake address sorting by slot no +fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { + registrations.sort_by_key(|k| k.slot_no); + registrations.into_iter().next().ok_or(anyhow::anyhow!( + "Can't sort latest registrations by slot no" + )) +} + +/// Stake addresses need to be individually checked to make sure they are still actively associated with the voting key, +/// and have not been registered to another voting key. +fn check_stake_addr_voting_key_association(registrations: Vec) -> Vec { + registrations } diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index 4fcfa26331..25856f0ee1 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -170,10 +170,10 @@ impl CardanoApi { )] /// Cip36 registrations /// - /// This endpoint gets the latest registraton given a vote key + /// This endpoint returns the list of stake address registrations currently associated with a given voting key. async fn latest_registration_cip36_given_vote_key( &self, vote_key: Query, - ) -> cip36::AllResponses { - cip36::get_latest_registration_from_vote_key(vote_key.0, true).await + ) -> cip36::AllResponsesVotingKey { + cip36::get_asscociated_vote_key_registrations(vote_key.0, true).await } } From 21ed8be36c8178503e5f1c842e8ecfede7f90e53 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 2 Oct 2024 16:31:10 +0000 Subject: [PATCH 20/37] feat(invalids): add error reporting in resp --- .../get_invalid_registration_w_stake_addr.cql | 3 + .../bin/src/db/index/queries/mod.rs | 15 +- ...w_stake_addr.rs => get_from_stake_addr.rs} | 6 +- ...w_stake_hash.rs => get_from_stake_hash.rs} | 0 ...est_w_vote_key.rs => get_from_vote_key.rs} | 0 .../queries/registrations/get_invalid.rs | 83 +++++ .../src/db/index/queries/registrations/mod.rs | 7 +- .../bin/src/service/api/cardano/cip36.rs | 284 +++++++++++------- .../bin/src/service/api/cardano/mod.rs | 3 +- 9 files changed, 290 insertions(+), 111 deletions(-) create mode 100644 catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql rename catalyst-gateway/bin/src/db/index/queries/registrations/{get_latest_w_stake_addr.rs => get_from_stake_addr.rs} (94%) rename catalyst-gateway/bin/src/db/index/queries/registrations/{get_latest_w_stake_hash.rs => get_from_stake_hash.rs} (100%) rename catalyst-gateway/bin/src/db/index/queries/registrations/{get_latest_w_vote_key.rs => get_from_vote_key.rs} (100%) create mode 100644 catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql new file mode 100644 index 0000000000..ee3fe78abb --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql @@ -0,0 +1,3 @@ +SELECT error_report +FROM cip36_registration_invalid +WHERE stake_address = :stake_address \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index febafd62bf..b59d70eeb9 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -10,8 +10,8 @@ use std::{fmt::Debug, sync::Arc}; use anyhow::{bail, Context}; use crossbeam_skiplist::SkipMap; use registrations::{ - get_latest_w_stake_addr::GetRegistrationQuery, get_latest_w_stake_hash::GetStakeAddrQuery, - get_latest_w_vote_key::GetStakeAddrFromVoteKeyQuery, + get_from_stake_addr::GetRegistrationQuery, get_from_stake_hash::GetStakeAddrQuery, + get_from_vote_key::GetStakeAddrFromVoteKeyQuery, get_invalid::GetInvalidRegistrationQuery, }; use scylla::{ batch::Batch, prepared_statement::PreparedStatement, serialize::row::SerializeRow, @@ -62,8 +62,10 @@ pub(crate) enum PreparedSelectQuery { TxoByStakeAddress, /// Get TXI by transaction hash query. TxiByTransactionHash, - /// Get Registration + /// Get Registrations RegistrationFromStakeAddr, + /// Get invalid Registration + InvalidRegistrationsFromStakeAddr, /// Get stake addr from stake hash StakeAddrFromStakeHash, /// Get stake addr from vote key @@ -103,6 +105,8 @@ pub(crate) struct PreparedQueries { stake_addr_from_stake_hash_query: PreparedStatement, /// stake addr from vote key stake_addr_from_vote_key_query: PreparedStatement, + /// Get invalid registrations + invalid_registrations_from_stake_addr_query: PreparedStatement, } /// An individual query response that can fail @@ -131,6 +135,7 @@ impl PreparedQueries { GetRegistrationQuery::prepare(session.clone()).await; let stake_addr_from_stake_hash = GetStakeAddrQuery::prepare(session.clone()).await; let stake_addr_from_vote_key = GetStakeAddrFromVoteKeyQuery::prepare(session.clone()).await; + let invalid_registrations = GetInvalidRegistrationQuery::prepare(session.clone()).await; let ( txo_insert_queries, @@ -161,6 +166,7 @@ impl PreparedQueries { registration_from_stake_addr_query: registration_from_stake_addr_query?, stake_addr_from_stake_hash_query: stake_addr_from_stake_hash?, stake_addr_from_vote_key_query: stake_addr_from_vote_key?, + invalid_registrations_from_stake_addr_query: invalid_registrations?, }) } @@ -223,6 +229,9 @@ impl PreparedQueries { }, PreparedSelectQuery::StakeAddrFromStakeHash => &self.stake_addr_from_stake_hash_query, PreparedSelectQuery::StakeAddrFromVoteKey => &self.stake_addr_from_vote_key_query, + PreparedSelectQuery::InvalidRegistrationsFromStakeAddr => { + &self.invalid_registrations_from_stake_addr_query + }, }; session diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs similarity index 94% rename from catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs rename to catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs index b6128285d7..21a24896de 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs @@ -65,7 +65,7 @@ pub(crate) struct GetRegistrationQuery; impl GetRegistrationQuery { /// Prepares a get registration query. pub(crate) async fn prepare(session: Arc) -> anyhow::Result { - let get_latest_registration_query = PreparedQueries::prepare( + let get_registrations_query = PreparedQueries::prepare( session, GET_REGISTRATIONS_FROM_STAKE_ADDR_QUERY, scylla::statement::Consistency::All, @@ -73,11 +73,11 @@ impl GetRegistrationQuery { ) .await; - if let Err(ref error) = get_latest_registration_query { + if let Err(ref error) = get_registrations_query { error!(error=%error, "Failed to prepare get registration query."); }; - get_latest_registration_query + get_registrations_query } /// Executes get registration info for given stake addr query. diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_hash.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs similarity index 100% rename from catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_stake_hash.rs rename to catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs similarity index 100% rename from catalyst-gateway/bin/src/db/index/queries/registrations/get_latest_w_vote_key.rs rename to catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs new file mode 100644 index 0000000000..e6fccfa3db --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs @@ -0,0 +1,83 @@ +//! Get TXI by Transaction hash query + +use std::sync::Arc; + +use scylla::{ + prepared_statement::PreparedStatement, transport::iterator::TypedRowIterator, SerializeRow, + Session, +}; +use tracing::error; + +use crate::db::index::{ + queries::{PreparedQueries, PreparedSelectQuery}, + session::CassandraSession, +}; + +/// Get invalid registrations from stake addr query. +const GET_INVALID_REGISTRATIONS_FROM_STAKE_ADDR_QUERY: &str = + include_str!("../cql/get_invalid_registration_w_stake_addr.cql"); + +/// Get registration +#[derive(SerializeRow)] +pub(crate) struct GetInvalidRegistrationParams { + /// Stake address. + pub stake_address: Vec, +} + +impl GetInvalidRegistrationParams { + /// Create a new instance of [`GetInvalidRegistrationParams`] + pub(crate) fn new(stake_addr: Vec) -> GetInvalidRegistrationParams { + Self { + stake_address: stake_addr, + } + } +} + +/// Get invalid registrations given stake addr +#[allow(clippy::expect_used)] +mod result { + use scylla::FromRow; + + /// Get registration query result. + #[derive(FromRow)] + pub(crate) struct GetInvalidRegistrationQuery { + /// Error report + pub error_report: Vec, + } +} +/// Get invalid registration query. +pub(crate) struct GetInvalidRegistrationQuery; + +impl GetInvalidRegistrationQuery { + /// Prepares a get invalid registration query. + pub(crate) async fn prepare(session: Arc) -> anyhow::Result { + let get_invalid_registration_query = PreparedQueries::prepare( + session, + GET_INVALID_REGISTRATIONS_FROM_STAKE_ADDR_QUERY, + scylla::statement::Consistency::All, + true, + ) + .await; + + if let Err(ref error) = get_invalid_registration_query { + error!(error=%error, "Failed to prepare get registration query."); + }; + + get_invalid_registration_query + } + + /// Executes get invalid registration info for given stake addr query. + pub(crate) async fn execute( + session: &CassandraSession, params: GetInvalidRegistrationParams, + ) -> anyhow::Result> { + let iter = session + .execute_iter( + PreparedSelectQuery::InvalidRegistrationsFromStakeAddr, + params, + ) + .await? + .into_typed::(); + + Ok(iter) + } +} diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs index 7a565e7b52..b0f2edfa4e 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs @@ -1,4 +1,5 @@ //! Registration related queries. -pub(crate) mod get_latest_w_stake_addr; -pub(crate) mod get_latest_w_stake_hash; -pub(crate) mod get_latest_w_vote_key; +pub(crate) mod get_from_stake_addr; +pub(crate) mod get_from_stake_hash; +pub(crate) mod get_from_vote_key; +pub(crate) mod get_invalid; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 837c2f64db..be36c8943e 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -9,9 +9,10 @@ use tracing::error; use crate::{ db::index::{ queries::registrations::{ - get_latest_w_stake_addr::{GetRegistrationParams, GetRegistrationQuery}, - get_latest_w_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, - get_latest_w_vote_key::{GetStakeAddrFromVoteKeyParams, GetStakeAddrFromVoteKeyQuery}, + get_from_stake_addr::{GetRegistrationParams, GetRegistrationQuery}, + get_from_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, + get_from_vote_key::{GetStakeAddrFromVoteKeyParams, GetStakeAddrFromVoteKeyQuery}, + get_invalid::{GetInvalidRegistrationParams, GetInvalidRegistrationQuery}, }, session::CassandraSession, }, @@ -23,7 +24,7 @@ use crate::{ pub(crate) enum Responses { /// Cip36 registration #[oai(status = 200)] - Ok(Json), + Ok(Json), /// No valid registration #[oai(status = 404)] NotFound, @@ -40,6 +41,15 @@ pub(crate) enum ResponsesVotingKey { NotFound, } +/// Cip36 info + invalid reporting +#[derive(Object, Default)] +pub(crate) struct Cip36Reporting { + /// Cip36 info + cip36: Vec, + /// Invald registration reporting + invalids: Vec, +} + /// Cip36 info #[derive(Object, Default)] pub(crate) struct Cip36Info { @@ -61,11 +71,154 @@ pub(crate) struct Cip36Info { pub cip36: bool, } +/// Invalid registration error reporting +#[derive(Object, Default)] +pub(crate) struct InvalidRegistrationsReport { + /// Error report + pub error_report: Vec, +} + /// All responses pub(crate) type AllResponses = WithErrorResponses; /// All responses voting key pub(crate) type AllResponsesVotingKey = WithErrorResponses; +/// Get latest registration given a stake address +pub(crate) async fn get_latest_registration_from_stake_addr( + stake_addr: String, persistent: bool, +) -> AllResponses { + let stake_addr = match hex::decode(stake_addr) { + Ok(stake_addr) => stake_addr, + Err(err) => { + error!("Failed to decode stake addr {:?}", err); + return Responses::NotFound.into(); + }, + }; + + let Some(session) = CassandraSession::get(persistent) else { + error!("Failed to acquire db session"); + return Responses::NotFound.into(); + }; + + let registration = + match latest_registration_from_stake_addr(stake_addr.clone(), session.clone()).await { + Ok(registrations) => registrations, + Err(err) => { + error!( + "Failed to obtain registrations for given stake addr:{:?} {:?}", + hex::encode(stake_addr), + err + ); + return Responses::NotFound.into(); + }, + }; + + let invalids_report = + match get_invalid_registrations(registration.stake_address.clone(), session).await { + Ok(invalids) => invalids, + Err(err) => { + error!( + "Failed to obtain invalid registrations for given stake addr:{:?} {:?}", + hex::encode(stake_addr), + err + ); + return Responses::NotFound.into(); + }, + }; + + let report = Cip36Reporting { + cip36: vec![registration], + invalids: invalids_report, + }; + + Responses::Ok(Json(report)).into() +} + +/// Get latest registration given a stake addr +async fn latest_registration_from_stake_addr( + stake_addr: Vec, session: Arc, +) -> anyhow::Result { + sort_latest_registration(get_all_registrations_from_stake_addr(session, stake_addr).await?) +} + +/// Get all cip36 registrations for a given stake address. +async fn get_all_registrations_from_stake_addr( + session: Arc, stake_addr: Vec, +) -> Result, anyhow::Error> { + let mut registrations_iter = + GetRegistrationQuery::execute(&session, GetRegistrationParams::new(stake_addr)).await?; + let mut registrations = Vec::new(); + while let Some(row) = registrations_iter.next().await { + let row = row?; + + let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { + *nonce + } else { + continue; + }; + + let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() { + *slot_no + } else { + continue; + }; + + let cip36 = Cip36Info { + stake_address: row.stake_address, + nonce, + slot_no, + txn: row.txn, + vote_key: row.vote_key, + payment_address: row.payment_address, + is_payable: row.is_payable, + cip36: row.cip36, + }; + + registrations.push(cip36); + } + Ok(registrations) +} + +/// Sort latest registrations for a given stake address sorting by slot no +fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { + registrations.sort_by_key(|k| k.slot_no); + registrations.into_iter().next().ok_or(anyhow::anyhow!( + "Can't sort latest registrations by slot no" + )) +} + +/// Get invalid registrations for stake addr after given slot no +async fn get_invalid_registrations( + stake_addr: Vec, session: Arc, +) -> anyhow::Result> { + let mut invalid_registrations_iter = GetInvalidRegistrationQuery::execute( + &session, + GetInvalidRegistrationParams::new(stake_addr), + ) + .await?; + let mut invalid_registrations = Vec::new(); + while let Some(row) = invalid_registrations_iter.next().await { + let row = row?; + + invalid_registrations.push(InvalidRegistrationsReport { + error_report: row.error_report, + }); + } + + Ok(invalid_registrations) +} + +/// Stake addresses need to be individually checked to make sure they are still actively +/// associated with the voting key, and have not been registered to another voting key. +fn check_stake_addr_voting_key_association( + registrations: Vec, associated_voting_key: &[u8], +) -> Vec { + registrations + .into_iter() + .filter(|r| r.vote_key == associated_voting_key) + .collect() +} + /// Get latest registration given a stake key hash. pub(crate) async fn get_latest_registration_from_stake_key_hash( stake_hash: String, persistent: bool, @@ -103,7 +256,9 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( }; let registration = - match latest_registration_from_stake_addr(row.stake_address.clone(), session).await { + match latest_registration_from_stake_addr(row.stake_address.clone(), session.clone()) + .await + { Ok(registration) => registration, Err(err) => { error!( @@ -115,13 +270,32 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( }, }; - return Responses::Ok(Json(registration)).into(); + let invalids_report = + match get_invalid_registrations(registration.stake_address.clone(), session).await { + Ok(invalids) => invalids, + Err(err) => { + error!( + "Failed to obtain invalid registrations for given stake addr:{:?} {:?}", + hex::encode(registration.stake_address.clone()), + err + ); + return Responses::NotFound.into(); + }, + }; + + let report = Cip36Reporting { + cip36: vec![registration], + invalids: invalids_report, + }; + + return Responses::Ok(Json(report)).into(); } Responses::NotFound.into() } -/// Returns the list of stake address registrations currently associated with a given voting key. +/// Returns the list of stake address registrations currently associated with a given +/// voting key. pub(crate) async fn get_asscociated_vote_key_registrations( vote_key: String, persistent: bool, ) -> AllResponsesVotingKey { @@ -140,7 +314,7 @@ pub(crate) async fn get_asscociated_vote_key_registrations( let mut stake_addr_iter = match GetStakeAddrFromVoteKeyQuery::execute( &session, - GetStakeAddrFromVoteKeyParams::new(vote_key), + GetStakeAddrFromVoteKeyParams::new(vote_key.clone()), ) .await { @@ -173,101 +347,9 @@ pub(crate) async fn get_asscociated_vote_key_registrations( }, }; - let registrations = check_stake_addr_voting_key_association(registrations); + let registrations = check_stake_addr_voting_key_association(registrations, &vote_key); return ResponsesVotingKey::Ok(Json(registrations)).into(); } ResponsesVotingKey::NotFound.into() } - -/// Get latest registration given a stake address -pub(crate) async fn get_latest_registration_from_stake_addr( - stake_addr: String, persistent: bool, -) -> AllResponses { - let stake_addr = match hex::decode(stake_addr) { - Ok(stake_addr) => stake_addr, - Err(err) => { - error!("Failed to decode stake addr {:?}", err); - return Responses::NotFound.into(); - }, - }; - - let Some(session) = CassandraSession::get(persistent) else { - error!("Failed to acquire db session"); - return Responses::NotFound.into(); - }; - - let registration = match latest_registration_from_stake_addr(stake_addr.clone(), session).await - { - Ok(registrations) => registrations, - Err(err) => { - error!( - "Failed to obtain registrations for given stake addr:{:?} {:?}", - hex::encode(stake_addr), - err - ); - return Responses::NotFound.into(); - }, - }; - - Responses::Ok(Json(registration)).into() -} - -/// Get latest registration given a stake addr -async fn latest_registration_from_stake_addr( - stake_addr: Vec, session: Arc, -) -> anyhow::Result { - sort_latest_registration(get_all_registrations_from_stake_addr(session, stake_addr).await?) -} - -/// Get all cip36 registrations for a given stake address. -async fn get_all_registrations_from_stake_addr( - session: Arc, stake_addr: Vec, -) -> Result, anyhow::Error> { - let mut registrations_iter = - GetRegistrationQuery::execute(&session, GetRegistrationParams::new(stake_addr)).await?; - let mut registrations = Vec::new(); - while let Some(row) = registrations_iter.next().await { - let row = row?; - - let nonce = if let Some(nonce) = row.nonce.into_parts().1.to_u64_digits().first() { - *nonce - } else { - continue; - }; - - let slot_no = if let Some(slot_no) = row.slot_no.into_parts().1.to_u64_digits().first() { - *slot_no - } else { - continue; - }; - - let cip36 = Cip36Info { - stake_address: row.stake_address, - nonce, - slot_no, - txn: row.txn, - vote_key: row.vote_key, - payment_address: row.payment_address, - is_payable: row.is_payable, - cip36: row.cip36, - }; - - registrations.push(cip36); - } - Ok(registrations) -} - -/// Sort latest registrations for a given stake address sorting by slot no -fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { - registrations.sort_by_key(|k| k.slot_no); - registrations.into_iter().next().ok_or(anyhow::anyhow!( - "Can't sort latest registrations by slot no" - )) -} - -/// Stake addresses need to be individually checked to make sure they are still actively associated with the voting key, -/// and have not been registered to another voting key. -fn check_stake_addr_voting_key_association(registrations: Vec) -> Vec { - registrations -} diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index 25856f0ee1..059b4b9945 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -170,7 +170,8 @@ impl CardanoApi { )] /// Cip36 registrations /// - /// This endpoint returns the list of stake address registrations currently associated with a given voting key. + /// This endpoint returns the list of stake address registrations currently associated + /// with a given voting key. async fn latest_registration_cip36_given_vote_key( &self, vote_key: Query, ) -> cip36::AllResponsesVotingKey { From 8a67bab304622bffedc3390c93d828ab3f4b672f Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 3 Oct 2024 10:35:33 +0000 Subject: [PATCH 21/37] feat(error reporting): invalids include any erroneous registrations which occur AFTER the slot# of the last valid registration --- .../get_invalid_registration_w_stake_addr.cql | 7 +- .../queries/registrations/get_invalid.rs | 13 +++- .../bin/src/service/api/cardano/cip36.rs | 76 ++++++++++++------- 3 files changed, 64 insertions(+), 32 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql index ee3fe78abb..1c2cfa76c8 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql @@ -1,3 +1,6 @@ -SELECT error_report +SELECT error_report, + stake_address, + vote_key FROM cip36_registration_invalid -WHERE stake_address = :stake_address \ No newline at end of file +WHERE stake_address = :stake_address + AND slot_no >= :slot_no \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs index e6fccfa3db..122fd660a2 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs @@ -22,13 +22,18 @@ const GET_INVALID_REGISTRATIONS_FROM_STAKE_ADDR_QUERY: &str = pub(crate) struct GetInvalidRegistrationParams { /// Stake address. pub stake_address: Vec, + /// Block Slot Number when spend occurred. + slot_no: num_bigint::BigInt, } impl GetInvalidRegistrationParams { /// Create a new instance of [`GetInvalidRegistrationParams`] - pub(crate) fn new(stake_addr: Vec) -> GetInvalidRegistrationParams { + pub(crate) fn new( + stake_address: Vec, slot_no: num_bigint::BigInt, + ) -> GetInvalidRegistrationParams { Self { - stake_address: stake_addr, + stake_address, + slot_no, } } } @@ -43,6 +48,10 @@ mod result { pub(crate) struct GetInvalidRegistrationQuery { /// Error report pub error_report: Vec, + /// Full Stake Address (not hashed, 32 byte ED25519 Public key). + pub stake_address: Vec, + /// Voting Public Key + pub vote_key: Vec, } } /// Get invalid registration query. diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index be36c8943e..b37fd3f3c8 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -54,7 +54,7 @@ pub(crate) struct Cip36Reporting { #[derive(Object, Default)] pub(crate) struct Cip36Info { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). - pub stake_address: Vec, + pub stake_address: String, /// Nonce value after normalization. pub nonce: u64, /// Slot Number the cert is in. @@ -76,6 +76,10 @@ pub(crate) struct Cip36Info { pub(crate) struct InvalidRegistrationsReport { /// Error report pub error_report: Vec, + /// Full Stake Address (not hashed, 32 byte ED25519 Public key). + pub stake_address: String, + /// Voting Public Key + pub vote_key: String, } /// All responses @@ -113,18 +117,23 @@ pub(crate) async fn get_latest_registration_from_stake_addr( }, }; - let invalids_report = - match get_invalid_registrations(registration.stake_address.clone(), session).await { - Ok(invalids) => invalids, - Err(err) => { - error!( - "Failed to obtain invalid registrations for given stake addr:{:?} {:?}", - hex::encode(stake_addr), - err - ); - return Responses::NotFound.into(); - }, - }; + let invalids_report = match get_invalid_registrations( + registration.stake_address.clone(), + registration.slot_no.into(), + session, + ) + .await + { + Ok(invalids) => invalids, + Err(err) => { + error!( + "Failed to obtain invalid registrations for given stake addr:{:?} {:?}", + hex::encode(stake_addr), + err + ); + return Responses::NotFound.into(); + }, + }; let report = Cip36Reporting { cip36: vec![registration], @@ -164,7 +173,7 @@ async fn get_all_registrations_from_stake_addr( }; let cip36 = Cip36Info { - stake_address: row.stake_address, + stake_address: hex::encode(row.stake_address), nonce, slot_no, txn: row.txn, @@ -189,11 +198,13 @@ fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result /// Get invalid registrations for stake addr after given slot no async fn get_invalid_registrations( - stake_addr: Vec, session: Arc, + stake_addr: String, slot_no: num_bigint::BigInt, session: Arc, ) -> anyhow::Result> { + let stake_addr = hex::decode(stake_addr)?; + let mut invalid_registrations_iter = GetInvalidRegistrationQuery::execute( &session, - GetInvalidRegistrationParams::new(stake_addr), + GetInvalidRegistrationParams::new(stake_addr, slot_no), ) .await?; let mut invalid_registrations = Vec::new(); @@ -202,6 +213,8 @@ async fn get_invalid_registrations( invalid_registrations.push(InvalidRegistrationsReport { error_report: row.error_report, + stake_address: hex::encode(row.stake_address), + vote_key: hex::encode(row.vote_key), }); } @@ -270,18 +283,25 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( }, }; - let invalids_report = - match get_invalid_registrations(registration.stake_address.clone(), session).await { - Ok(invalids) => invalids, - Err(err) => { - error!( - "Failed to obtain invalid registrations for given stake addr:{:?} {:?}", - hex::encode(registration.stake_address.clone()), - err - ); - return Responses::NotFound.into(); - }, - }; + // include any erroneous registrations which occur AFTER the slot# of the last valid + // registration + let invalids_report = match get_invalid_registrations( + registration.stake_address.clone(), + registration.slot_no.into(), + session, + ) + .await + { + Ok(invalids) => invalids, + Err(err) => { + error!( + "Failed to obtain invalid registrations for given stake addr:{:?} {:?}", + hex::encode(registration.stake_address.clone()), + err + ); + return Responses::NotFound.into(); + }, + }; let report = Cip36Reporting { cip36: vec![registration], From f20e71f63bf5da1bfd9d662457d213f134e3857e Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 3 Oct 2024 10:52:35 +0000 Subject: [PATCH 22/37] feat(error reporting): invalids include any erroneous registrations which occur AFTER the slot# of the last valid registration --- .../get_invalid_registration_w_stake_addr.cql | 5 +++- .../queries/registrations/get_invalid.rs | 6 +++++ .../bin/src/service/api/cardano/cip36.rs | 26 ++++++++++++++----- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql index 1c2cfa76c8..1ccdb03629 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_invalid_registration_w_stake_addr.cql @@ -1,6 +1,9 @@ SELECT error_report, stake_address, - vote_key + vote_key, + payment_address, + is_payable, + cip36 FROM cip36_registration_invalid WHERE stake_address = :stake_address AND slot_no >= :slot_no \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs index 122fd660a2..0c3e971cd0 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs @@ -52,6 +52,12 @@ mod result { pub stake_address: Vec, /// Voting Public Key pub vote_key: Vec, + /// Full Payment Address (not hashed, 32 byte ED25519 Public key). + pub payment_address: Vec, + /// Is the stake address a script or not. + pub is_payable: bool, + /// Is the Registration CIP36 format, or CIP15 + pub cip36: bool, } } /// Get invalid registration query. diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index b37fd3f3c8..b6d562e362 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -62,9 +62,9 @@ pub(crate) struct Cip36Info { /// Transaction Index. pub txn: i16, /// Voting Public Key - pub vote_key: Vec, + pub vote_key: String, /// Full Payment Address (not hashed, 32 byte ED25519 Public key). - pub payment_address: Vec, + pub payment_address: String, /// Is the stake address a script or not. pub is_payable: bool, /// Is the Registration CIP36 format, or CIP15 @@ -80,6 +80,12 @@ pub(crate) struct InvalidRegistrationsReport { pub stake_address: String, /// Voting Public Key pub vote_key: String, + /// Full Payment Address (not hashed, 32 byte ED25519 Public key). + pub payment_address: String, + /// Is the stake address a script or not. + pub is_payable: bool, + /// Is the Registration CIP36 format, or CIP15 + pub cip36: bool, } /// All responses @@ -177,8 +183,8 @@ async fn get_all_registrations_from_stake_addr( nonce, slot_no, txn: row.txn, - vote_key: row.vote_key, - payment_address: row.payment_address, + vote_key: hex::encode(row.vote_key), + payment_address: hex::encode(row.payment_address), is_payable: row.is_payable, cip36: row.cip36, }; @@ -215,6 +221,9 @@ async fn get_invalid_registrations( error_report: row.error_report, stake_address: hex::encode(row.stake_address), vote_key: hex::encode(row.vote_key), + payment_address: hex::encode(row.payment_address), + is_payable: row.is_payable, + cip36: row.cip36, }); } @@ -224,7 +233,7 @@ async fn get_invalid_registrations( /// Stake addresses need to be individually checked to make sure they are still actively /// associated with the voting key, and have not been registered to another voting key. fn check_stake_addr_voting_key_association( - registrations: Vec, associated_voting_key: &[u8], + registrations: Vec, associated_voting_key: &str, ) -> Vec { registrations .into_iter() @@ -367,8 +376,11 @@ pub(crate) async fn get_asscociated_vote_key_registrations( }, }; - let registrations = check_stake_addr_voting_key_association(registrations, &vote_key); - return ResponsesVotingKey::Ok(Json(registrations)).into(); + return ResponsesVotingKey::Ok(Json(check_stake_addr_voting_key_association( + registrations, + &hex::encode(vote_key), + ))) + .into(); } ResponsesVotingKey::NotFound.into() From c92ece2e622ec2ee9d3e84908b844bd6457228f9 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 3 Oct 2024 11:56:57 +0000 Subject: [PATCH 23/37] refactor(docs): clean --- .../src/db/index/queries/registrations/get_from_stake_addr.rs | 2 +- .../src/db/index/queries/registrations/get_from_stake_hash.rs | 2 +- .../src/db/index/queries/registrations/get_from_vote_key.rs | 3 +-- .../bin/src/db/index/queries/registrations/get_invalid.rs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs index 21a24896de..cb6d537748 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs @@ -1,4 +1,4 @@ -//! Get TXI by Transaction hash query +//! Get stake addr registrations use std::sync::Arc; diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs index ffa79e9d45..75cbe6dca1 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs @@ -1,4 +1,4 @@ -//! Get TXI by Transaction hash query +//! Get stake addr from stake hash use std::sync::Arc; diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs index 6d427d2061..9b352860ac 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs @@ -1,5 +1,4 @@ -//! Get TXI by Transaction hash query - +//! get stake addr from vote key use std::sync::Arc; use scylla::{ diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs index 0c3e971cd0..cb09152fbd 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_invalid.rs @@ -1,4 +1,4 @@ -//! Get TXI by Transaction hash query +//! Get invalid registrations for stake addr after given slot no. use std::sync::Arc; From d87066c3db2b04e5342e3e1f404608581f11a7e5 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 3 Oct 2024 13:12:25 +0000 Subject: [PATCH 24/37] refactor(housekeeping): clean --- .../bin/src/service/api/cardano/cip36.rs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index b6d562e362..77273430de 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -21,7 +21,7 @@ use crate::{ /// Endpoint responses #[derive(ApiResponse)] -pub(crate) enum Responses { +pub(crate) enum ResponseSingleRegistration { /// Cip36 registration #[oai(status = 200)] Ok(Json), @@ -32,7 +32,7 @@ pub(crate) enum Responses { /// Endpoint responses #[derive(ApiResponse)] -pub(crate) enum ResponsesVotingKey { +pub(crate) enum ResponseMultipleRegistrations { /// Cip36 registration #[oai(status = 200)] Ok(Json>), @@ -44,7 +44,7 @@ pub(crate) enum ResponsesVotingKey { /// Cip36 info + invalid reporting #[derive(Object, Default)] pub(crate) struct Cip36Reporting { - /// Cip36 info + /// List of registrations cip36: Vec, /// Invald registration reporting invalids: Vec, @@ -89,9 +89,9 @@ pub(crate) struct InvalidRegistrationsReport { } /// All responses -pub(crate) type AllResponses = WithErrorResponses; +pub(crate) type AllResponses = WithErrorResponses; /// All responses voting key -pub(crate) type AllResponsesVotingKey = WithErrorResponses; +pub(crate) type AllResponsesVotingKey = WithErrorResponses; /// Get latest registration given a stake address pub(crate) async fn get_latest_registration_from_stake_addr( @@ -101,13 +101,13 @@ pub(crate) async fn get_latest_registration_from_stake_addr( Ok(stake_addr) => stake_addr, Err(err) => { error!("Failed to decode stake addr {:?}", err); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }, }; let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }; let registration = @@ -119,7 +119,7 @@ pub(crate) async fn get_latest_registration_from_stake_addr( hex::encode(stake_addr), err ); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }, }; @@ -137,7 +137,7 @@ pub(crate) async fn get_latest_registration_from_stake_addr( hex::encode(stake_addr), err ); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }, }; @@ -146,7 +146,7 @@ pub(crate) async fn get_latest_registration_from_stake_addr( invalids: invalids_report, }; - Responses::Ok(Json(report)).into() + ResponseSingleRegistration::Ok(Json(report)).into() } /// Get latest registration given a stake addr @@ -249,13 +249,13 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( Ok(stake_hash) => stake_hash, Err(err) => { error!("Failed to decode stake addr {:?}", err); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }, }; let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }; // Get stake addr assosciated with give stake hash @@ -264,7 +264,7 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( Ok(latest) => latest, Err(err) => { error!("Failed to query stake addr from stake hash {:?}", err); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }, }; @@ -273,7 +273,7 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( Ok(r) => r, Err(err) => { error!("Failed to get latest registration {:?}", err); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }, }; @@ -288,7 +288,7 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( hex::encode(row.stake_address), err ); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }, }; @@ -308,7 +308,7 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( hex::encode(registration.stake_address.clone()), err ); - return Responses::NotFound.into(); + return ResponseSingleRegistration::NotFound.into(); }, }; @@ -317,10 +317,10 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( invalids: invalids_report, }; - return Responses::Ok(Json(report)).into(); + return ResponseSingleRegistration::Ok(Json(report)).into(); } - Responses::NotFound.into() + ResponseSingleRegistration::NotFound.into() } /// Returns the list of stake address registrations currently associated with a given @@ -332,13 +332,13 @@ pub(crate) async fn get_asscociated_vote_key_registrations( Ok(vote_key) => vote_key, Err(err) => { error!("Failed to decode vote key {:?}", err); - return ResponsesVotingKey::NotFound.into(); + return ResponseMultipleRegistrations::NotFound.into(); }, }; let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); - return ResponsesVotingKey::NotFound.into(); + return ResponseMultipleRegistrations::NotFound.into(); }; let mut stake_addr_iter = match GetStakeAddrFromVoteKeyQuery::execute( @@ -350,7 +350,7 @@ pub(crate) async fn get_asscociated_vote_key_registrations( Ok(latest) => latest, Err(err) => { error!("Failed to query stake addr from vote key {:?}", err); - return ResponsesVotingKey::NotFound.into(); + return ResponseMultipleRegistrations::NotFound.into(); }, }; @@ -359,7 +359,7 @@ pub(crate) async fn get_asscociated_vote_key_registrations( Ok(r) => r, Err(err) => { error!("Failed to get latest registration {:?}", err); - return ResponsesVotingKey::NotFound.into(); + return ResponseMultipleRegistrations::NotFound.into(); }, }; @@ -372,16 +372,16 @@ pub(crate) async fn get_asscociated_vote_key_registrations( hex::encode(row.stake_address), err ); - return ResponsesVotingKey::NotFound.into(); + return ResponseMultipleRegistrations::NotFound.into(); }, }; - return ResponsesVotingKey::Ok(Json(check_stake_addr_voting_key_association( + return ResponseMultipleRegistrations::Ok(Json(check_stake_addr_voting_key_association( registrations, &hex::encode(vote_key), ))) .into(); } - ResponsesVotingKey::NotFound.into() + ResponseMultipleRegistrations::NotFound.into() } From fd5b3cb895ba9f76edd604281cb4bcff1e9f836b Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 3 Oct 2024 20:44:11 +0000 Subject: [PATCH 25/37] feat(error reporting): vote key --- .../bin/src/service/api/cardano/cip36.rs | 64 +++++++++++++++---- .../bin/src/service/api/cardano/mod.rs | 6 +- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 77273430de..af8c89cd96 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -35,7 +35,7 @@ pub(crate) enum ResponseSingleRegistration { pub(crate) enum ResponseMultipleRegistrations { /// Cip36 registration #[oai(status = 200)] - Ok(Json>), + Ok(Json>), /// No valid registration #[oai(status = 404)] NotFound, @@ -88,15 +88,15 @@ pub(crate) struct InvalidRegistrationsReport { pub cip36: bool, } -/// All responses -pub(crate) type AllResponses = WithErrorResponses; +/// Single registration response +pub(crate) type SingleRegistrationResponse = WithErrorResponses; /// All responses voting key -pub(crate) type AllResponsesVotingKey = WithErrorResponses; +pub(crate) type MultipleRegistrationResponse = WithErrorResponses; /// Get latest registration given a stake address pub(crate) async fn get_latest_registration_from_stake_addr( stake_addr: String, persistent: bool, -) -> AllResponses { +) -> SingleRegistrationResponse { let stake_addr = match hex::decode(stake_addr) { Ok(stake_addr) => stake_addr, Err(err) => { @@ -244,7 +244,7 @@ fn check_stake_addr_voting_key_association( /// Get latest registration given a stake key hash. pub(crate) async fn get_latest_registration_from_stake_key_hash( stake_hash: String, persistent: bool, -) -> AllResponses { +) -> SingleRegistrationResponse { let stake_hash = match hex::decode(stake_hash) { Ok(stake_hash) => stake_hash, Err(err) => { @@ -324,10 +324,11 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( } /// Returns the list of stake address registrations currently associated with a given -/// voting key. +/// voting key and returns any erroneous registrations which occur AFTER the slot# of the +/// last valid registration. pub(crate) async fn get_asscociated_vote_key_registrations( vote_key: String, persistent: bool, -) -> AllResponsesVotingKey { +) -> MultipleRegistrationResponse { let vote_key = match hex::decode(vote_key) { Ok(vote_key) => vote_key, Err(err) => { @@ -363,8 +364,11 @@ pub(crate) async fn get_asscociated_vote_key_registrations( }, }; + // We have obtained all the stake addresses associated with the vote key. let registrations = - match get_all_registrations_from_stake_addr(session, row.stake_address.clone()).await { + match get_all_registrations_from_stake_addr(session.clone(), row.stake_address.clone()) + .await + { Ok(registration) => registration, Err(err) => { error!( @@ -376,11 +380,43 @@ pub(crate) async fn get_asscociated_vote_key_registrations( }, }; - return ResponseMultipleRegistrations::Ok(Json(check_stake_addr_voting_key_association( - registrations, - &hex::encode(vote_key), - ))) - .into(); + // check stake addrs are still actively associated with the voting key, and have not been + // registered to another voting key. + let redacted_registrations = + check_stake_addr_voting_key_association(registrations, &hex::encode(vote_key)); + + // Report includes registration info and any erroneous registrations which occur AFTER + // the slot# of the last valid registration + let mut reports = Vec::new(); + + for registration in redacted_registrations { + let invalids_report = match get_invalid_registrations( + registration.stake_address.clone(), + registration.slot_no.into(), + session.clone(), + ) + .await + { + Ok(invalids) => invalids, + Err(err) => { + error!( + "Failed to obtain invalid registrations for given stake addr:{:?} {:?}", + hex::encode(registration.stake_address.clone()), + err + ); + continue; + }, + }; + + let report = Cip36Reporting { + cip36: vec![registration], + invalids: invalids_report, + }; + + reports.push(report); + } + + return ResponseMultipleRegistrations::Ok(Json(reports)).into(); } ResponseMultipleRegistrations::NotFound.into() diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index 059b4b9945..e81ce35323 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -145,7 +145,7 @@ impl CardanoApi { /// This endpoint gets the latest registraton given a stake addr async fn latest_registration_cip36_given_stake_addr( &self, stake_addr: Query, - ) -> cip36::AllResponses { + ) -> cip36::SingleRegistrationResponse { cip36::get_latest_registration_from_stake_addr(stake_addr.0, true).await } @@ -159,7 +159,7 @@ impl CardanoApi { /// This endpoint gets the latest registraton given a stake key hash async fn latest_registration_cip36_given_stake_key_hash( &self, stake_key_hash: Query, - ) -> cip36::AllResponses { + ) -> cip36::SingleRegistrationResponse { cip36::get_latest_registration_from_stake_key_hash(stake_key_hash.0, true).await } @@ -174,7 +174,7 @@ impl CardanoApi { /// with a given voting key. async fn latest_registration_cip36_given_vote_key( &self, vote_key: Query, - ) -> cip36::AllResponsesVotingKey { + ) -> cip36::MultipleRegistrationResponse { cip36::get_asscociated_vote_key_registrations(vote_key.0, true).await } } From 91f76282dee3d4b66b2da13fe061bb715c691df1 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 3 Oct 2024 20:49:26 +0000 Subject: [PATCH 26/37] feat(error reporting): vote key --- catalyst-gateway/bin/src/service/api/cardano/cip36.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index af8c89cd96..e9ad130410 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -364,7 +364,7 @@ pub(crate) async fn get_asscociated_vote_key_registrations( }, }; - // We have obtained all the stake addresses associated with the vote key. + // We have the stake addr associated with vote key, now get all registrations with the stake addr. let registrations = match get_all_registrations_from_stake_addr(session.clone(), row.stake_address.clone()) .await @@ -380,7 +380,7 @@ pub(crate) async fn get_asscociated_vote_key_registrations( }, }; - // check stake addrs are still actively associated with the voting key, and have not been + // check registrations (stake addrs) are still actively associated with the voting key, and have not been // registered to another voting key. let redacted_registrations = check_stake_addr_voting_key_association(registrations, &hex::encode(vote_key)); From 7855f89dc5908c32cf0c8263c4f64e6dc22413fc Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 3 Oct 2024 20:50:51 +0000 Subject: [PATCH 27/37] feat(error reporting): vote key --- .../src/db/index/queries/registrations/get_from_stake_hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs index 75cbe6dca1..24722b54d7 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs @@ -47,7 +47,7 @@ mod result { pub(crate) struct GetStakeAddrQuery; impl GetStakeAddrQuery { - /// Prepares a get txi query. + /// Prepares a get get stake addr from stake hash query. pub(crate) async fn prepare(session: Arc) -> anyhow::Result { let get_stake_addr_query = PreparedQueries::prepare( session, From 33819009897c9ebe04fc0b98fa237e305d57de1e Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 3 Oct 2024 20:54:36 +0000 Subject: [PATCH 28/37] fix(code formatting): format --- catalyst-gateway/bin/src/service/api/cardano/cip36.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index e9ad130410..7d7e064fb9 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -364,7 +364,8 @@ pub(crate) async fn get_asscociated_vote_key_registrations( }, }; - // We have the stake addr associated with vote key, now get all registrations with the stake addr. + // We have the stake addr associated with vote key, now get all registrations with the + // stake addr. let registrations = match get_all_registrations_from_stake_addr(session.clone(), row.stake_address.clone()) .await @@ -380,8 +381,8 @@ pub(crate) async fn get_asscociated_vote_key_registrations( }, }; - // check registrations (stake addrs) are still actively associated with the voting key, and have not been - // registered to another voting key. + // check registrations (stake addrs) are still actively associated with the voting key, + // and have not been registered to another voting key. let redacted_registrations = check_stake_addr_voting_key_association(registrations, &hex::encode(vote_key)); From 125d2dc7da3d4857d8fab29bdb292dc868a620cb Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 3 Oct 2024 21:13:47 +0000 Subject: [PATCH 29/37] style(spelling): cspell --- catalyst-gateway/bin/src/service/api/cardano/cip36.rs | 6 +++--- catalyst-gateway/bin/src/service/api/cardano/mod.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index 7d7e064fb9..adf1324f92 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -46,7 +46,7 @@ pub(crate) enum ResponseMultipleRegistrations { pub(crate) struct Cip36Reporting { /// List of registrations cip36: Vec, - /// Invald registration reporting + /// Invalid registration reporting invalids: Vec, } @@ -258,7 +258,7 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( return ResponseSingleRegistration::NotFound.into(); }; - // Get stake addr assosciated with give stake hash + // Get stake addr associated with give stake hash let mut stake_addr_iter = match GetStakeAddrQuery::execute(&session, GetStakeAddrParams::new(stake_hash)).await { Ok(latest) => latest, @@ -326,7 +326,7 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( /// Returns the list of stake address registrations currently associated with a given /// voting key and returns any erroneous registrations which occur AFTER the slot# of the /// last valid registration. -pub(crate) async fn get_asscociated_vote_key_registrations( +pub(crate) async fn get_associated_vote_key_registrations( vote_key: String, persistent: bool, ) -> MultipleRegistrationResponse { let vote_key = match hex::decode(vote_key) { diff --git a/catalyst-gateway/bin/src/service/api/cardano/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/mod.rs index e81ce35323..5df55a7743 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/mod.rs @@ -142,7 +142,7 @@ impl CardanoApi { )] /// Cip36 registrations /// - /// This endpoint gets the latest registraton given a stake addr + /// This endpoint gets the latest registration given a stake addr async fn latest_registration_cip36_given_stake_addr( &self, stake_addr: Query, ) -> cip36::SingleRegistrationResponse { @@ -156,7 +156,7 @@ impl CardanoApi { )] /// Cip36 registrations /// - /// This endpoint gets the latest registraton given a stake key hash + /// This endpoint gets the latest registration given a stake key hash async fn latest_registration_cip36_given_stake_key_hash( &self, stake_key_hash: Query, ) -> cip36::SingleRegistrationResponse { @@ -175,6 +175,6 @@ impl CardanoApi { async fn latest_registration_cip36_given_vote_key( &self, vote_key: Query, ) -> cip36::MultipleRegistrationResponse { - cip36::get_asscociated_vote_key_registrations(vote_key.0, true).await + cip36::get_associated_vote_key_registrations(vote_key.0, true).await } } From 68101089bfb6ca573d07397a1230014465a39be0 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Fri, 4 Oct 2024 15:19:44 +0700 Subject: [PATCH 30/37] fix(cat-gateway): spelling --- .config/dictionaries/project.dic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic index bfba1b49d9..63bc362ca0 100644 --- a/.config/dictionaries/project.dic +++ b/.config/dictionaries/project.dic @@ -7,6 +7,7 @@ adminer afinet androidx anypolicy +appbar appspot Arbritrary ARGB @@ -294,4 +295,3 @@ xctestrun xcworkspace xvfb yoroi -appbar \ No newline at end of file From e872455f81d201173324a1a198d4ae737cea815e Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Fri, 4 Oct 2024 15:19:56 +0700 Subject: [PATCH 31/37] fix(cat-gateway): docs --- .../src/db/index/queries/registrations/get_from_stake_addr.rs | 2 +- .../bin/src/db/index/queries/registrations/get_from_vote_key.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs index cb6d537748..f6ccb04008 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs @@ -25,7 +25,7 @@ pub(crate) struct GetRegistrationParams { } impl GetRegistrationParams { - /// Create a new instance of [`GetLatestRegistrationParams`] + /// Create a new instance of [`GetRegistrationParams`] pub(crate) fn new(stake_addr: Vec) -> GetRegistrationParams { Self { stake_address: stake_addr, diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs index 9b352860ac..1d9d591a73 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs @@ -23,7 +23,7 @@ pub(crate) struct GetStakeAddrFromVoteKeyParams { } impl GetStakeAddrFromVoteKeyParams { - /// Create a new instance of [`GetStakeAddrParams`] + /// Create a new instance of [`GetStakeAddrFromVoteKeyParams`] pub(crate) fn new(vote_key: Vec) -> GetStakeAddrFromVoteKeyParams { Self { vote_key } } From 70dcb28be6f20f662a1ae9ea9580129f45e6b956 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Fri, 4 Oct 2024 15:20:27 +0700 Subject: [PATCH 32/37] fix(cat-gateway): generated dart openapi files --- .../cat_gateway_api.models.swagger.dart | 286 ++++++++++++++++++ .../cat_gateway_api.models.swagger.g.dart | 66 ++++ .../cat_gateway_api.swagger.chopper.dart | 51 ++++ .../cat_gateway_api.swagger.dart | 54 ++++ 4 files changed, 457 insertions(+) diff --git a/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.models.swagger.dart b/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.models.swagger.dart index 3385365eaf..0fd16fcdf9 100644 --- a/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.models.swagger.dart +++ b/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.models.swagger.dart @@ -113,6 +113,186 @@ extension $BlockDateExtension on BlockDate { } } +@JsonSerializable(explicitToJson: true) +class Cip36Info { + const Cip36Info({ + required this.stakeAddress, + required this.nonce, + required this.slotNo, + required this.txn, + required this.voteKey, + required this.paymentAddress, + required this.isPayable, + required this.cip36, + }); + + factory Cip36Info.fromJson(Map json) => + _$Cip36InfoFromJson(json); + + static const toJsonFactory = _$Cip36InfoToJson; + Map toJson() => _$Cip36InfoToJson(this); + + @JsonKey(name: 'stake_address') + final String stakeAddress; + @JsonKey(name: 'nonce') + final int nonce; + @JsonKey(name: 'slot_no') + final int slotNo; + @JsonKey(name: 'txn') + final int txn; + @JsonKey(name: 'vote_key') + final String voteKey; + @JsonKey(name: 'payment_address') + final String paymentAddress; + @JsonKey(name: 'is_payable') + final bool isPayable; + @JsonKey(name: 'cip36') + final bool cip36; + static const fromJsonFactory = _$Cip36InfoFromJson; + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other is Cip36Info && + (identical(other.stakeAddress, stakeAddress) || + const DeepCollectionEquality() + .equals(other.stakeAddress, stakeAddress)) && + (identical(other.nonce, nonce) || + const DeepCollectionEquality().equals(other.nonce, nonce)) && + (identical(other.slotNo, slotNo) || + const DeepCollectionEquality().equals(other.slotNo, slotNo)) && + (identical(other.txn, txn) || + const DeepCollectionEquality().equals(other.txn, txn)) && + (identical(other.voteKey, voteKey) || + const DeepCollectionEquality() + .equals(other.voteKey, voteKey)) && + (identical(other.paymentAddress, paymentAddress) || + const DeepCollectionEquality() + .equals(other.paymentAddress, paymentAddress)) && + (identical(other.isPayable, isPayable) || + const DeepCollectionEquality() + .equals(other.isPayable, isPayable)) && + (identical(other.cip36, cip36) || + const DeepCollectionEquality().equals(other.cip36, cip36))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(stakeAddress) ^ + const DeepCollectionEquality().hash(nonce) ^ + const DeepCollectionEquality().hash(slotNo) ^ + const DeepCollectionEquality().hash(txn) ^ + const DeepCollectionEquality().hash(voteKey) ^ + const DeepCollectionEquality().hash(paymentAddress) ^ + const DeepCollectionEquality().hash(isPayable) ^ + const DeepCollectionEquality().hash(cip36) ^ + runtimeType.hashCode; +} + +extension $Cip36InfoExtension on Cip36Info { + Cip36Info copyWith( + {String? stakeAddress, + int? nonce, + int? slotNo, + int? txn, + String? voteKey, + String? paymentAddress, + bool? isPayable, + bool? cip36}) { + return Cip36Info( + stakeAddress: stakeAddress ?? this.stakeAddress, + nonce: nonce ?? this.nonce, + slotNo: slotNo ?? this.slotNo, + txn: txn ?? this.txn, + voteKey: voteKey ?? this.voteKey, + paymentAddress: paymentAddress ?? this.paymentAddress, + isPayable: isPayable ?? this.isPayable, + cip36: cip36 ?? this.cip36); + } + + Cip36Info copyWithWrapped( + {Wrapped? stakeAddress, + Wrapped? nonce, + Wrapped? slotNo, + Wrapped? txn, + Wrapped? voteKey, + Wrapped? paymentAddress, + Wrapped? isPayable, + Wrapped? cip36}) { + return Cip36Info( + stakeAddress: + (stakeAddress != null ? stakeAddress.value : this.stakeAddress), + nonce: (nonce != null ? nonce.value : this.nonce), + slotNo: (slotNo != null ? slotNo.value : this.slotNo), + txn: (txn != null ? txn.value : this.txn), + voteKey: (voteKey != null ? voteKey.value : this.voteKey), + paymentAddress: (paymentAddress != null + ? paymentAddress.value + : this.paymentAddress), + isPayable: (isPayable != null ? isPayable.value : this.isPayable), + cip36: (cip36 != null ? cip36.value : this.cip36)); + } +} + +@JsonSerializable(explicitToJson: true) +class Cip36Reporting { + const Cip36Reporting({ + required this.cip36, + required this.invalids, + }); + + factory Cip36Reporting.fromJson(Map json) => + _$Cip36ReportingFromJson(json); + + static const toJsonFactory = _$Cip36ReportingToJson; + Map toJson() => _$Cip36ReportingToJson(this); + + @JsonKey(name: 'cip36', defaultValue: []) + final List cip36; + @JsonKey(name: 'invalids', defaultValue: []) + final List invalids; + static const fromJsonFactory = _$Cip36ReportingFromJson; + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other is Cip36Reporting && + (identical(other.cip36, cip36) || + const DeepCollectionEquality().equals(other.cip36, cip36)) && + (identical(other.invalids, invalids) || + const DeepCollectionEquality() + .equals(other.invalids, invalids))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(cip36) ^ + const DeepCollectionEquality().hash(invalids) ^ + runtimeType.hashCode; +} + +extension $Cip36ReportingExtension on Cip36Reporting { + Cip36Reporting copyWith( + {List? cip36, List? invalids}) { + return Cip36Reporting( + cip36: cip36 ?? this.cip36, invalids: invalids ?? this.invalids); + } + + Cip36Reporting copyWithWrapped( + {Wrapped>? cip36, + Wrapped>? invalids}) { + return Cip36Reporting( + cip36: (cip36 != null ? cip36.value : this.cip36), + invalids: (invalids != null ? invalids.value : this.invalids)); + } +} + @JsonSerializable(explicitToJson: true) class DelegatePublicKey { const DelegatePublicKey({ @@ -529,6 +709,112 @@ extension $HashExtension on Hash { } } +@JsonSerializable(explicitToJson: true) +class InvalidRegistrationsReport { + const InvalidRegistrationsReport({ + required this.errorReport, + required this.stakeAddress, + required this.voteKey, + required this.paymentAddress, + required this.isPayable, + required this.cip36, + }); + + factory InvalidRegistrationsReport.fromJson(Map json) => + _$InvalidRegistrationsReportFromJson(json); + + static const toJsonFactory = _$InvalidRegistrationsReportToJson; + Map toJson() => _$InvalidRegistrationsReportToJson(this); + + @JsonKey(name: 'error_report', defaultValue: []) + final List errorReport; + @JsonKey(name: 'stake_address') + final String stakeAddress; + @JsonKey(name: 'vote_key') + final String voteKey; + @JsonKey(name: 'payment_address') + final String paymentAddress; + @JsonKey(name: 'is_payable') + final bool isPayable; + @JsonKey(name: 'cip36') + final bool cip36; + static const fromJsonFactory = _$InvalidRegistrationsReportFromJson; + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other is InvalidRegistrationsReport && + (identical(other.errorReport, errorReport) || + const DeepCollectionEquality() + .equals(other.errorReport, errorReport)) && + (identical(other.stakeAddress, stakeAddress) || + const DeepCollectionEquality() + .equals(other.stakeAddress, stakeAddress)) && + (identical(other.voteKey, voteKey) || + const DeepCollectionEquality() + .equals(other.voteKey, voteKey)) && + (identical(other.paymentAddress, paymentAddress) || + const DeepCollectionEquality() + .equals(other.paymentAddress, paymentAddress)) && + (identical(other.isPayable, isPayable) || + const DeepCollectionEquality() + .equals(other.isPayable, isPayable)) && + (identical(other.cip36, cip36) || + const DeepCollectionEquality().equals(other.cip36, cip36))); + } + + @override + String toString() => jsonEncode(this); + + @override + int get hashCode => + const DeepCollectionEquality().hash(errorReport) ^ + const DeepCollectionEquality().hash(stakeAddress) ^ + const DeepCollectionEquality().hash(voteKey) ^ + const DeepCollectionEquality().hash(paymentAddress) ^ + const DeepCollectionEquality().hash(isPayable) ^ + const DeepCollectionEquality().hash(cip36) ^ + runtimeType.hashCode; +} + +extension $InvalidRegistrationsReportExtension on InvalidRegistrationsReport { + InvalidRegistrationsReport copyWith( + {List? errorReport, + String? stakeAddress, + String? voteKey, + String? paymentAddress, + bool? isPayable, + bool? cip36}) { + return InvalidRegistrationsReport( + errorReport: errorReport ?? this.errorReport, + stakeAddress: stakeAddress ?? this.stakeAddress, + voteKey: voteKey ?? this.voteKey, + paymentAddress: paymentAddress ?? this.paymentAddress, + isPayable: isPayable ?? this.isPayable, + cip36: cip36 ?? this.cip36); + } + + InvalidRegistrationsReport copyWithWrapped( + {Wrapped>? errorReport, + Wrapped? stakeAddress, + Wrapped? voteKey, + Wrapped? paymentAddress, + Wrapped? isPayable, + Wrapped? cip36}) { + return InvalidRegistrationsReport( + errorReport: + (errorReport != null ? errorReport.value : this.errorReport), + stakeAddress: + (stakeAddress != null ? stakeAddress.value : this.stakeAddress), + voteKey: (voteKey != null ? voteKey.value : this.voteKey), + paymentAddress: (paymentAddress != null + ? paymentAddress.value + : this.paymentAddress), + isPayable: (isPayable != null ? isPayable.value : this.isPayable), + cip36: (cip36 != null ? cip36.value : this.cip36)); + } +} + @JsonSerializable(explicitToJson: true) class RegistrationInfo { const RegistrationInfo({ diff --git a/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.models.swagger.g.dart b/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.models.swagger.g.dart index 4dba49dd5a..91789e3052 100644 --- a/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.models.swagger.g.dart +++ b/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.models.swagger.g.dart @@ -30,6 +30,47 @@ Map _$BlockDateToJson(BlockDate instance) => { 'slot_id': instance.slotId, }; +Cip36Info _$Cip36InfoFromJson(Map json) => Cip36Info( + stakeAddress: json['stake_address'] as String, + nonce: (json['nonce'] as num).toInt(), + slotNo: (json['slot_no'] as num).toInt(), + txn: (json['txn'] as num).toInt(), + voteKey: json['vote_key'] as String, + paymentAddress: json['payment_address'] as String, + isPayable: json['is_payable'] as bool, + cip36: json['cip36'] as bool, + ); + +Map _$Cip36InfoToJson(Cip36Info instance) => { + 'stake_address': instance.stakeAddress, + 'nonce': instance.nonce, + 'slot_no': instance.slotNo, + 'txn': instance.txn, + 'vote_key': instance.voteKey, + 'payment_address': instance.paymentAddress, + 'is_payable': instance.isPayable, + 'cip36': instance.cip36, + }; + +Cip36Reporting _$Cip36ReportingFromJson(Map json) => + Cip36Reporting( + cip36: (json['cip36'] as List?) + ?.map((e) => Cip36Info.fromJson(e as Map)) + .toList() ?? + [], + invalids: (json['invalids'] as List?) + ?.map((e) => InvalidRegistrationsReport.fromJson( + e as Map)) + .toList() ?? + [], + ); + +Map _$Cip36ReportingToJson(Cip36Reporting instance) => + { + 'cip36': instance.cip36.map((e) => e.toJson()).toList(), + 'invalids': instance.invalids.map((e) => e.toJson()).toList(), + }; + DelegatePublicKey _$DelegatePublicKeyFromJson(Map json) => DelegatePublicKey( address: json['address'] as String, @@ -134,6 +175,31 @@ Map _$HashToJson(Hash instance) => { 'hash': instance.hash, }; +InvalidRegistrationsReport _$InvalidRegistrationsReportFromJson( + Map json) => + InvalidRegistrationsReport( + errorReport: (json['error_report'] as List?) + ?.map((e) => e as String) + .toList() ?? + [], + stakeAddress: json['stake_address'] as String, + voteKey: json['vote_key'] as String, + paymentAddress: json['payment_address'] as String, + isPayable: json['is_payable'] as bool, + cip36: json['cip36'] as bool, + ); + +Map _$InvalidRegistrationsReportToJson( + InvalidRegistrationsReport instance) => + { + 'error_report': instance.errorReport, + 'stake_address': instance.stakeAddress, + 'vote_key': instance.voteKey, + 'payment_address': instance.paymentAddress, + 'is_payable': instance.isPayable, + 'cip36': instance.cip36, + }; + RegistrationInfo _$RegistrationInfoFromJson(Map json) => RegistrationInfo( rewardsAddress: json['rewards_address'] as String, diff --git a/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.swagger.chopper.dart b/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.swagger.chopper.dart index 88a535d087..b53b42fe7b 100644 --- a/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.swagger.chopper.dart +++ b/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.swagger.chopper.dart @@ -141,6 +141,57 @@ final class _$CatGatewayApi extends CatGatewayApi { return client.send($request); } + @override + Future> + _apiCardanoCip36LatestRegistrationStakeAddrGet( + {required String? stakeAddr}) { + final Uri $url = + Uri.parse('/api/cardano/cip36/latest_registration/stake_addr'); + final Map $params = { + 'stake_addr': stakeAddr + }; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + ); + return client.send($request); + } + + @override + Future> + _apiCardanoCip36LatestRegistrationStakeKeyHashGet( + {required String? stakeKeyHash}) { + final Uri $url = + Uri.parse('/api/cardano/cip36/latest_registration/stake_key_hash'); + final Map $params = { + 'stake_key_hash': stakeKeyHash + }; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + ); + return client.send($request); + } + + @override + Future>> + _apiCardanoCip36LatestRegistrationVoteKeyGet({required String? voteKey}) { + final Uri $url = + Uri.parse('/api/cardano/cip36/latest_registration/vote_key'); + final Map $params = {'vote_key': voteKey}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + ); + return client.send, Cip36Reporting>($request); + } + @override Future> _apiRegistrationVoterVotingKeyGet({ required String? votingKey, diff --git a/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.swagger.dart b/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.swagger.dart index 121209f0e7..8846b22230 100644 --- a/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.swagger.dart +++ b/catalyst_voices/packages/catalyst_voices_services/lib/generated/catalyst_gateway/cat_gateway_api.swagger.dart @@ -193,6 +193,60 @@ abstract class CatGatewayApi extends ChopperService { @Query('network') String? network, }); + ///Cip36 registrations + ///@param stake_addr + Future> + apiCardanoCip36LatestRegistrationStakeAddrGet( + {required String? stakeAddr}) { + generatedMapping.putIfAbsent( + Cip36Reporting, () => Cip36Reporting.fromJsonFactory); + + return _apiCardanoCip36LatestRegistrationStakeAddrGet(stakeAddr: stakeAddr); + } + + ///Cip36 registrations + ///@param stake_addr + @Get(path: '/api/cardano/cip36/latest_registration/stake_addr') + Future> + _apiCardanoCip36LatestRegistrationStakeAddrGet( + {@Query('stake_addr') required String? stakeAddr}); + + ///Cip36 registrations + ///@param stake_key_hash + Future> + apiCardanoCip36LatestRegistrationStakeKeyHashGet( + {required String? stakeKeyHash}) { + generatedMapping.putIfAbsent( + Cip36Reporting, () => Cip36Reporting.fromJsonFactory); + + return _apiCardanoCip36LatestRegistrationStakeKeyHashGet( + stakeKeyHash: stakeKeyHash); + } + + ///Cip36 registrations + ///@param stake_key_hash + @Get(path: '/api/cardano/cip36/latest_registration/stake_key_hash') + Future> + _apiCardanoCip36LatestRegistrationStakeKeyHashGet( + {@Query('stake_key_hash') required String? stakeKeyHash}); + + ///Cip36 registrations + ///@param vote_key + Future>> + apiCardanoCip36LatestRegistrationVoteKeyGet({required String? voteKey}) { + generatedMapping.putIfAbsent( + Cip36Reporting, () => Cip36Reporting.fromJsonFactory); + + return _apiCardanoCip36LatestRegistrationVoteKeyGet(voteKey: voteKey); + } + + ///Cip36 registrations + ///@param vote_key + @Get(path: '/api/cardano/cip36/latest_registration/vote_key') + Future>> + _apiCardanoCip36LatestRegistrationVoteKeyGet( + {@Query('vote_key') required String? voteKey}); + ///Voter's info ///@param voting_key A Voters Public ED25519 Key (as registered in their most recent valid [CIP-15](https://cips.cardano.org/cips/cip15) or [CIP-36](https://cips.cardano.org/cips/cip36) registration). ///@param event_id The Event ID to return results for. See [GET Events](Link to events endpoint) for details on retrieving all valid event IDs. From 9b4c00be609be06fe2c7f8a339599ffb281fc1b7 Mon Sep 17 00:00:00 2001 From: cong-or Date: Fri, 4 Oct 2024 09:41:06 +0000 Subject: [PATCH 33/37] ci(docs): lints --- .../src/db/index/queries/registrations/get_from_stake_addr.rs | 2 +- .../src/db/index/queries/registrations/get_from_stake_hash.rs | 2 +- .../bin/src/db/index/queries/registrations/get_from_vote_key.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs index cb6d537748..f6ccb04008 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_addr.rs @@ -25,7 +25,7 @@ pub(crate) struct GetRegistrationParams { } impl GetRegistrationParams { - /// Create a new instance of [`GetLatestRegistrationParams`] + /// Create a new instance of [`GetRegistrationParams`] pub(crate) fn new(stake_addr: Vec) -> GetRegistrationParams { Self { stake_address: stake_addr, diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs index 24722b54d7..89804f5d7f 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs @@ -25,7 +25,7 @@ pub(crate) struct GetStakeAddrParams { } impl GetStakeAddrParams { - /// Create a new instance of [`GetStakeAddrParams`] + /// Create a new instance of [`GetStakeAddrFromStakeHashParams`] pub(crate) fn new(stake_hash: Vec) -> GetStakeAddrParams { Self { stake_hash } } diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs index 9b352860ac..1d9d591a73 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_vote_key.rs @@ -23,7 +23,7 @@ pub(crate) struct GetStakeAddrFromVoteKeyParams { } impl GetStakeAddrFromVoteKeyParams { - /// Create a new instance of [`GetStakeAddrParams`] + /// Create a new instance of [`GetStakeAddrFromVoteKeyParams`] pub(crate) fn new(vote_key: Vec) -> GetStakeAddrFromVoteKeyParams { Self { vote_key } } From 600c5fefd371d4a4e0d1498ca5dce729e3999520 Mon Sep 17 00:00:00 2001 From: cong-or Date: Fri, 4 Oct 2024 10:52:16 +0000 Subject: [PATCH 34/37] ci(docs): lints --- catalyst-gateway/bin/src/service/api/cardano/cip36.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index adf1324f92..cf871e87a1 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -1,6 +1,6 @@ //! Implementation of the GET `/registration/cip36` endpoint -use std::sync::Arc; +use std::{cmp::Reverse, sync::Arc}; use futures::StreamExt; use poem_openapi::{payload::Json, ApiResponse, Object}; @@ -196,7 +196,7 @@ async fn get_all_registrations_from_stake_addr( /// Sort latest registrations for a given stake address sorting by slot no fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { - registrations.sort_by_key(|k| k.slot_no); + registrations.sort_by_key(|k| Reverse(k.slot_no)); registrations.into_iter().next().ok_or(anyhow::anyhow!( "Can't sort latest registrations by slot no" )) From 9d0ce3e7e07df338af4d0328927dfdb571d976c6 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Fri, 4 Oct 2024 20:10:05 +0700 Subject: [PATCH 35/37] fix(cat-gateway): bump cache version --- catalyst-gateway/Earthfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalyst-gateway/Earthfile b/catalyst-gateway/Earthfile index ec3861ec4d..255638b83e 100644 --- a/catalyst-gateway/Earthfile +++ b/catalyst-gateway/Earthfile @@ -16,7 +16,7 @@ COPY_SRC: # builder : Set up our target toolchains, and copy our files. builder: - DO rust-ci+SETUP + DO rust-ci+SETUP --CACHE_VERSION="00002" # sync-cfg: Synchronize local config with CI version. # Must be run by the developer manually. From 15196cfa125a4476a000e459a5293c08574c3e0e Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Fri, 4 Oct 2024 20:16:11 +0700 Subject: [PATCH 36/37] docs(cat-gateway): fix docs --- .../src/db/index/queries/registrations/get_from_stake_hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs index 89804f5d7f..24722b54d7 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_from_stake_hash.rs @@ -25,7 +25,7 @@ pub(crate) struct GetStakeAddrParams { } impl GetStakeAddrParams { - /// Create a new instance of [`GetStakeAddrFromStakeHashParams`] + /// Create a new instance of [`GetStakeAddrParams`] pub(crate) fn new(stake_hash: Vec) -> GetStakeAddrParams { Self { stake_hash } } From 2be4089f5459a317b2e2a71d472ba50ac5c5d97a Mon Sep 17 00:00:00 2001 From: cong-or Date: Fri, 4 Oct 2024 13:54:34 +0000 Subject: [PATCH 37/37] ci(oai lints): add missing endpoint annotations --- catalyst-gateway/bin/src/service/api/cardano/cip36.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs index cf871e87a1..810fbc431f 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36.rs @@ -45,8 +45,10 @@ pub(crate) enum ResponseMultipleRegistrations { #[derive(Object, Default)] pub(crate) struct Cip36Reporting { /// List of registrations + #[oai(validator(max_items = "100000"))] cip36: Vec, /// Invalid registration reporting + #[oai(validator(max_items = "100000"))] invalids: Vec, } @@ -54,16 +56,22 @@ pub(crate) struct Cip36Reporting { #[derive(Object, Default)] pub(crate) struct Cip36Info { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). + #[oai(validator(max_length = 66, min_length = 66, pattern = "0x[0-9a-f]{64}"))] pub stake_address: String, /// Nonce value after normalization. + #[oai(validator(minimum(value = "0"), maximum(value = "9223372036854775807")))] pub nonce: u64, /// Slot Number the cert is in. + #[oai(validator(minimum(value = "0"), maximum(value = "9223372036854775807")))] pub slot_no: u64, /// Transaction Index. + #[oai(validator(minimum(value = "0"), maximum(value = "9223372036854775807")))] pub txn: i16, /// Voting Public Key + #[oai(validator(max_length = 66, min_length = 66, pattern = "0x[0-9a-f]{64}"))] pub vote_key: String, /// Full Payment Address (not hashed, 32 byte ED25519 Public key). + #[oai(validator(max_length = 66, min_length = 66, pattern = "0x[0-9a-f]{64}"))] pub payment_address: String, /// Is the stake address a script or not. pub is_payable: bool,