From 7c08bf0f9a86a828791593438d734813f0258ac6 Mon Sep 17 00:00:00 2001 From: cong-or Date: Sat, 4 Jan 2025 21:05:45 +0000 Subject: [PATCH 01/46] refactor(ignore): wip --- .../registrations/get_from_stake_addr.rs | 6 +- .../queries/registrations/get_invalid.rs | 15 +- .../cql/cip36_registration_for_vote_key.cql | 2 +- .../src/service/api/cardano/cip36/endpoint.rs | 76 ++++++- .../bin/src/service/api/cardano/cip36/mod.rs | 8 +- .../service/api/cardano/cip36/old_endpoint.rs | 209 ++++++++++-------- .../src/service/api/cardano/cip36/response.rs | 10 +- .../service/common/objects/cardano/cip36.rs | 2 + .../common/objects/generic/pagination.rs | 2 +- .../types/cardano/cip19_stake_address.rs | 20 ++ .../src/service/common/types/cardano/nonce.rs | 2 +- .../service/common/types/cardano/slot_no.rs | 9 +- .../service/common/types/cardano/txn_index.rs | 2 +- .../bin/src/settings/cassandra_db.rs | 4 +- 14 files changed, 244 insertions(+), 123 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 c0ff611937a..5c6e0d7362f 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 @@ -24,10 +24,10 @@ pub(crate) struct GetRegistrationParams { pub stake_address: Vec, } -impl From<&ed25519_dalek::VerifyingKey> for GetRegistrationParams { - fn from(value: &ed25519_dalek::VerifyingKey) -> Self { +impl From> for GetRegistrationParams { + fn from(value: Vec) -> Self { GetRegistrationParams { - stake_address: value.as_bytes().to_vec(), + stake_address: value, } } } 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 ada25c54937..b3ab545446b 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 @@ -8,9 +8,12 @@ use scylla::{ }; use tracing::error; -use crate::db::index::{ - queries::{PreparedQueries, PreparedSelectQuery}, - session::CassandraSession, +use crate::{ + db::index::{ + queries::{PreparedQueries, PreparedSelectQuery}, + session::CassandraSession, + }, + service::common::types::cardano::slot_no::SlotNo, }; /// Get invalid registrations from stake addr query. @@ -28,12 +31,10 @@ pub(crate) struct GetInvalidRegistrationParams { impl GetInvalidRegistrationParams { /// Create a new instance of [`GetInvalidRegistrationParams`] - pub(crate) fn new( - stake_address: Vec, slot_no: num_bigint::BigInt, - ) -> GetInvalidRegistrationParams { + pub(crate) fn new(stake_address: Vec, slot_no: SlotNo) -> GetInvalidRegistrationParams { Self { stake_address, - slot_no, + slot_no: slot_no.into_big_int(), } } } diff --git a/catalyst-gateway/bin/src/db/index/schema/cql/cip36_registration_for_vote_key.cql b/catalyst-gateway/bin/src/db/index/schema/cql/cip36_registration_for_vote_key.cql index c3ba5f6dfce..7ec1aee35c2 100644 --- a/catalyst-gateway/bin/src/db/index/schema/cql/cip36_registration_for_vote_key.cql +++ b/catalyst-gateway/bin/src/db/index/schema/cql/cip36_registration_for_vote_key.cql @@ -1,5 +1,5 @@ -- Index of CIP-36 registrations searchable by Stake Address. --- Full registration data needs to be queried from the man cip36 registration tables. +-- Full registration data needs to be queried from the main cip36 registration tables. -- Includes both Valid and Invalid registrations. CREATE TABLE IF NOT EXISTS cip36_registration_for_vote_key ( -- Primary Key Data diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index cfd5d5f2a51..f46e13e9c2c 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -1,32 +1,90 @@ //! Implementation of the GET `/cardano/cip36` endpoint -use std::time::Duration; - -use poem::http::HeaderMap; -use tokio::time::sleep; +use poem::http::{HeaderMap, StatusCode}; +use tracing::{error, info}; +use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddressOrPublicKey}; use super::{ cardano::{self}, + old_endpoint::get_latest_registration_from_stake_key_hash, response, NoneOrRBAC, SlotNo, }; use crate::service::common::{self}; /// Process the endpoint operation +#[allow(clippy::unused_async)] pub(crate) async fn cip36_registrations( - _lookup: Option, _asat: Option, + lookup: Option, asat: Option, _page: common::types::generic::query::pagination::Page, _limit: common::types::generic::query::pagination::Limit, _auth: NoneOrRBAC, _headers: &HeaderMap, ) -> response::AllRegistration { - // Dummy sleep, remove it - sleep(Duration::from_millis(1)).await; + if let Some(_slot) = asat { + } else { + // If _asat is None, then get the latest slot number from the chain follower and + // use that. + } + + if let Some(stake_or_voter) = lookup { + match StakeAddressOrPublicKey::from(stake_or_voter) { + StakeAddressOrPublicKey::Address(cip19_stake_address) => { + // Convert stake address into stake key hash + let stake_hash: HexEncodedHash28 = match cip19_stake_address.try_into() { + Ok(stake_hash) => stake_hash, + Err(err) => { + error!("stake addr to stake hash {:?}", err); + return response::AllRegistration::unprocessable_content(vec![ + poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + ), + ]); + }, + }; - // Todo: refactor the below into a single operation here. + // Get stake public key from stake hash + match get_latest_registration_from_stake_key_hash(stake_hash.to_string(), true) + .await + { + common::responses::WithErrorResponses::With(resp) => resp, + common::responses::WithErrorResponses::Error(_error_responses) => { + return response::AllRegistration::unprocessable_content(vec![ + poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + ), + ]); + }, + } + }, + StakeAddressOrPublicKey::PublicKey(_ed25519_hex_encoded_public_key) => { + info!("stake public key conor"); + return response::AllRegistration::unprocessable_content(vec![ + poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + ), + ]); + }, + StakeAddressOrPublicKey::All => { + info!("stake address conor all"); + return response::AllRegistration::unprocessable_content(vec![ + poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + ), + ]); + }, + }; + }; - // If _asat is None, then get the latest slot number from the chain follower and use that. // If _for is not defined, use the stake addresses defined for Role0 in the _auth // parameter. _auth not yet implemented, so put placeholder for that, and return not // found until _auth is implemented. + // return 404 for auth + // distill down to stake addr or list of stake addr + // stake addr hash for cip36 registratioin + response::Cip36Registration::NotFound.into() } diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs index 549dc776443..2a2ea11e3c1 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs @@ -1,6 +1,5 @@ //! CIP36 Registration Endpoints -use ed25519_dalek::VerifyingKey; use poem::http::{HeaderMap, StatusCode}; use poem_openapi::{param::Query, OpenApi}; @@ -15,6 +14,7 @@ use crate::service::common::{ pub(crate) mod endpoint; pub(crate) mod old_endpoint; + pub(crate) mod response; /// Cardano Staking API Endpoints @@ -94,9 +94,9 @@ impl Api { _auth: NoneOrRBAC, ) -> old_endpoint::SingleRegistrationResponse { let hex_key = stake_pub_key.0; - let pub_key: VerifyingKey = hex_key.into(); - old_endpoint::get_latest_registration_from_stake_addr(&pub_key, true).await + old_endpoint::get_latest_registration_from_stake_addr(hex_key.as_bytes().to_vec(), true) + .await } /// Get latest CIP36 registrations from a stake key hash. @@ -114,7 +114,7 @@ impl Api { stake_key_hash: Query, /// No Authorization required, but Token permitted. _auth: NoneOrRBAC, - ) -> old_endpoint::SingleRegistrationResponse { + ) -> response::AllRegistration { old_endpoint::get_latest_registration_from_stake_key_hash(stake_key_hash.0, true).await } diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/old_endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/old_endpoint.rs index d004ddf70c1..e97aec39274 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/old_endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/old_endpoint.rs @@ -2,11 +2,20 @@ use std::{cmp::Reverse, sync::Arc}; -use anyhow::anyhow; use futures::StreamExt; +use poem::http::StatusCode; use poem_openapi::{payload::Json, ApiResponse}; use tracing::error; +use super::{ + cardano::{cip19_shelley_address::Cip19ShelleyAddress, nonce::Nonce, txn_index::TxnIndex}, + common::{objects::generic::pagination::CurrentPage, types::generic::error_msg::ErrorMessage}, + response::{ + AllRegistration, Cip36Details, Cip36Registration, Cip36RegistrationList, + Cip36RegistrationsForVotingPublicKey, + }, + Ed25519HexEncodedPublicKey, SlotNo, +}; use crate::{ db::index::{ queries::registrations::{ @@ -18,17 +27,14 @@ use crate::{ session::CassandraSession, }, service::common::{ - objects::cardano::cip36::{ - Cip36Info, Cip36Reporting, Cip36ReportingList, InvalidRegistrationsReport, - }, + objects::cardano::cip36::{Cip36Reporting, Cip36ReportingList}, responses::WithErrorResponses, - types::headers::retry_after::RetryAfterOption, }, - utils::ed25519, }; /// Endpoint responses. #[derive(ApiResponse)] +#[allow(dead_code)] pub(crate) enum ResponseSingleRegistration { /// A CIP36 registration report. #[oai(status = 200)] @@ -56,7 +62,7 @@ pub(crate) type MultipleRegistrationResponse = WithErrorResponses, persistent: bool, ) -> SingleRegistrationResponse { let Some(session) = CassandraSession::get(persistent) else { error!( @@ -67,7 +73,7 @@ pub(crate) async fn get_latest_registration_from_stake_addr( }; let registration = - match latest_registration_from_stake_addr(stake_pub_key, session.clone()).await { + match latest_registration_from_stake_addr(stake_pub_key.clone(), session.clone()).await { Ok(registrations) => registrations, Err(err) => { error!( @@ -80,9 +86,9 @@ pub(crate) async fn get_latest_registration_from_stake_addr( }; let raw_invalids = - get_invalid_registrations(stake_pub_key, registration.slot_no.into(), session).await; + get_invalid_registrations(&stake_pub_key, registration.slot_no, session).await; - let invalids_report = match raw_invalids { + let _invalids_report = match raw_invalids { Ok(invalids) => invalids, Err(err) => { error!( @@ -94,15 +100,13 @@ pub(crate) async fn get_latest_registration_from_stake_addr( }, }; - let report = Cip36Reporting::new(vec![registration], invalids_report); - - ResponseSingleRegistration::Ok(Json(report)).into() + ResponseSingleRegistration::NotFound.into() } /// Get latest registration given a stake addr async fn latest_registration_from_stake_addr( - stake_pub_key: &ed25519_dalek::VerifyingKey, session: Arc, -) -> anyhow::Result { + stake_pub_key: Vec, session: Arc, +) -> anyhow::Result { sort_latest_registration( get_all_registrations_from_stake_pub_key(session, stake_pub_key).await?, ) @@ -110,8 +114,8 @@ async fn latest_registration_from_stake_addr( /// Get all cip36 registrations for a given stake address. async fn get_all_registrations_from_stake_pub_key( - session: Arc, stake_pub_key: &ed25519_dalek::VerifyingKey, -) -> Result, anyhow::Error> { + session: Arc, stake_pub_key: Vec, +) -> Result, anyhow::Error> { let mut registrations_iter = GetRegistrationQuery::execute(&session, stake_pub_key.into()).await?; let mut registrations = Vec::new(); @@ -130,15 +134,16 @@ async fn get_all_registrations_from_stake_pub_key( continue; }; - let cip36 = Cip36Info { - stake_pub_key: row.stake_address.try_into()?, - nonce, - slot_no, - txn: row.txn, - vote_key: hex::encode(row.vote_key), - payment_address: hex::encode(row.payment_address), + let cip36 = Cip36Details { + slot_no: SlotNo::from(slot_no), + stake_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.stake_address)?), + vote_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.vote_key)?), + nonce: Some(Nonce::from(nonce)), + txn: Some(TxnIndex::try_from(row.txn)?), + payment_address: Some(Cip19ShelleyAddress::new(hex::encode(row.payment_address))), is_payable: row.is_payable, - cip36: row.cip36, + cip15: !row.cip36, + errors: vec![], }; registrations.push(cip36); @@ -147,8 +152,8 @@ async fn get_all_registrations_from_stake_pub_key( } /// 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| Reverse(k.slot_no)); +fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { + registrations.sort_by_key(|k| Reverse(k.slot_no.clone())); registrations.into_iter().next().ok_or(anyhow::anyhow!( "Can't sort latest registrations by slot no" )) @@ -156,25 +161,31 @@ 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_pub_key: &ed25519_dalek::VerifyingKey, slot_no: num_bigint::BigInt, - session: Arc, -) -> anyhow::Result> { + stake_pub_key: &[u8], slot_no: SlotNo, session: Arc, +) -> anyhow::Result> { let mut invalid_registrations_iter = GetInvalidRegistrationQuery::execute( &session, - GetInvalidRegistrationParams::new(stake_pub_key.as_bytes().to_vec(), slot_no), + GetInvalidRegistrationParams::new(stake_pub_key.to_owned(), slot_no.clone()), ) .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, - stake_address: row.stake_address.try_into()?, - vote_key: hex::encode(row.vote_key), - payment_address: hex::encode(row.payment_address), + invalid_registrations.push(Cip36Details { + slot_no: slot_no.clone(), + stake_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.stake_address)?), + vote_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.vote_key)?), + nonce: None, + txn: None, + payment_address: Some(Cip19ShelleyAddress::new(hex::encode(row.payment_address))), is_payable: row.is_payable, - cip36: row.cip36, + cip15: !row.cip36, + errors: row + .error_report + .iter() + .map(|e| ErrorMessage::from(e.to_string())) + .collect(), }); } @@ -184,30 +195,42 @@ 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: &str, -) -> Vec { + registrations: Vec, associated_voting_key: &str, +) -> Vec { registrations .into_iter() - .filter(|r| r.vote_key == associated_voting_key) + .filter(|r| { + r.vote_pub_key.clone().unwrap() + == Ed25519HexEncodedPublicKey::try_from(associated_voting_key).unwrap() + }) .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, -) -> SingleRegistrationResponse { +) -> AllRegistration { let stake_hash = match hex::decode(stake_hash) { Ok(stake_hash) => stake_hash, Err(err) => { error!(id="get_latest_registration_from_stake_key_hash_stake_hash",error=?err, "Failed to decode stake hash"); - return ResponseSingleRegistration::NotFound.into(); + // return ResponseSingleRegistration::NotFound.into(); + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + )]); }, }; let Some(session) = CassandraSession::get(persistent) else { error!("Failed to acquire db session"); - let err = anyhow::anyhow!("Failed to acquire db session"); - return SingleRegistrationResponse::service_unavailable(&err, RetryAfterOption::Default); + let _err = anyhow::anyhow!("Failed to acquire db session"); + // return SingleRegistrationResponse::service_unavailable(&err, + // RetryAfterOption::Default); + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + )]); }; // Get stake addr associated with give stake hash @@ -220,7 +243,11 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( error=?err, "Failed to query stake addr from stake hash" ); - return ResponseSingleRegistration::NotFound.into(); + // return ResponseSingleRegistration::NotFound.into(); + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + )]); }, }; @@ -233,21 +260,16 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( error=?err, "Failed to get latest registration" ); - return ResponseSingleRegistration::NotFound.into(); - }, - }; - - let stake_pub_key = match ed25519::verifying_key_from_vec(&row.stake_address) { - Ok(v) => v, - Err(err) => { - error!(error=?err, "Invalid Stake Public Key in database."); - let err = anyhow!(err); - return SingleRegistrationResponse::internal_error(&err); + // return ResponseSingleRegistration::NotFound.into(); + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + )]); }, }; let registration = match latest_registration_from_stake_addr( - &stake_pub_key, + row.stake_address.clone(), session.clone(), ) .await @@ -259,15 +281,19 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( error=?err, "Failed to obtain registration for given stake addr", ); - return ResponseSingleRegistration::NotFound.into(); + // return ResponseSingleRegistration::NotFound.into(); + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + )]); }, }; // include any erroneous registrations which occur AFTER the slot# of the last valid // registration let invalids_report = match get_invalid_registrations( - &stake_pub_key, - registration.slot_no.into(), + &row.stake_address, + registration.slot_no.clone(), session, ) .await @@ -279,16 +305,33 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( error=?err, "Failed to obtain invalid registrations for given stake addr", ); - return ResponseSingleRegistration::NotFound.into(); + // return ResponseSingleRegistration::NotFound.into(); + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + )]); }, }; - let report = Cip36Reporting::new(vec![registration], invalids_report); + let r = Cip36RegistrationsForVotingPublicKey { + vote_pub_key: registration.vote_pub_key.clone().unwrap(), + registrations: vec![registration.clone()], + }; + + let a = Cip36RegistrationList { + slot: registration.slot_no, + voting_key: vec![r], + invalid: invalids_report, + page: CurrentPage::new(1, 1, 1).unwrap(), + }; - return ResponseSingleRegistration::Ok(Json(report)).into(); + return AllRegistration::With(Cip36Registration::Ok(poem_openapi::payload::Json(a))); } - ResponseSingleRegistration::NotFound.into() + AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + )]) } /// Returns the list of stake address registrations currently associated with a given @@ -347,33 +390,25 @@ pub(crate) async fn get_associated_vote_key_registrations( }, }; - let stake_pub_key = match ed25519::verifying_key_from_vec(&row.stake_address) { - Ok(k) => k, + // 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_pub_key( + session.clone(), + row.stake_address.clone(), + ) + .await + { + Ok(registration) => registration, Err(err) => { error!( - id="get_associated_vote_key_registrations_latest_registration", + id="get_associated_vote_key_registrations_get_registrations_for_stake_addr", error=?err, - "Not a valid staking public key" + "Failed to obtain registrations for given stake addr", ); return ResponseMultipleRegistrations::NotFound.into(); }, }; - // 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_pub_key(session.clone(), &stake_pub_key).await { - Ok(registration) => registration, - Err(err) => { - error!( - id="get_associated_vote_key_registrations_get_registrations_for_stake_addr", - error=?err, - "Failed to obtain registrations for given stake addr", - ); - return ResponseMultipleRegistrations::NotFound.into(); - }, - }; - // check registrations (stake addrs) are still actively associated with the voting key, // and have not been registered to another voting key. let redacted_registrations = @@ -381,12 +416,12 @@ pub(crate) async fn get_associated_vote_key_registrations( // Report includes registration info and any erroneous registrations which occur AFTER // the slot# of the last valid registration - let mut reports = Cip36ReportingList::new(); + let reports = Cip36ReportingList::new(); for registration in redacted_registrations { - let invalids_report = match get_invalid_registrations( - &stake_pub_key, - registration.slot_no.into(), + let _invalids_report = match get_invalid_registrations( + &row.stake_address, + registration.slot_no, session.clone(), ) .await @@ -401,8 +436,6 @@ pub(crate) async fn get_associated_vote_key_registrations( continue; }, }; - - reports.add(Cip36Reporting::new(vec![registration], invalids_report)); } return ResponseMultipleRegistrations::Ok(Json(reports)).into(); diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs index fc9d0b5411a..6e599104da4 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs @@ -31,15 +31,15 @@ pub(crate) struct Cip36RegistrationList { /// Errors are reported only if they fall between the last valid registration and this /// slot number. /// Earlier errors are never reported. - slot: common::types::cardano::slot_no::SlotNo, + pub slot: common::types::cardano::slot_no::SlotNo, /// List of registrations associated with the query. #[oai(validator(max_items = "100"))] - voting_key: Vec, + pub voting_key: Vec, /// List of latest invalid registrations that were found, for the requested filter. #[oai(skip_serializing_if_is_empty, validator(max_items = "10"))] - invalid: Vec, + pub invalid: Vec, /// Current Page - page: common::objects::generic::pagination::CurrentPage, + pub page: common::objects::generic::pagination::CurrentPage, } impl Example for Cip36RegistrationList { @@ -75,7 +75,7 @@ impl Example for Cip36RegistrationsForVotingPublicKey { } /// CIP36 Registration Data as found on-chain. -#[derive(Object)] +#[derive(Object, Clone)] #[oai(example = true)] pub(crate) struct Cip36Details { /// Blocks Slot Number that the registration certificate is in. diff --git a/catalyst-gateway/bin/src/service/common/objects/cardano/cip36.rs b/catalyst-gateway/bin/src/service/common/objects/cardano/cip36.rs index 707d1e5631b..c0c5d2ec2e8 100644 --- a/catalyst-gateway/bin/src/service/common/objects/cardano/cip36.rs +++ b/catalyst-gateway/bin/src/service/common/objects/cardano/cip36.rs @@ -23,6 +23,7 @@ impl Cip36ReportingList { } /// Add a new `Cip36Reporting` to the list. + #[allow(dead_code)] pub(crate) fn add(&mut self, cip36: Cip36Reporting) { self.cip36.push(cip36); } @@ -50,6 +51,7 @@ pub(crate) struct Cip36Reporting { impl Cip36Reporting { /// Create a new instance of `Cip36Reporting`. + #[allow(dead_code)] pub(crate) fn new(cip36: Vec, invalids: Vec) -> Self { Self { cip36, invalids } } diff --git a/catalyst-gateway/bin/src/service/common/objects/generic/pagination.rs b/catalyst-gateway/bin/src/service/common/objects/generic/pagination.rs index ee92db00ae2..4e3e0a99964 100644 --- a/catalyst-gateway/bin/src/service/common/objects/generic/pagination.rs +++ b/catalyst-gateway/bin/src/service/common/objects/generic/pagination.rs @@ -43,7 +43,7 @@ impl CurrentPage { /// - Invalid `limit` value, must be in range /// - Invalid `remaining` value, must be in range #[allow(dead_code)] - fn new(page: u64, limit: u64, remaining: u64) -> anyhow::Result { + pub fn new(page: u64, limit: u64, remaining: u64) -> anyhow::Result { Ok(Self { page: page.try_into()?, limit: limit.try_into()?, diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/cip19_stake_address.rs b/catalyst-gateway/bin/src/service/common/types/cardano/cip19_stake_address.rs index 83f01951003..979f4d67d4f 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/cip19_stake_address.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/cip19_stake_address.rs @@ -17,6 +17,7 @@ use poem_openapi::{ }; use serde_json::Value; +use super::hash28::HexEncodedHash28; use crate::service::common::types::string_types::impl_string_types; /// Stake address title. @@ -145,6 +146,15 @@ impl TryInto for Cip19StakeAddress { } } +impl TryInto for Cip19StakeAddress { + type Error = anyhow::Error; + + fn try_into(self) -> Result { + let stake_addr: StakeAddress = self.try_into()?; + HexEncodedHash28::try_from(stake_addr.payload().as_hash().to_vec()) + } +} + impl Example for Cip19StakeAddress { fn example() -> Self { Self(EXAMPLE.to_owned()) @@ -180,4 +190,14 @@ mod tests { let stake_address = Cip19StakeAddress::try_from(INVALID_STAKE_ADDRESS.to_string()); assert!(stake_address.is_err()); } + + #[test] + fn test_stake_address_to_stake_hash() { + let stake_address_prod = + Cip19StakeAddress::try_from(VALID_PROD_STAKE_ADDRESS.to_string()).unwrap(); + + let stake_addr: StakeAddress = stake_address_prod.try_into().unwrap(); + + assert!(stake_addr.payload().as_hash().len() == 28); + } } diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/nonce.rs b/catalyst-gateway/bin/src/service/common/types/cardano/nonce.rs index e99679b7d7f..18639402487 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/nonce.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/nonce.rs @@ -39,7 +39,7 @@ static SCHEMA: LazyLock = LazyLock::new(|| { }); /// Value of a Nonce. -#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Debug, Eq, PartialEq, Hash, Clone)] pub(crate) struct Nonce(u64); /// Is the Nonce valid? diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/slot_no.rs b/catalyst-gateway/bin/src/service/common/types/cardano/slot_no.rs index f2e2f2d2daa..59c70c0e171 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/slot_no.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/slot_no.rs @@ -3,6 +3,7 @@ use std::sync::LazyLock; use anyhow::bail; +use num_bigint::BigInt; use poem_openapi::{ registry::{MetaSchema, MetaSchemaRef}, types::{Example, ParseError, ParseFromJSON, ParseFromParameter, ParseResult, ToJSON, Type}, @@ -34,7 +35,8 @@ static SCHEMA: LazyLock = LazyLock::new(|| { }); /// Slot number -#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Debug, Eq, PartialEq, Hash, Clone, PartialOrd, Ord)] + pub(crate) struct SlotNo(u64); /// Is the Slot Number valid? @@ -122,6 +124,11 @@ impl SlotNo { pub(crate) fn into_option>(value: Option) -> Option { value.map(std::convert::Into::into) } + + /// big int + pub(crate) fn into_big_int(self) -> BigInt { + BigInt::from(self.0) + } } impl Example for SlotNo { diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/txn_index.rs b/catalyst-gateway/bin/src/service/common/types/cardano/txn_index.rs index 7a48bd1c344..d37b7008962 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/txn_index.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/txn_index.rs @@ -36,7 +36,7 @@ static SCHEMA: LazyLock = LazyLock::new(|| { }); /// Transaction Index within a block. -#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Debug, Eq, PartialEq, Hash, Clone)] pub(crate) struct TxnIndex(u16); /// Is the Slot Number valid? diff --git a/catalyst-gateway/bin/src/settings/cassandra_db.rs b/catalyst-gateway/bin/src/settings/cassandra_db.rs index 6ce6e220a26..8d4aa620dc7 100644 --- a/catalyst-gateway/bin/src/settings/cassandra_db.rs +++ b/catalyst-gateway/bin/src/settings/cassandra_db.rs @@ -9,13 +9,13 @@ use crate::db::{ }; /// Default Cassandra DB URL for the Persistent DB. -pub(super) const PERSISTENT_URL_DEFAULT: &str = "127.0.0.1:9042"; +pub(super) const PERSISTENT_URL_DEFAULT: &str = "172.17.0.2:9042"; /// Default Cassandra DB URL for the Persistent DB. pub(super) const PERSISTENT_NAMESPACE_DEFAULT: &str = "persistent"; /// Default Cassandra DB URL for the Persistent DB. -pub(super) const VOLATILE_URL_DEFAULT: &str = "127.0.0.1:9042"; +pub(super) const VOLATILE_URL_DEFAULT: &str = "172.17.0.2:9042"; /// Default Cassandra DB URL for the Persistent DB. pub(super) const VOLATILE_NAMESPACE_DEFAULT: &str = "volatile"; From 9200bc5d8fb5f9e1095c72a7c90beacc7b859dec Mon Sep 17 00:00:00 2001 From: cong-or Date: Sun, 5 Jan 2025 12:26:54 +0000 Subject: [PATCH 02/46] refactor(cip36): remove legacy endpoints --- .../src/db/index/queries/registrations/mod.rs | 1 + .../src/service/api/cardano/cip36/endpoint.rs | 23 +- .../bin/src/service/api/cardano/cip36/mod.rs | 63 +-- .../service/api/cardano/cip36/old_endpoint.rs | 445 ------------------ .../src/service/api/cardano/cip36/response.rs | 4 +- .../service/common/objects/cardano/cip36.rs | 144 ------ .../src/service/common/objects/cardano/mod.rs | 1 - 7 files changed, 25 insertions(+), 656 deletions(-) delete mode 100644 catalyst-gateway/bin/src/service/api/cardano/cip36/old_endpoint.rs delete mode 100644 catalyst-gateway/bin/src/service/common/objects/cardano/cip36.rs 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 b0f2edfa4e4..8518dd16584 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs @@ -1,5 +1,6 @@ //! Registration related queries. pub(crate) mod get_from_stake_addr; pub(crate) mod get_from_stake_hash; +#[allow(dead_code)] pub(crate) mod get_from_vote_key; pub(crate) mod get_invalid; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index f46e13e9c2c..17daf5d71a8 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -6,10 +6,16 @@ use tracing::{error, info}; use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddressOrPublicKey}; use super::{ cardano::{self}, - old_endpoint::get_latest_registration_from_stake_key_hash, + filter::get_latest_registration_from_stake_key_hash, response, NoneOrRBAC, SlotNo, }; -use crate::service::common::{self}; +use crate::{ + db::index::session::CassandraSession, + service::{ + api::cardano::cip36::response::AllRegistration, + common::{self}, + }, +}; /// Process the endpoint operation #[allow(clippy::unused_async)] @@ -19,6 +25,17 @@ pub(crate) async fn cip36_registrations( _limit: common::types::generic::query::pagination::Limit, _auth: NoneOrRBAC, _headers: &HeaderMap, ) -> response::AllRegistration { + let Some(session) = CassandraSession::get(true) else { + error!("Failed to acquire db session"); + let _err = anyhow::anyhow!("Failed to acquire db session"); + // return SingleRegistrationResponse::service_unavailable(&err, + // RetryAfterOption::Default); + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Invalid Stake Address or Voter Key", + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }; + if let Some(_slot) = asat { } else { // If _asat is None, then get the latest slot number from the chain follower and @@ -43,7 +60,7 @@ pub(crate) async fn cip36_registrations( }; // Get stake public key from stake hash - match get_latest_registration_from_stake_key_hash(stake_hash.to_string(), true) + match get_latest_registration_from_stake_key_hash(stake_hash.to_string(), session) .await { common::responses::WithErrorResponses::With(resp) => resp, diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs index 2a2ea11e3c1..3f6919048c4 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs @@ -13,7 +13,8 @@ use crate::service::common::{ }; pub(crate) mod endpoint; -pub(crate) mod old_endpoint; +#[allow(dead_code)] +pub(crate) mod filter; pub(crate) mod response; @@ -77,64 +78,4 @@ impl Api { ) .await } - - /// Get latest CIP36 registrations from stake address. - /// - /// This endpoint gets the latest registration given a stake address. - #[oai( - path = "/draft/cardano/cip36/latest_registration/stake_addr", - method = "get", - operation_id = "latestRegistrationGivenStakeAddr" - )] - async fn latest_registration_cip36_given_stake_addr( - &self, - /// Stake Public Key to find the latest registration for. - stake_pub_key: Query, // Validation provided by type. - /// No Authorization required, but Token permitted. - _auth: NoneOrRBAC, - ) -> old_endpoint::SingleRegistrationResponse { - let hex_key = stake_pub_key.0; - - old_endpoint::get_latest_registration_from_stake_addr(hex_key.as_bytes().to_vec(), true) - .await - } - - /// Get latest CIP36 registrations from a stake key hash. - /// - /// This endpoint gets the latest registration given a stake key hash. - #[oai( - path = "/draft/cardano/cip36/latest_registration/stake_key_hash", - method = "get", - operation_id = "latestRegistrationGivenStakeHash" - )] - async fn latest_registration_cip36_given_stake_key_hash( - &self, - /// Stake Key Hash to find the latest registration for. - #[oai(validator(max_length = 66, min_length = 0, pattern = "[0-9a-f]"))] - stake_key_hash: Query, - /// No Authorization required, but Token permitted. - _auth: NoneOrRBAC, - ) -> response::AllRegistration { - old_endpoint::get_latest_registration_from_stake_key_hash(stake_key_hash.0, true).await - } - - /// Get latest CIP36 registrations from voting key. - /// - /// This endpoint returns the list of stake address registrations currently associated - /// with a given voting key. - #[oai( - path = "/draft/cardano/cip36/latest_registration/vote_key", - method = "get", - operation_id = "latestRegistrationGivenVoteKey" - )] - async fn latest_registration_cip36_given_vote_key( - &self, - /// Voting Key to find CIP36 registrations for. - #[oai(validator(max_length = 66, min_length = 66, pattern = "0x[0-9a-f]"))] - vote_key: Query, - /// No Authorization required, but Token permitted. - _auth: NoneOrRBAC, - ) -> old_endpoint::MultipleRegistrationResponse { - old_endpoint::get_associated_vote_key_registrations(vote_key.0, true).await - } } diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/old_endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/old_endpoint.rs deleted file mode 100644 index e97aec39274..00000000000 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/old_endpoint.rs +++ /dev/null @@ -1,445 +0,0 @@ -//! Implementation of the GET `/cardano/cip36` endpoint - -use std::{cmp::Reverse, sync::Arc}; - -use futures::StreamExt; -use poem::http::StatusCode; -use poem_openapi::{payload::Json, ApiResponse}; -use tracing::error; - -use super::{ - cardano::{cip19_shelley_address::Cip19ShelleyAddress, nonce::Nonce, txn_index::TxnIndex}, - common::{objects::generic::pagination::CurrentPage, types::generic::error_msg::ErrorMessage}, - response::{ - AllRegistration, Cip36Details, Cip36Registration, Cip36RegistrationList, - Cip36RegistrationsForVotingPublicKey, - }, - Ed25519HexEncodedPublicKey, SlotNo, -}; -use crate::{ - db::index::{ - queries::registrations::{ - get_from_stake_addr::GetRegistrationQuery, - get_from_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, - get_from_vote_key::{GetStakeAddrFromVoteKeyParams, GetStakeAddrFromVoteKeyQuery}, - get_invalid::{GetInvalidRegistrationParams, GetInvalidRegistrationQuery}, - }, - session::CassandraSession, - }, - service::common::{ - objects::cardano::cip36::{Cip36Reporting, Cip36ReportingList}, - responses::WithErrorResponses, - }, -}; - -/// Endpoint responses. -#[derive(ApiResponse)] -#[allow(dead_code)] -pub(crate) enum ResponseSingleRegistration { - /// A CIP36 registration report. - #[oai(status = 200)] - Ok(Json), - /// No valid registration. - #[oai(status = 404)] - NotFound, -} - -/// Endpoint responses. -#[derive(ApiResponse)] -pub(crate) enum ResponseMultipleRegistrations { - /// All CIP36 registrations associated with the same Voting Key. - #[oai(status = 200)] - Ok(Json), - /// No valid registration. - #[oai(status = 404)] - NotFound, -} - -/// Single registration response -pub(crate) type SingleRegistrationResponse = WithErrorResponses; -/// All responses voting key -pub(crate) type MultipleRegistrationResponse = WithErrorResponses; - -/// Get latest registration given a stake public key -pub(crate) async fn get_latest_registration_from_stake_addr( - stake_pub_key: Vec, persistent: bool, -) -> SingleRegistrationResponse { - let Some(session) = CassandraSession::get(persistent) else { - error!( - id = "get_latest_registration_from_stake_addr", - "Failed to acquire db session" - ); - return ResponseSingleRegistration::NotFound.into(); - }; - - let registration = - match latest_registration_from_stake_addr(stake_pub_key.clone(), session.clone()).await { - Ok(registrations) => registrations, - Err(err) => { - error!( - id="get_latest_registration_from_stake_addr", - error=?err, - "Failed to obtain registrations for given stake addr", - ); - return ResponseSingleRegistration::NotFound.into(); - }, - }; - - let raw_invalids = - get_invalid_registrations(&stake_pub_key, registration.slot_no, session).await; - - let _invalids_report = match raw_invalids { - Ok(invalids) => invalids, - Err(err) => { - error!( - id="get_latest_registration_from_stake_addr", - error=?err, - "Failed to obtain invalid registrations for given stake addr", - ); - return ResponseSingleRegistration::NotFound.into(); - }, - }; - - ResponseSingleRegistration::NotFound.into() -} - -/// Get latest registration given a stake addr -async fn latest_registration_from_stake_addr( - stake_pub_key: Vec, session: Arc, -) -> anyhow::Result { - sort_latest_registration( - get_all_registrations_from_stake_pub_key(session, stake_pub_key).await?, - ) -} - -/// Get all cip36 registrations for a given stake address. -async fn get_all_registrations_from_stake_pub_key( - session: Arc, stake_pub_key: Vec, -) -> Result, anyhow::Error> { - let mut registrations_iter = - GetRegistrationQuery::execute(&session, stake_pub_key.into()).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 = Cip36Details { - slot_no: SlotNo::from(slot_no), - stake_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.stake_address)?), - vote_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.vote_key)?), - nonce: Some(Nonce::from(nonce)), - txn: Some(TxnIndex::try_from(row.txn)?), - payment_address: Some(Cip19ShelleyAddress::new(hex::encode(row.payment_address))), - is_payable: row.is_payable, - cip15: !row.cip36, - errors: vec![], - }; - - 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| Reverse(k.slot_no.clone())); - 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_pub_key: &[u8], slot_no: SlotNo, session: Arc, -) -> anyhow::Result> { - let mut invalid_registrations_iter = GetInvalidRegistrationQuery::execute( - &session, - GetInvalidRegistrationParams::new(stake_pub_key.to_owned(), slot_no.clone()), - ) - .await?; - let mut invalid_registrations = Vec::new(); - while let Some(row) = invalid_registrations_iter.next().await { - let row = row?; - - invalid_registrations.push(Cip36Details { - slot_no: slot_no.clone(), - stake_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.stake_address)?), - vote_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.vote_key)?), - nonce: None, - txn: None, - payment_address: Some(Cip19ShelleyAddress::new(hex::encode(row.payment_address))), - is_payable: row.is_payable, - cip15: !row.cip36, - errors: row - .error_report - .iter() - .map(|e| ErrorMessage::from(e.to_string())) - .collect(), - }); - } - - 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: &str, -) -> Vec { - registrations - .into_iter() - .filter(|r| { - r.vote_pub_key.clone().unwrap() - == Ed25519HexEncodedPublicKey::try_from(associated_voting_key).unwrap() - }) - .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, -) -> AllRegistration { - let stake_hash = match hex::decode(stake_hash) { - Ok(stake_hash) => stake_hash, - Err(err) => { - error!(id="get_latest_registration_from_stake_key_hash_stake_hash",error=?err, "Failed to decode stake hash"); - // return ResponseSingleRegistration::NotFound.into(); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }, - }; - - let Some(session) = CassandraSession::get(persistent) else { - error!("Failed to acquire db session"); - let _err = anyhow::anyhow!("Failed to acquire db session"); - // return SingleRegistrationResponse::service_unavailable(&err, - // RetryAfterOption::Default); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }; - - // 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, - Err(err) => { - error!( - id="get_latest_registration_from_stake_key_hash_query_stake_addr", - error=?err, - "Failed to query stake addr from stake hash" - ); - // return ResponseSingleRegistration::NotFound.into(); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }, - }; - - if let Some(row_stake_addr) = stake_addr_iter.next().await { - let row = match row_stake_addr { - Ok(r) => r, - Err(err) => { - error!( - id="get_latest_registration_from_stake_key_hash_latest_registration", - error=?err, - "Failed to get latest registration" - ); - // return ResponseSingleRegistration::NotFound.into(); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }, - }; - - let registration = match latest_registration_from_stake_addr( - row.stake_address.clone(), - session.clone(), - ) - .await - { - Ok(registration) => registration, - Err(err) => { - error!( - id="get_latest_registration_from_stake_key_hash_registration_for_stake_addr", - error=?err, - "Failed to obtain registration for given stake addr", - ); - // return ResponseSingleRegistration::NotFound.into(); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }, - }; - - // include any erroneous registrations which occur AFTER the slot# of the last valid - // registration - let invalids_report = match get_invalid_registrations( - &row.stake_address, - registration.slot_no.clone(), - session, - ) - .await - { - Ok(invalids) => invalids, - Err(err) => { - error!( - id="get_latest_registration_from_stake_key_hash_invalid_registrations_for_stake_addr", - error=?err, - "Failed to obtain invalid registrations for given stake addr", - ); - // return ResponseSingleRegistration::NotFound.into(); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }, - }; - - let r = Cip36RegistrationsForVotingPublicKey { - vote_pub_key: registration.vote_pub_key.clone().unwrap(), - registrations: vec![registration.clone()], - }; - - let a = Cip36RegistrationList { - slot: registration.slot_no, - voting_key: vec![r], - invalid: invalids_report, - page: CurrentPage::new(1, 1, 1).unwrap(), - }; - - return AllRegistration::With(Cip36Registration::Ok(poem_openapi::payload::Json(a))); - } - - AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - )]) -} - -/// 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_associated_vote_key_registrations( - vote_key: String, persistent: bool, -) -> MultipleRegistrationResponse { - let vote_key = match hex::decode(vote_key) { - Ok(vote_key) => vote_key, - Err(err) => { - error!( - id="get_associated_vote_key_registrations_vote_key", - error=?err, - "Failed to decode vote key" - ); - return ResponseMultipleRegistrations::NotFound.into(); - }, - }; - - let Some(session) = CassandraSession::get(persistent) else { - error!( - id = "get_associated_vote_key_registrations_db_session", - "Failed to acquire db session" - ); - return ResponseMultipleRegistrations::NotFound.into(); - }; - - let mut stake_addr_iter = match GetStakeAddrFromVoteKeyQuery::execute( - &session, - GetStakeAddrFromVoteKeyParams::new(vote_key.clone()), - ) - .await - { - Ok(latest) => latest, - Err(err) => { - error!( - id="get_associated_vote_key_registrations_query_stake_addr_from_vote_key", - error=?err, - "Failed to query stake addr from vote key" - ); - return ResponseMultipleRegistrations::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!( - id="get_associated_vote_key_registrations_latest_registration", - error=?err, - "Failed to get latest registration" - ); - return ResponseMultipleRegistrations::NotFound.into(); - }, - }; - - // 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_pub_key( - session.clone(), - row.stake_address.clone(), - ) - .await - { - Ok(registration) => registration, - Err(err) => { - error!( - id="get_associated_vote_key_registrations_get_registrations_for_stake_addr", - error=?err, - "Failed to obtain registrations for given stake addr", - ); - return ResponseMultipleRegistrations::NotFound.into(); - }, - }; - - // 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)); - - // Report includes registration info and any erroneous registrations which occur AFTER - // the slot# of the last valid registration - let reports = Cip36ReportingList::new(); - - for registration in redacted_registrations { - let _invalids_report = match get_invalid_registrations( - &row.stake_address, - registration.slot_no, - session.clone(), - ) - .await - { - Ok(invalids) => invalids, - Err(err) => { - error!( - id="get_associated_vote_key_registrations_invalid_registrations_for_stake_addr", - error=?err, - "Failed to obtain invalid registrations for given stake addr", - ); - continue; - }, - }; - } - - return ResponseMultipleRegistrations::Ok(Json(reports)).into(); - } - - ResponseMultipleRegistrations::NotFound.into() -} diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs index 6e599104da4..7fae26b687a 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs @@ -39,7 +39,7 @@ pub(crate) struct Cip36RegistrationList { #[oai(skip_serializing_if_is_empty, validator(max_items = "10"))] pub invalid: Vec, /// Current Page - pub page: common::objects::generic::pagination::CurrentPage, + pub page: Option, } impl Example for Cip36RegistrationList { @@ -48,7 +48,7 @@ impl Example for Cip36RegistrationList { slot: (common::types::cardano::slot_no::EXAMPLE + 635).into(), voting_key: vec![Cip36RegistrationsForVotingPublicKey::example()], invalid: vec![Cip36Details::invalid_example()], - page: common::objects::generic::pagination::CurrentPage::example(), + page: Some(common::objects::generic::pagination::CurrentPage::example()), } } } diff --git a/catalyst-gateway/bin/src/service/common/objects/cardano/cip36.rs b/catalyst-gateway/bin/src/service/common/objects/cardano/cip36.rs deleted file mode 100644 index c0c5d2ec2e8..00000000000 --- a/catalyst-gateway/bin/src/service/common/objects/cardano/cip36.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! CIP36 object - -// TODO: This is NOT common, remove it once the rationalized endpoint is implemented. -// Retained to keep the existing code from breaking only. - -use poem_openapi::{types::Example, Object}; - -use crate::service::common::types::generic::ed25519_public_key::Ed25519HexEncodedPublicKey; - -/// List of CIP36 Registration Data as found on-chain. -#[derive(Object, Default)] -#[oai(example = true)] -pub(crate) struct Cip36ReportingList { - /// List of registrations associated with the same Voting Key - #[oai(validator(max_items = "100000"))] - cip36: Vec, -} - -impl Cip36ReportingList { - /// Create a new instance of `Cip36ReportingList`. - pub(crate) fn new() -> Self { - Self { cip36: vec![] } - } - - /// Add a new `Cip36Reporting` to the list. - #[allow(dead_code)] - pub(crate) fn add(&mut self, cip36: Cip36Reporting) { - self.cip36.push(cip36); - } -} - -impl Example for Cip36ReportingList { - fn example() -> Self { - Self { - cip36: vec![Cip36Reporting::example()], - } - } -} - -/// CIP36 info + invalid reporting. -#[derive(Object, Default)] -#[oai(example = true)] -pub(crate) struct Cip36Reporting { - /// List of registrations. - #[oai(validator(max_items = "100000"))] - cip36: Vec, - /// Invalid registration reporting. - #[oai(validator(max_items = "100000"))] - invalids: Vec, -} - -impl Cip36Reporting { - /// Create a new instance of `Cip36Reporting`. - #[allow(dead_code)] - pub(crate) fn new(cip36: Vec, invalids: Vec) -> Self { - Self { cip36, invalids } - } -} - -impl Example for Cip36Reporting { - fn example() -> Self { - Self { - cip36: vec![Cip36Info::example()], - invalids: vec![InvalidRegistrationsReport::example()], - } - } -} - -/// CIP36 Registration Data as found on-chain. -#[derive(Object)] -#[oai(example = true)] -pub(crate) struct Cip36Info { - /// Full Stake Address (not hashed, 32 byte ED25519 Public key). - pub stake_pub_key: Ed25519HexEncodedPublicKey, // Validation provided by type - /// 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 = 116, 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, - /// Is the Registration CIP36 format, or CIP15 - pub cip36: bool, -} - -impl Example for Cip36Info { - fn example() -> Self { - Self { - stake_pub_key: Ed25519HexEncodedPublicKey::example(), - nonce: 0, - slot_no: 12345, - txn: 0, - vote_key: "0xa6a3c0447aeb9cc54cf6422ba32b294e5e1c3ef6d782f2acff4a70694c4d1663" - .to_string(), - payment_address: "0x00588e8e1d18cba576a4d35758069fe94e53f638b6faf7c07b8abd2bc5c5cdee47b60edc7772855324c85033c638364214cbfc6627889f81c4".to_string(), - is_payable: false, - cip36: true, - } - } -} - -/// Invalid registration error reporting. -#[derive(Object)] -#[oai(example = true)] -pub(crate) struct InvalidRegistrationsReport { - /// Error report - #[oai(validator(max_items = "100000", max_length = "100", pattern = ".*"))] - pub error_report: Vec, - /// Full Stake Public Key (32 byte Ed25519 Public key, not hashed). - pub stake_address: Ed25519HexEncodedPublicKey, // Validation provided by the type. - /// Voting Public Key - #[oai(validator(max_length = 66, min_length = 0, pattern = "[0-9a-f]"))] - pub vote_key: String, - /// Full Payment Address (not hashed, 32 byte ED25519 Public key). - #[oai(validator(max_length = 116, min_length = 0, pattern = "[0-9a-f]"))] - 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, -} - -impl Example for InvalidRegistrationsReport { - fn example() -> Self { - Self { - error_report: vec!["Invalid registration".to_string()], - stake_address: Ed25519HexEncodedPublicKey::example(), - vote_key: "0xa6a3c0447aeb9cc54cf6422ba32b294e5e1c3ef6d782f2acff4a70694c4d1663".to_string(), - payment_address: "0x00588e8e1d18cba576a4d35758069fe94e53f638b6faf7c07b8abd2bc5c5cdee47b60edc7772855324c85033c638364214cbfc6627889f81c4".to_string(), - is_payable: false, - cip36: true, - } - } -} diff --git a/catalyst-gateway/bin/src/service/common/objects/cardano/mod.rs b/catalyst-gateway/bin/src/service/common/objects/cardano/mod.rs index 391e5e16e4b..e541c9b1164 100644 --- a/catalyst-gateway/bin/src/service/common/objects/cardano/mod.rs +++ b/catalyst-gateway/bin/src/service/common/objects/cardano/mod.rs @@ -4,7 +4,6 @@ //! common. They should not be simple types. but actual objects. //! Simple types belong in `common/types`. -pub(crate) mod cip36; // TODO: Not common, to be removed once code refactored. pub(crate) mod hash; pub(crate) mod network; pub(crate) mod registration_info; From 6a241a75dc443858bf134d02241586a505ec411b Mon Sep 17 00:00:00 2001 From: cong-or Date: Sun, 5 Jan 2025 18:51:23 +0000 Subject: [PATCH 03/46] refactor(stake addr): get registration by latest or slot no --- .../src/service/api/cardano/cip36/endpoint.rs | 49 +-- .../src/service/api/cardano/cip36/filter.rs | 385 ++++++++++++++++++ .../bin/src/service/api/cardano/cip36/mod.rs | 22 +- .../bin/src/service/common/auth/mod.rs | 1 + .../service/common/types/cardano/query/mod.rs | 1 + .../types/cardano/query/stake_or_voter.rs | 41 +- .../types/generic/ed25519_public_key.rs | 13 + 7 files changed, 455 insertions(+), 57 deletions(-) create mode 100644 catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index 17daf5d71a8..22e2a33c3c5 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -6,7 +6,7 @@ use tracing::{error, info}; use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddressOrPublicKey}; use super::{ cardano::{self}, - filter::get_latest_registration_from_stake_key_hash, + filter::{get_latest_registration_from_stake_key_hash, get_registration_from_stake_addr}, response, NoneOrRBAC, SlotNo, }; use crate::{ @@ -18,7 +18,6 @@ use crate::{ }; /// Process the endpoint operation -#[allow(clippy::unused_async)] pub(crate) async fn cip36_registrations( lookup: Option, asat: Option, _page: common::types::generic::query::pagination::Page, @@ -27,21 +26,12 @@ pub(crate) async fn cip36_registrations( ) -> response::AllRegistration { let Some(session) = CassandraSession::get(true) else { error!("Failed to acquire db session"); - let _err = anyhow::anyhow!("Failed to acquire db session"); - // return SingleRegistrationResponse::service_unavailable(&err, - // RetryAfterOption::Default); return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Invalid Stake Address or Voter Key", + "Connection issue", StatusCode::UNPROCESSABLE_ENTITY, )]); }; - if let Some(_slot) = asat { - } else { - // If _asat is None, then get the latest slot number from the chain follower and - // use that. - } - if let Some(stake_or_voter) = lookup { match StakeAddressOrPublicKey::from(stake_or_voter) { StakeAddressOrPublicKey::Address(cip19_stake_address) => { @@ -49,42 +39,27 @@ pub(crate) async fn cip36_registrations( let stake_hash: HexEncodedHash28 = match cip19_stake_address.try_into() { Ok(stake_hash) => stake_hash, Err(err) => { - error!("stake addr to stake hash {:?}", err); return response::AllRegistration::unprocessable_content(vec![ poem::Error::from_string( - "Invalid Stake Address or Voter Key", + format!("Stake addr to stake hash {err:?}"), StatusCode::UNPROCESSABLE_ENTITY, ), ]); }, }; - // Get stake public key from stake hash - match get_latest_registration_from_stake_key_hash(stake_hash.to_string(), session) - .await - { - common::responses::WithErrorResponses::With(resp) => resp, - common::responses::WithErrorResponses::Error(_error_responses) => { - return response::AllRegistration::unprocessable_content(vec![ - poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - ), - ]); - }, - } + return get_latest_registration_from_stake_key_hash(stake_hash, session).await; }, - StakeAddressOrPublicKey::PublicKey(_ed25519_hex_encoded_public_key) => { - info!("stake public key conor"); - return response::AllRegistration::unprocessable_content(vec![ - poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - ), - ]); + StakeAddressOrPublicKey::PublicKey(ed25519_hex_encoded_public_key) => { + return get_registration_from_stake_addr( + ed25519_hex_encoded_public_key, + asat, + session, + ) + .await; }, StakeAddressOrPublicKey::All => { - info!("stake address conor all"); + info!("ALL"); return response::AllRegistration::unprocessable_content(vec![ poem::Error::from_string( "Invalid Stake Address or Voter Key", diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs new file mode 100644 index 00000000000..2f68ca56548 --- /dev/null +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -0,0 +1,385 @@ +//! Implementation of the GET `/cardano/cip36` endpoint + +use std::{cmp::Reverse, sync::Arc}; + +use futures::StreamExt; +use poem::http::StatusCode; +use tracing::{error, info}; + +use super::{ + cardano::{ + cip19_shelley_address::Cip19ShelleyAddress, hash28::HexEncodedHash28, nonce::Nonce, + txn_index::TxnIndex, + }, + common::types::generic::error_msg::ErrorMessage, + response::{ + AllRegistration, Cip36Details, Cip36Registration, Cip36RegistrationList, + Cip36RegistrationsForVotingPublicKey, + }, + Ed25519HexEncodedPublicKey, SlotNo, +}; +use crate::db::index::{ + queries::registrations::{ + get_from_stake_addr::GetRegistrationQuery, + get_from_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, + get_invalid::{GetInvalidRegistrationParams, GetInvalidRegistrationQuery}, + }, + session::CassandraSession, +}; + +/// Get registration from stake addr +pub async fn get_registration_from_stake_addr( + stake_pub_key: Ed25519HexEncodedPublicKey, asat: Option, session: Arc, +) -> AllRegistration { + let registrations = match get_all_registrations_from_stake_pub_key( + session.clone(), + stake_pub_key.as_bytes().to_vec(), + ) + .await + { + Ok(registration) => registration, + Err(err) => { + error!( + id="get_registration_from_stake_addr", + error=?err, + "Failed to query stake addr" + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to query stake addr {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + let registration = if let Some(slot_no) = asat { + match get_registration_given_slot_no(registrations, &slot_no) { + Ok(registration) => registration, + Err(err) => { + error!( + id="get_registration_given_slot_no", + error=?err, + "Failed to get registration given slot no" + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to get registration given slot no {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + } + } else { + info!("conor {:?}", hex::encode(stake_pub_key.as_bytes())); + + match sort_latest_registration(registrations) { + Ok(registration) => registration, + Err(err) => { + error!( + id="get_latest_registration", + error=?err, + "Failed to sort latest registration" + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to sort latest registration {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + } + }; + + let slot_no = registration.clone().slot_no; + let Some(stake_pub_key) = registration.clone().stake_pub_key else { + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Stake pub key not in registration {stake_pub_key:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }; + + let Some(vote_pub_key) = registration.clone().vote_pub_key else { + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Vote pub key not in registration {stake_pub_key:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }; + + // include any erroneous registrations which occur AFTER the slot# of the last valid + // registration + let invalids_report = match get_invalid_registrations( + stake_pub_key.as_bytes(), + slot_no.clone(), + session, + ) + .await + { + Ok(invalids) => invalids, + Err(err) => { + error!( + id="get_latest_registration_from_stake_key_hash_invalid_registrations_for_stake_addr", + error=?err, + "Failed to obtain invalid registrations for given stake addr", + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to obtain invalid registrations for given stake addr {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + AllRegistration::With(Cip36Registration::Ok(poem_openapi::payload::Json( + Cip36RegistrationList { + slot: slot_no, + voting_key: vec![Cip36RegistrationsForVotingPublicKey { + vote_pub_key, + registrations: vec![registration.clone()], + }], + invalid: invalids_report, + page: None, + }, + ))) +} + +/// Get registration given stake address, latest or based on optional slot no +async fn latest_registration_from_stake_addr( + stake_pub_key: Ed25519HexEncodedPublicKey, session: Arc, +) -> anyhow::Result { + sort_latest_registration( + get_all_registrations_from_stake_pub_key(session, stake_pub_key.as_bytes().to_vec()) + .await?, + ) +} + +/// Get all cip36 registrations for a given stake address. +async fn get_all_registrations_from_stake_pub_key( + session: Arc, stake_pub_key: Vec, +) -> Result, anyhow::Error> { + let mut registrations_iter = + GetRegistrationQuery::execute(&session, stake_pub_key.into()).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 = Cip36Details { + slot_no: SlotNo::from(slot_no), + stake_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.stake_address)?), + vote_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.vote_key)?), + nonce: Some(Nonce::from(nonce)), + txn: Some(TxnIndex::try_from(row.txn)?), + payment_address: Some(Cip19ShelleyAddress::new(hex::encode(row.payment_address))), + is_payable: row.is_payable, + cip15: !row.cip36, + errors: vec![], + }; + + 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(|registration| Reverse(registration.slot_no.clone())); + registrations.into_iter().next().ok_or(anyhow::anyhow!( + "Can't sort latest registrations by slot no" + )) +} + +/// Get registration given slot no. +fn get_registration_given_slot_no( + registrations: Vec, slot_no: &SlotNo, +) -> anyhow::Result { + registrations + .into_iter() + .find(|registration| registration.slot_no == *slot_no) + .ok_or(anyhow::anyhow!("Unable to get registration given slot no")) +} + +/// Get invalid registrations for stake addr after given slot no +async fn get_invalid_registrations( + stake_pub_key: &[u8], slot_no: SlotNo, session: Arc, +) -> anyhow::Result> { + let mut invalid_registrations_iter = GetInvalidRegistrationQuery::execute( + &session, + GetInvalidRegistrationParams::new(stake_pub_key.to_owned(), slot_no.clone()), + ) + .await?; + let mut invalid_registrations = Vec::new(); + while let Some(row) = invalid_registrations_iter.next().await { + let row = row?; + + invalid_registrations.push(Cip36Details { + slot_no: slot_no.clone(), + stake_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.stake_address)?), + vote_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.vote_key)?), + nonce: None, + txn: None, + payment_address: Some(Cip19ShelleyAddress::new(hex::encode(row.payment_address))), + is_payable: row.is_payable, + cip15: !row.cip36, + errors: row + .error_report + .iter() + .map(|e| ErrorMessage::from(e.to_string())) + .collect(), + }); + } + + 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: &str, +) -> anyhow::Result> { + let Ok(associated_key) = Ed25519HexEncodedPublicKey::try_from(associated_voting_key) else { + return Err(anyhow::anyhow!( + "Can't sort latest registrations by slot no" + )); + }; + + Ok(registrations + .into_iter() + .filter(|r| { + r.vote_pub_key + .clone() + .unwrap_or(Ed25519HexEncodedPublicKey::examples(0)) + == associated_key + }) + .collect()) +} + +/// Get latest registration given a stake key hash. +#[allow(clippy::too_many_lines)] +pub(crate) async fn get_latest_registration_from_stake_key_hash( + stake_hash: HexEncodedHash28, session: Arc, +) -> AllRegistration { + // Get stake addr associated with give stake hash + let mut stake_addr_iter = match GetStakeAddrQuery::execute( + &session, + GetStakeAddrParams::new(stake_hash.into()), + ) + .await + { + Ok(latest) => latest, + Err(err) => { + error!( + id="get_latest_registration_from_stake_key_hash_query_stake_addr", + error=?err, + "Failed to query stake addr from stake hash" + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to query stake addr from stake hash {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + if let Some(row_stake_addr) = stake_addr_iter.next().await { + let row = match row_stake_addr { + Ok(r) => r, + Err(err) => { + error!( + id="get_latest_registration_from_stake_key_hash_latest_registration", + error=?err, + "Failed to get latest registration" + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to get latest registration {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + let stake_pub_key = match Ed25519HexEncodedPublicKey::try_from(row.stake_address.clone()) { + Ok(key) => key, + Err(err) => { + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to type stake address {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + let registration = match latest_registration_from_stake_addr(stake_pub_key, session.clone()) + .await + { + Ok(registration) => registration, + Err(err) => { + error!( + id="get_latest_registration_from_stake_key_hash_registration_for_stake_addr", + error=?err, + "Failed to obtain registration for given stake addr", + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to obtain registration for given stake addr {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + // include any erroneous registrations which occur AFTER the slot# of the last valid + // registration + let invalids_report = match get_invalid_registrations( + &row.stake_address, + registration.slot_no.clone(), + session, + ) + .await + { + Ok(invalids) => invalids, + Err(err) => { + error!( + id="get_latest_registration_from_stake_key_hash_invalid_registrations_for_stake_addr", + error=?err, + "Failed to obtain invalid registrations for given stake addr", + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to obtain invalid registrations for given stake addr {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + let Some(vote_pub_key) = registration.clone().vote_pub_key else { + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Vote pub key does not exist".to_string(), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }; + + return AllRegistration::With(Cip36Registration::Ok(poem_openapi::payload::Json( + Cip36RegistrationList { + slot: registration.clone().slot_no, + voting_key: vec![Cip36RegistrationsForVotingPublicKey { + vote_pub_key, + registrations: vec![registration.clone()], + }], + invalid: invalids_report, + page: None, + }, + ))); + } + + AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Stake hash does not exist", + StatusCode::UNPROCESSABLE_ENTITY, + )]) +} diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs index 3f6919048c4..38d9b01bcfb 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs @@ -1,6 +1,6 @@ //! CIP36 Registration Endpoints -use poem::http::{HeaderMap, StatusCode}; +use poem::http::HeaderMap; use poem_openapi::{param::Query, OpenApi}; use self::cardano::slot_no::SlotNo; @@ -57,16 +57,16 @@ impl Api { // Special validation for the `lookup` parameter. // If the parameter is ALL, BUT we do not have a valid API Key, just report the parameter // is invalid. - if let Some(lookup) = lookup.0.clone() { - if lookup.is_all(headers).is_err() { - return response::AllRegistration::unprocessable_content(vec![ - poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - ), - ]); - } - } + // if let Some(lookup) = lookup.0.clone() { + // if lookup.is_all(headers).is_err() { + // return response::AllRegistration::unprocessable_content(vec![ + // poem::Error::from_string( + // "Invalid Stake Address or Voter Key", + // StatusCode::UNPROCESSABLE_ENTITY, + // ), + // ]); + // } + // } endpoint::cip36_registrations( lookup.0, diff --git a/catalyst-gateway/bin/src/service/common/auth/mod.rs b/catalyst-gateway/bin/src/service/common/auth/mod.rs index a45315bf836..b3b36b681c8 100644 --- a/catalyst-gateway/bin/src/service/common/auth/mod.rs +++ b/catalyst-gateway/bin/src/service/common/auth/mod.rs @@ -1,5 +1,6 @@ //! Catalyst RBAC Token Authentication +#[allow(dead_code)] pub(crate) mod api_key; pub(crate) mod none; pub(crate) mod none_or_rbac; diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/query/mod.rs b/catalyst-gateway/bin/src/service/common/types/cardano/query/mod.rs index 2583fded07c..ee7e88ee2aa 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/query/mod.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/query/mod.rs @@ -4,6 +4,7 @@ //! `OpenAPI` pub(crate) mod as_at; +#[allow(dead_code)] pub(crate) mod stake_or_voter; pub(crate) use as_at::AsAt; diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs index 63e2aaf7a84..2ed09f7bf80 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs @@ -18,7 +18,14 @@ use poem_openapi::{ use regex::Regex; use serde_json::Value; -use crate::service::common::{self, auth::api_key::check_api_key}; +use crate::service::common::{ + self, + auth::api_key::check_api_key, + types::{ + cardano::cip19_stake_address::Cip19StakeAddress, + generic::ed25519_public_key::Ed25519HexEncodedPublicKey, + }, +}; /// A Query Parameter that can take a CIP-19 stake address, or a public key. /// Defining these are mutually exclusive, ao a single parameter is required to be used. @@ -52,15 +59,13 @@ impl TryFrom<&str> for StakeAddressOrPublicKey { return Ok(Self::All); } - // Otherwise, work out use the regex to work out what it is, and validate it. - if let Some(results) = RE.captures(value) { - if let Some(stake_addr) = results.get(1) { - return Ok(Self::Address(stake_addr.as_str().try_into()?)); - } else if let Some(public_key) = results.get(2) { - return Ok(Self::PublicKey(public_key.as_str().try_into()?)); - } + if let Ok(pub_key) = Ed25519HexEncodedPublicKey::try_from(value) { + Ok(Self::PublicKey(pub_key)) + } else if let Ok(stake_addr) = Cip19StakeAddress::try_from(value) { + Ok(Self::Address(stake_addr)) + } else { + bail!("Not a valid \"Stake or Public Key\" parameter."); } - bail!("Not a valid \"Stake or Public Key\" parameter."); } } @@ -198,3 +203,21 @@ impl TryInto for ed25519_dalek::VerifyingKey { .expect("This can only fail if the type was invalidly constructed.") } } + +#[cfg(test)] +mod tests { + use super::Ed25519HexEncodedPublicKey; + + #[test] + fn hex_to_pub_key() { + assert!(Ed25519HexEncodedPublicKey::try_from( + "0x76e7ac0e460b6cdecea4be70479dab13c4adbd117421259a9b36caac007394de".to_string(), + ) + .is_ok()); + } +} From 17d074cecaed5a110257eed21f724c62f7f2149a Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 6 Jan 2025 10:49:29 +0000 Subject: [PATCH 04/46] refactor(fix types): incoming stake pub key conversion --- .../src/service/api/cardano/cip36/filter.rs | 19 +++++++++---------- .../types/generic/ed25519_public_key.rs | 8 ++++++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 2f68ca56548..fd2e0d18097 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -4,7 +4,7 @@ use std::{cmp::Reverse, sync::Arc}; use futures::StreamExt; use poem::http::StatusCode; -use tracing::{error, info}; +use tracing::error; use super::{ cardano::{ @@ -20,7 +20,7 @@ use super::{ }; use crate::db::index::{ queries::registrations::{ - get_from_stake_addr::GetRegistrationQuery, + get_from_stake_addr::{GetRegistrationParams, GetRegistrationQuery}, get_from_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, get_invalid::{GetInvalidRegistrationParams, GetInvalidRegistrationQuery}, }, @@ -33,7 +33,7 @@ pub async fn get_registration_from_stake_addr( ) -> AllRegistration { let registrations = match get_all_registrations_from_stake_pub_key( session.clone(), - stake_pub_key.as_bytes().to_vec(), + stake_pub_key.clone(), ) .await { @@ -69,8 +69,6 @@ pub async fn get_registration_from_stake_addr( }, } } else { - info!("conor {:?}", hex::encode(stake_pub_key.as_bytes())); - match sort_latest_registration(registrations) { Ok(registration) => registration, Err(err) => { @@ -145,17 +143,18 @@ async fn latest_registration_from_stake_addr( stake_pub_key: Ed25519HexEncodedPublicKey, session: Arc, ) -> anyhow::Result { sort_latest_registration( - get_all_registrations_from_stake_pub_key(session, stake_pub_key.as_bytes().to_vec()) - .await?, + get_all_registrations_from_stake_pub_key(session, stake_pub_key).await?, ) } /// Get all cip36 registrations for a given stake address. async fn get_all_registrations_from_stake_pub_key( - session: Arc, stake_pub_key: Vec, + session: Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = - GetRegistrationQuery::execute(&session, stake_pub_key.into()).await?; + let mut registrations_iter = GetRegistrationQuery::execute(&session, GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }) + .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { let row = row?; diff --git a/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs b/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs index 96f7d604311..d3bcf7d2b98 100644 --- a/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs +++ b/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs @@ -134,6 +134,14 @@ impl From for ed25519_dalek::VerifyingKey { } } +impl TryInto> for Ed25519HexEncodedPublicKey { + type Error = anyhow::Error; + + fn try_into(self) -> Result, Self::Error> { + Ok(hex::decode(self.0.trim_start_matches("0x"))?) + } +} + #[cfg(test)] mod tests { use super::Ed25519HexEncodedPublicKey; From f1cb7e548bbd3b8d9582ab9f724e6796550975d2 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 6 Jan 2025 11:11:18 +0000 Subject: [PATCH 05/46] refactor(invalid registrations): fix --- .../src/service/api/cardano/cip36/endpoint.rs | 4 +++- .../src/service/api/cardano/cip36/filter.rs | 21 +++++++++++-------- .../types/cardano/query/stake_or_voter.rs | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index 22e2a33c3c5..cc69185d0f8 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -35,7 +35,9 @@ pub(crate) async fn cip36_registrations( if let Some(stake_or_voter) = lookup { match StakeAddressOrPublicKey::from(stake_or_voter) { StakeAddressOrPublicKey::Address(cip19_stake_address) => { - // Convert stake address into stake key hash + // Typically, a stake address will start with 'stake1', + // for example: stake1ux7k5ztvhwj7ykv5v7vwjjzdfckjk0v74z9p9m5w0t5534clf62eq + // We need to convert this to a stake hash as per our data model. let stake_hash: HexEncodedHash28 = match cip19_stake_address.try_into() { Ok(stake_hash) => stake_hash, Err(err) => { diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index fd2e0d18097..cee481ab22d 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -103,12 +103,8 @@ pub async fn get_registration_from_stake_addr( // include any erroneous registrations which occur AFTER the slot# of the last valid // registration - let invalids_report = match get_invalid_registrations( - stake_pub_key.as_bytes(), - slot_no.clone(), - session, - ) - .await + let invalids_report = match get_invalid_registrations(stake_pub_key, slot_no.clone(), session) + .await { Ok(invalids) => invalids, Err(err) => { @@ -208,11 +204,11 @@ fn get_registration_given_slot_no( /// Get invalid registrations for stake addr after given slot no async fn get_invalid_registrations( - stake_pub_key: &[u8], slot_no: SlotNo, session: Arc, + stake_pub_key: Ed25519HexEncodedPublicKey, slot_no: SlotNo, session: Arc, ) -> anyhow::Result> { let mut invalid_registrations_iter = GetInvalidRegistrationQuery::execute( &session, - GetInvalidRegistrationParams::new(stake_pub_key.to_owned(), slot_no.clone()), + GetInvalidRegistrationParams::new(stake_pub_key.try_into()?, slot_no.clone()), ) .await?; let mut invalid_registrations = Vec::new(); @@ -333,10 +329,17 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( }, }; + let Ok(stake_pub_key) = Ed25519HexEncodedPublicKey::try_from(row.stake_address) else { + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Failed to convert raw stake pub key to pub key type".to_string(), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }; + // include any erroneous registrations which occur AFTER the slot# of the last valid // registration let invalids_report = match get_invalid_registrations( - &row.stake_address, + stake_pub_key, registration.slot_no.clone(), session, ) diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs index 2ed09f7bf80..e2537d2d798 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs @@ -28,7 +28,7 @@ use crate::service::common::{ }; /// A Query Parameter that can take a CIP-19 stake address, or a public key. -/// Defining these are mutually exclusive, ao a single parameter is required to be used. +/// Defining these are mutually exclusive, as a single parameter is required to be used. #[derive(Clone)] pub(crate) enum StakeAddressOrPublicKey { /// A CIP-19 stake address From a969fe08871bdc1fe17b823d116a37fd66b3920f Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 6 Jan 2025 12:17:52 +0000 Subject: [PATCH 06/46] refactor(fix as at time from query string parameter ): asat --- .../src/service/api/cardano/cip36/endpoint.rs | 7 ++++--- .../service/common/types/cardano/query/as_at.rs | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index cc69185d0f8..45c30b31035 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -36,14 +36,15 @@ pub(crate) async fn cip36_registrations( match StakeAddressOrPublicKey::from(stake_or_voter) { StakeAddressOrPublicKey::Address(cip19_stake_address) => { // Typically, a stake address will start with 'stake1', - // for example: stake1ux7k5ztvhwj7ykv5v7vwjjzdfckjk0v74z9p9m5w0t5534clf62eq - // We need to convert this to a stake hash as per our data model. + // for example: stake1ux7k5ztvhwj7ykv5v7vwjjzdfckjk0v74z9p9m5w0t5534clf62eq, + // We need to convert this to a stake hash as per our data model to then find the, + // Full Stake Public Key (32 byte Ed25519 Public key, not hashed) let stake_hash: HexEncodedHash28 = match cip19_stake_address.try_into() { Ok(stake_hash) => stake_hash, Err(err) => { return response::AllRegistration::unprocessable_content(vec![ poem::Error::from_string( - format!("Stake addr to stake hash {err:?}"), + format!("Stake addr to stake hash conversion error: {err:?}"), StatusCode::UNPROCESSABLE_ENTITY, ), ]); diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/query/as_at.rs b/catalyst-gateway/bin/src/service/common/types/cardano/query/as_at.rs index f95fbb607f6..25645b57b30 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/query/as_at.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/query/as_at.rs @@ -1,4 +1,4 @@ -//! Query Parameter that can take either a Blockchain slot Number of Unix Epoch timestamp. +//! Query Parameter that can take either a Blockchain slot Number or Unix Epoch timestamp. //! //! Allows better specifying of times that restrict a GET endpoints response. @@ -167,3 +167,17 @@ impl Display for AsAt { write!(f, "{}:{}", self.0 .0, self.0 .1) } } + +#[cfg(test)] +mod tests { + use super::parse_parameter; + + #[test] + fn test_string_to_slot_no() { + let slot_no = "SLOT:12396302"; + assert!(parse_parameter(slot_no).is_ok()); + + let unix_timestamp = "TIME:1736164751"; + assert!(parse_parameter(unix_timestamp).is_ok()); + } +} From 8c25f7c3a2c9c872e65c9006d670cba6283eabf5 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 6 Jan 2025 13:45:27 +0000 Subject: [PATCH 07/46] refactor(voting key): cross reference activity with stake addr 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. --- .../src/service/api/cardano/cip36/endpoint.rs | 8 +- .../src/service/api/cardano/cip36/filter.rs | 231 +++++++++--------- .../bin/src/service/api/cardano/cip36/mod.rs | 2 +- 3 files changed, 121 insertions(+), 120 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index 45c30b31035..fa5a37e35e6 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -6,7 +6,7 @@ use tracing::{error, info}; use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddressOrPublicKey}; use super::{ cardano::{self}, - filter::{get_latest_registration_from_stake_key_hash, get_registration_from_stake_addr}, + filter::{get_registration_given_stake_key_hash, get_registration_given_vote_key}, response, NoneOrRBAC, SlotNo, }; use crate::{ @@ -51,13 +51,13 @@ pub(crate) async fn cip36_registrations( }, }; - return get_latest_registration_from_stake_key_hash(stake_hash, session).await; + return get_registration_given_stake_key_hash(stake_hash, session, asat).await; }, StakeAddressOrPublicKey::PublicKey(ed25519_hex_encoded_public_key) => { - return get_registration_from_stake_addr( + return get_registration_given_vote_key( ed25519_hex_encoded_public_key, - asat, session, + asat, ) .await; }, diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index cee481ab22d..47e300ccb09 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -22,16 +22,82 @@ use crate::db::index::{ queries::registrations::{ get_from_stake_addr::{GetRegistrationParams, GetRegistrationQuery}, get_from_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, + get_from_vote_key::{GetStakeAddrFromVoteKeyParams, GetStakeAddrFromVoteKeyQuery}, get_invalid::{GetInvalidRegistrationParams, GetInvalidRegistrationQuery}, }, session::CassandraSession, }; +/// Get registration given a stake key hash, time specific based on asat param, +/// or latest registration returned if no asat given. +pub(crate) async fn get_registration_given_stake_key_hash( + stake_hash: HexEncodedHash28, session: Arc, asat: Option, +) -> AllRegistration { + // Get stake addr associated with given stake hash. + let mut stake_addr_iter = match GetStakeAddrQuery::execute( + &session, + GetStakeAddrParams::new(stake_hash.into()), + ) + .await + { + Ok(stake_addr) => stake_addr, + Err(err) => { + error!( + id="get_latest_registration_from_stake_key_hash_query_stake_addr", + error=?err, + "Failed to query stake addr from stake hash" + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to query stake addr from stake hash {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + if let Some(row_stake_addr) = stake_addr_iter.next().await { + let row = match row_stake_addr { + Ok(r) => r, + Err(err) => { + error!( + id="get_latest_registration_from_stake_key_hash_latest_registration", + error=?err, + "Failed to get latest registration" + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to get latest registration {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + // Stake hash successfully converted into associated stake pub key which we use to lookup + // registrations. + let stake_pub_key = match Ed25519HexEncodedPublicKey::try_from(row.stake_address.clone()) { + Ok(key) => key, + Err(err) => { + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to type stake address {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + return get_registration_from_stake_addr(stake_pub_key, asat, session, None).await; + } + AllRegistration::unprocessable_content(vec![poem::Error::from_string( + "Stake hash does not exist", + StatusCode::UNPROCESSABLE_ENTITY, + )]) +} + /// Get registration from stake addr pub async fn get_registration_from_stake_addr( - stake_pub_key: Ed25519HexEncodedPublicKey, asat: Option, session: Arc, + stake_pub_key: Ed25519HexEncodedPublicKey, asat: Option, + session: Arc, vote_key: Option, ) -> AllRegistration { - let registrations = match get_all_registrations_from_stake_pub_key( + let mut registrations = match get_all_registrations_from_stake_pub_key( session.clone(), stake_pub_key.clone(), ) @@ -52,6 +118,12 @@ pub async fn get_registration_from_stake_addr( }, }; + // check registrations (stake addrs) are still actively associated with the voting key, + // and have not been registered to another voting key. + if let Some(vote_key) = vote_key { + registrations = check_stake_addr_voting_key_association(registrations, &vote_key); + } + let registration = if let Some(slot_no) = asat { match get_registration_given_slot_no(registrations, &slot_no) { Ok(registration) => registration, @@ -134,15 +206,6 @@ pub async fn get_registration_from_stake_addr( ))) } -/// Get registration given stake address, latest or based on optional slot no -async fn latest_registration_from_stake_addr( - stake_pub_key: Ed25519HexEncodedPublicKey, session: Arc, -) -> anyhow::Result { - sort_latest_registration( - get_all_registrations_from_stake_pub_key(session, stake_pub_key).await?, - ) -} - /// Get all cip36 registrations for a given stake address. async fn get_all_registrations_from_stake_pub_key( session: Arc, stake_pub_key: Ed25519HexEncodedPublicKey, @@ -235,50 +298,37 @@ async fn get_invalid_registrations( 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: &str, -) -> anyhow::Result> { - let Ok(associated_key) = Ed25519HexEncodedPublicKey::try_from(associated_voting_key) else { - return Err(anyhow::anyhow!( - "Can't sort latest registrations by slot no" - )); +/// Get registration given a stake key hash, time specific based on asat param, +/// or latest registration returned if no asat given. +pub(crate) async fn get_registration_given_vote_key( + vote_key: Ed25519HexEncodedPublicKey, session: Arc, asat: Option, +) -> AllRegistration { + let vote_key: Vec = match vote_key.try_into() { + Ok(vote_key) => vote_key, + Err(err) => { + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to convert vote key to bytes {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, }; - Ok(registrations - .into_iter() - .filter(|r| { - r.vote_pub_key - .clone() - .unwrap_or(Ed25519HexEncodedPublicKey::examples(0)) - == associated_key - }) - .collect()) -} - -/// Get latest registration given a stake key hash. -#[allow(clippy::too_many_lines)] -pub(crate) async fn get_latest_registration_from_stake_key_hash( - stake_hash: HexEncodedHash28, session: Arc, -) -> AllRegistration { - // Get stake addr associated with give stake hash - let mut stake_addr_iter = match GetStakeAddrQuery::execute( + // Get stake addr associated voting key. + let mut stake_addr_iter = match GetStakeAddrFromVoteKeyQuery::execute( &session, - GetStakeAddrParams::new(stake_hash.into()), + GetStakeAddrFromVoteKeyParams::new(vote_key), ) .await { - Ok(latest) => latest, + Ok(stake_addr) => stake_addr, Err(err) => { error!( - id="get_latest_registration_from_stake_key_hash_query_stake_addr", + id="get_associated_vote_key_registrations_query_stake_addr_from_vote_key", error=?err, - "Failed to query stake addr from stake hash" + "Failed to query stake addr from vote key" ); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query stake addr from stake hash {err:?}"), + format!("Failed to query stake addr from vote key {err:?}"), StatusCode::UNPROCESSABLE_ENTITY, )]); }, @@ -289,18 +339,19 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( Ok(r) => r, Err(err) => { error!( - id="get_latest_registration_from_stake_key_hash_latest_registration", + id="get_associated_vote_key_registrations_latest_registration", error=?err, "Failed to get latest registration" ); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to get latest registration {err:?}"), + format!("Failed to query stake addr from vote key {err:?}"), StatusCode::UNPROCESSABLE_ENTITY, )]); }, }; + // Stake hash successfully converted into associated stake pub key which we use to lookup + // registrations. let stake_pub_key = match Ed25519HexEncodedPublicKey::try_from(row.stake_address.clone()) { Ok(key) => key, Err(err) => { @@ -311,77 +362,27 @@ pub(crate) async fn get_latest_registration_from_stake_key_hash( }, }; - let registration = match latest_registration_from_stake_addr(stake_pub_key, session.clone()) - .await - { - Ok(registration) => registration, - Err(err) => { - error!( - id="get_latest_registration_from_stake_key_hash_registration_for_stake_addr", - error=?err, - "Failed to obtain registration for given stake addr", - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to obtain registration for given stake addr {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }, - }; - - let Ok(stake_pub_key) = Ed25519HexEncodedPublicKey::try_from(row.stake_address) else { - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Failed to convert raw stake pub key to pub key type".to_string(), - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }; - - // include any erroneous registrations which occur AFTER the slot# of the last valid - // registration - let invalids_report = match get_invalid_registrations( - stake_pub_key, - registration.slot_no.clone(), - session, - ) - .await - { - Ok(invalids) => invalids, - Err(err) => { - error!( - id="get_latest_registration_from_stake_key_hash_invalid_registrations_for_stake_addr", - error=?err, - "Failed to obtain invalid registrations for given stake addr", - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to obtain invalid registrations for given stake addr {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }, - }; - - let Some(vote_pub_key) = registration.clone().vote_pub_key else { - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Vote pub key does not exist".to_string(), - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }; - - return AllRegistration::With(Cip36Registration::Ok(poem_openapi::payload::Json( - Cip36RegistrationList { - slot: registration.clone().slot_no, - voting_key: vec![Cip36RegistrationsForVotingPublicKey { - vote_pub_key, - registrations: vec![registration.clone()], - }], - invalid: invalids_report, - page: None, - }, - ))); + return get_registration_from_stake_addr(stake_pub_key, asat, session, None).await; } AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Stake hash does not exist", + "Vote key does not exist", StatusCode::UNPROCESSABLE_ENTITY, )]) } + +/// 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: &Ed25519HexEncodedPublicKey, +) -> Vec { + registrations + .into_iter() + .filter(|r| { + r.vote_pub_key + .clone() + .unwrap_or(Ed25519HexEncodedPublicKey::examples(0)) + == *associated_voting_key + }) + .collect() +} diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs index 38d9b01bcfb..3e42072f56b 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs @@ -13,7 +13,7 @@ use crate::service::common::{ }; pub(crate) mod endpoint; -#[allow(dead_code)] + pub(crate) mod filter; pub(crate) mod response; From 648893e7e835aee466e1ea52854b02a0b6e96169 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 6 Jan 2025 13:49:09 +0000 Subject: [PATCH 08/46] refactor(voting key): cross reference activity with stake addr 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/endpoint.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index fa5a37e35e6..ccc356d3010 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -39,6 +39,8 @@ pub(crate) async fn cip36_registrations( // for example: stake1ux7k5ztvhwj7ykv5v7vwjjzdfckjk0v74z9p9m5w0t5534clf62eq, // We need to convert this to a stake hash as per our data model to then find the, // Full Stake Public Key (32 byte Ed25519 Public key, not hashed) + // Get latest registration given address or Restrict the query to a + // time; which can be represented as either the blockchains slot number. let stake_hash: HexEncodedHash28 = match cip19_stake_address.try_into() { Ok(stake_hash) => stake_hash, Err(err) => { @@ -54,6 +56,8 @@ pub(crate) async fn cip36_registrations( return get_registration_given_stake_key_hash(stake_hash, session, asat).await; }, StakeAddressOrPublicKey::PublicKey(ed25519_hex_encoded_public_key) => { + // Get latest registration given voting key or Restrict the query to a + // time; which can be represented as either the blockchains slot number. return get_registration_given_vote_key( ed25519_hex_encoded_public_key, session, From 3cdf4226af77d1bcc6118f69d4b4fb0b94e703b7 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 6 Jan 2025 14:33:46 +0000 Subject: [PATCH 09/46] refactor(slot no to big int): into --- .../src/db/index/queries/registrations/get_invalid.rs | 2 +- .../bin/src/service/api/cardano/cip36/filter.rs | 2 +- .../bin/src/service/api/cardano/cip36/response.rs | 1 - .../bin/src/service/common/types/cardano/slot_no.rs | 11 ++++++----- 4 files changed, 8 insertions(+), 8 deletions(-) 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 b3ab545446b..130f6bcb2d8 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 @@ -34,7 +34,7 @@ impl GetInvalidRegistrationParams { pub(crate) fn new(stake_address: Vec, slot_no: SlotNo) -> GetInvalidRegistrationParams { Self { stake_address, - slot_no: slot_no.into_big_int(), + slot_no: slot_no.into(), } } } diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 47e300ccb09..a11b2d7b40c 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -247,7 +247,7 @@ async fn get_all_registrations_from_stake_pub_key( Ok(registrations) } -/// Sort latest registrations for a given stake address sorting by slot no +/// Sort latest registrations for a given stake address, sort by slot no. fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { registrations.sort_by_key(|registration| Reverse(registration.slot_no.clone())); registrations.into_iter().next().ok_or(anyhow::anyhow!( diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs index 7fae26b687a..67b8e5b3082 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/response.rs @@ -8,7 +8,6 @@ use crate::service::common; /// Endpoint responses. #[derive(ApiResponse)] -#[allow(dead_code)] // TODO: Remove once endpoint fully implemented pub(crate) enum Cip36Registration { /// All CIP36 registrations associated with the same Voting Key. #[oai(status = 200)] diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/slot_no.rs b/catalyst-gateway/bin/src/service/common/types/cardano/slot_no.rs index 59c70c0e171..be35e7f4eca 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/slot_no.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/slot_no.rs @@ -95,6 +95,12 @@ impl ParseFromJSON for SlotNo { } } +impl From for BigInt { + fn from(val: SlotNo) -> Self { + BigInt::from(val.0) + } +} + impl ToJSON for SlotNo { fn to_json(&self) -> Option { Some(self.0.into()) @@ -124,11 +130,6 @@ impl SlotNo { pub(crate) fn into_option>(value: Option) -> Option { value.map(std::convert::Into::into) } - - /// big int - pub(crate) fn into_big_int(self) -> BigInt { - BigInt::from(self.0) - } } impl Example for SlotNo { From 48962505c41ae1b4c2d35955971a8da3240a5f3e Mon Sep 17 00:00:00 2001 From: cong-or Date: Tue, 7 Jan 2025 14:08:36 +0000 Subject: [PATCH 10/46] refactor(snapshot): auth --- .../src/db/index/queries/registrations/mod.rs | 1 - .../src/service/api/cardano/cip36/endpoint.rs | 9 ++++---- .../src/service/api/cardano/cip36/filter.rs | 17 +++++++++----- .../bin/src/service/api/cardano/cip36/mod.rs | 22 +++++++++---------- .../src/service/common/auth/rbac/scheme.rs | 8 +++++-- catalyst-gateway/bin/src/settings/mod.rs | 3 +++ 6 files changed, 36 insertions(+), 24 deletions(-) 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 8518dd16584..b0f2edfa4e4 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs @@ -1,6 +1,5 @@ //! Registration related queries. pub(crate) mod get_from_stake_addr; pub(crate) mod get_from_stake_hash; -#[allow(dead_code)] pub(crate) mod get_from_vote_key; pub(crate) mod get_invalid; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index ccc356d3010..830e706d50e 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -1,7 +1,7 @@ //! Implementation of the GET `/cardano/cip36` endpoint use poem::http::{HeaderMap, StatusCode}; -use tracing::{error, info}; +use tracing::error; use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddressOrPublicKey}; use super::{ @@ -40,7 +40,8 @@ pub(crate) async fn cip36_registrations( // We need to convert this to a stake hash as per our data model to then find the, // Full Stake Public Key (32 byte Ed25519 Public key, not hashed) // Get latest registration given address or Restrict the query to a - // time; which can be represented as either the blockchains slot number. + // time; which can be represented as either the blockchains slot number or unix + // timestamp. let stake_hash: HexEncodedHash28 = match cip19_stake_address.try_into() { Ok(stake_hash) => stake_hash, Err(err) => { @@ -57,7 +58,8 @@ pub(crate) async fn cip36_registrations( }, StakeAddressOrPublicKey::PublicKey(ed25519_hex_encoded_public_key) => { // Get latest registration given voting key or Restrict the query to a - // time; which can be represented as either the blockchains slot number. + // time; which can be represented as either the blockchains slot number or unix time + // ts. return get_registration_given_vote_key( ed25519_hex_encoded_public_key, session, @@ -66,7 +68,6 @@ pub(crate) async fn cip36_registrations( .await; }, StakeAddressOrPublicKey::All => { - info!("ALL"); return response::AllRegistration::unprocessable_content(vec![ poem::Error::from_string( "Invalid Stake Address or Voter Key", diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index a11b2d7b40c..316c9e88dcc 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -378,11 +378,16 @@ fn check_stake_addr_voting_key_association( ) -> Vec { registrations .into_iter() - .filter(|r| { - r.vote_pub_key - .clone() - .unwrap_or(Ed25519HexEncodedPublicKey::examples(0)) - == *associated_voting_key - }) + .filter(|registration| cross_reference_key(associated_voting_key, registration)) .collect() } + +/// Check associated voting key matches registration voting key +fn cross_reference_key( + associated_voting_key: &Ed25519HexEncodedPublicKey, r: &Cip36Details, +) -> bool { + match r.vote_pub_key.clone() { + Some(key) => key == *associated_voting_key, + None => false, + } +} diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs index 3e42072f56b..6bd32eded28 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs @@ -1,6 +1,6 @@ //! CIP36 Registration Endpoints -use poem::http::HeaderMap; +use poem::http::{HeaderMap, StatusCode}; use poem_openapi::{param::Query, OpenApi}; use self::cardano::slot_no::SlotNo; @@ -57,16 +57,16 @@ impl Api { // Special validation for the `lookup` parameter. // If the parameter is ALL, BUT we do not have a valid API Key, just report the parameter // is invalid. - // if let Some(lookup) = lookup.0.clone() { - // if lookup.is_all(headers).is_err() { - // return response::AllRegistration::unprocessable_content(vec![ - // poem::Error::from_string( - // "Invalid Stake Address or Voter Key", - // StatusCode::UNPROCESSABLE_ENTITY, - // ), - // ]); - // } - // } + if let Some(lookup) = lookup.0.clone() { + if lookup.is_all(headers).is_err() { + return response::AllRegistration::unprocessable_content(vec![ + poem::Error::from_string( + "Invalid Stake Address or Voter Keyz", + StatusCode::UNPROCESSABLE_ENTITY, + ), + ]); + } + } endpoint::cip36_registrations( lookup.0, diff --git a/catalyst-gateway/bin/src/service/common/auth/rbac/scheme.rs b/catalyst-gateway/bin/src/service/common/auth/rbac/scheme.rs index 0489a5b9721..3f452d92c04 100644 --- a/catalyst-gateway/bin/src/service/common/auth/rbac/scheme.rs +++ b/catalyst-gateway/bin/src/service/common/auth/rbac/scheme.rs @@ -66,7 +66,9 @@ impl ResponseError for AuthTokenError { /// Convert this error to a HTTP response. fn as_response(&self) -> poem::Response - where Self: Error + Send + Sync + 'static { + where + Self: Error + Send + Sync + 'static, + { ErrorResponses::unauthorized().into_response() } } @@ -85,7 +87,9 @@ impl ResponseError for AuthTokenAccessViolation { /// Convert this error to a HTTP response. fn as_response(&self) -> poem::Response - where Self: Error + Send + Sync + 'static { + where + Self: Error + Send + Sync + 'static, + { // TODO: Actually check permissions needed for an endpoint. ErrorResponses::forbidden(Some(self.0.clone())).into_response() } diff --git a/catalyst-gateway/bin/src/settings/mod.rs b/catalyst-gateway/bin/src/settings/mod.rs index 6be03945050..23b179d79dd 100644 --- a/catalyst-gateway/bin/src/settings/mod.rs +++ b/catalyst-gateway/bin/src/settings/mod.rs @@ -1,5 +1,6 @@ //! Command line and environment variable settings for the service use std::{ + env, net::{IpAddr, Ipv4Addr, SocketAddr}, path::PathBuf, str::FromStr, @@ -170,6 +171,8 @@ static ENV_VARS: LazyLock = LazyLock::new(|| { }, }; + env::set_var("INTERNAL_API_KEY", "catv1.UP6jrII9HzfRdP9CbJGBMa1QAZRA9fjV8GH-0BQZ0H4GxVhAjPgtsUqyvIbNEMX24Mhgyz4miy7J5q84lxE8AJ2kzfRPw9Cz9tWUTHe1ebNw-rYjBbxspBr2ajr__7dood7pDw"); + EnvVars { github_repo_owner: StringEnvVar::new("GITHUB_REPO_OWNER", GITHUB_REPO_OWNER_DEFAULT.into()), github_repo_name: StringEnvVar::new("GITHUB_REPO_NAME", GITHUB_REPO_NAME_DEFAULT.into()), From a65bb813660d292c47dbf1478e0e8983cc64c6c4 Mon Sep 17 00:00:00 2001 From: cong-or Date: Tue, 7 Jan 2025 14:08:43 +0000 Subject: [PATCH 11/46] refactor(snapshot): auth --- .../bin/src/service/common/auth/rbac/scheme.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/catalyst-gateway/bin/src/service/common/auth/rbac/scheme.rs b/catalyst-gateway/bin/src/service/common/auth/rbac/scheme.rs index 3f452d92c04..0489a5b9721 100644 --- a/catalyst-gateway/bin/src/service/common/auth/rbac/scheme.rs +++ b/catalyst-gateway/bin/src/service/common/auth/rbac/scheme.rs @@ -66,9 +66,7 @@ impl ResponseError for AuthTokenError { /// Convert this error to a HTTP response. fn as_response(&self) -> poem::Response - where - Self: Error + Send + Sync + 'static, - { + where Self: Error + Send + Sync + 'static { ErrorResponses::unauthorized().into_response() } } @@ -87,9 +85,7 @@ impl ResponseError for AuthTokenAccessViolation { /// Convert this error to a HTTP response. fn as_response(&self) -> poem::Response - where - Self: Error + Send + Sync + 'static, - { + where Self: Error + Send + Sync + 'static { // TODO: Actually check permissions needed for an endpoint. ErrorResponses::forbidden(Some(self.0.clone())).into_response() } From 14791fc77c06ddf147c76a0547b75d97a3fcd843 Mon Sep 17 00:00:00 2001 From: cong-or Date: Tue, 7 Jan 2025 22:18:15 +0000 Subject: [PATCH 12/46] refactor(query all): snapshot --- .../index/queries/cql/get_all_stake_addrs.cql | 2 + .../cql/get_registations_w_vote_key.cql | 10 + .../bin/src/db/index/queries/mod.rs | 14 +- .../registrations/get_all_with_limit.rs | 61 ++++++ .../src/db/index/queries/registrations/mod.rs | 1 + .../src/service/api/cardano/cip36/endpoint.rs | 24 +- .../src/service/api/cardano/cip36/filter.rs | 206 ++++++++++++++++-- 7 files changed, 280 insertions(+), 38 deletions(-) create mode 100644 catalyst-gateway/bin/src/db/index/queries/cql/get_all_stake_addrs.cql create mode 100644 catalyst-gateway/bin/src/db/index/queries/cql/get_registations_w_vote_key.cql create mode 100644 catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_all_stake_addrs.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_all_stake_addrs.cql new file mode 100644 index 00000000000..9930ae7f1c6 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_all_stake_addrs.cql @@ -0,0 +1,2 @@ +SELECT stake_address,vote_key +FROM cip36_registration; \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_registations_w_vote_key.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_registations_w_vote_key.cql new file mode 100644 index 00000000000..911a80f8bd7 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_registations_w_vote_key.cql @@ -0,0 +1,10 @@ +SELECT stake_address, + nonce, + slot_no, + txn, + vote_key, + payment_address, + is_payable, + cip36 +FROM cip36_registration +WHERE vote_key = :vote_key \ 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 a759fb5a27e..fa19e3587a7 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -16,8 +16,9 @@ use rbac::{ get_role0_chain_root::GetRole0ChainRootQuery, }; use registrations::{ - get_from_stake_addr::GetRegistrationQuery, get_from_stake_hash::GetStakeAddrQuery, - get_from_vote_key::GetStakeAddrFromVoteKeyQuery, get_invalid::GetInvalidRegistrationQuery, + get_all_with_limit::GetAllWithLimitsQuery, 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, @@ -96,6 +97,8 @@ pub(crate) enum PreparedSelectQuery { RegistrationsByChainRoot, /// Get chain root by role0 key ChainRootByRole0Key, + /// Get all with limit + GetAllWithLimit, } /// All prepared UPSERT query statements (inserts/updates a single value of data). @@ -157,6 +160,8 @@ pub(crate) struct PreparedQueries { registrations_by_chain_root_query: PreparedStatement, /// Get chain root by role0 key chain_root_by_role0_key_query: PreparedStatement, + /// Get all with limit + get_all_with_limit_query: PreparedStatement, } /// An individual query response that can fail @@ -193,7 +198,8 @@ impl PreparedQueries { let chain_root_by_stake_address = GetChainRootQuery::prepare(session.clone()).await; let registrations_by_chain_root = GetRegistrationsByChainRootQuery::prepare(session.clone()).await; - let chain_root_by_role0_key = GetRole0ChainRootQuery::prepare(session).await; + let chain_root_by_role0_key = GetRole0ChainRootQuery::prepare(session.clone()).await; + let get_all_with_limit = GetAllWithLimitsQuery::prepare(session).await; let ( txo_insert_queries, @@ -241,6 +247,7 @@ impl PreparedQueries { chain_root_by_stake_address_query: chain_root_by_stake_address?, registrations_by_chain_root_query: registrations_by_chain_root?, chain_root_by_role0_key_query: chain_root_by_role0_key?, + get_all_with_limit_query: get_all_with_limit?, }) } @@ -331,6 +338,7 @@ impl PreparedQueries { &self.registrations_by_chain_root_query }, PreparedSelectQuery::ChainRootByRole0Key => &self.chain_root_by_role0_key_query, + PreparedSelectQuery::GetAllWithLimit => &self.get_all_with_limit_query, }; session diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs new file mode 100644 index 00000000000..c08473a31b5 --- /dev/null +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs @@ -0,0 +1,61 @@ +//! Get stake addr registrations + +use std::sync::Arc; + +use scylla::{ + prepared_statement::PreparedStatement, transport::iterator::TypedRowStream, DeserializeRow, + SerializeRow, Session, +}; +use tracing::error; + +use crate::db::index::{ + queries::{PreparedQueries, PreparedSelectQuery}, + session::CassandraSession, +}; + +/// Get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] +const GET_ALL_WITH_LIMIT: &str = include_str!("../cql/get_all_stake_addrs.cql"); + +/// Get registration +#[derive(SerializeRow)] +pub(crate) struct GetAllWithLimitsParams {} + +/// Get registration query. +#[derive(DeserializeRow)] +pub(crate) struct GetAllWithLimitsQuery { + /// Full Stake Address (not hashed, 32 byte ED25519 Public key). + pub stake_address: Vec, + /// Voting Public Key + pub vote_key: Vec, +} + +impl GetAllWithLimitsQuery { + /// Prepares get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] + pub(crate) async fn prepare(session: Arc) -> anyhow::Result { + let get_registrations_query = PreparedQueries::prepare( + session, + GET_ALL_WITH_LIMIT, + scylla::statement::Consistency::All, + true, + ) + .await; + + if let Err(ref error) = get_registrations_query { + error!(error=%error, "Failed to prepare get all stake addrs"); + }; + + get_registrations_query + } + + /// Executes get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] + pub(crate) async fn execute( + session: &CassandraSession, params: GetAllWithLimitsParams, + ) -> anyhow::Result> { + let iter = session + .execute_iter(PreparedSelectQuery::GetAllWithLimit, params) + .await? + .rows_stream::()?; + + 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 b0f2edfa4e4..ec58759c888 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_all_with_limit; pub(crate) mod get_from_stake_addr; pub(crate) mod get_from_stake_hash; pub(crate) mod get_from_vote_key; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index 830e706d50e..6288beb66ba 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -6,7 +6,7 @@ use tracing::error; use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddressOrPublicKey}; use super::{ cardano::{self}, - filter::{get_registration_given_stake_key_hash, get_registration_given_vote_key}, + filter::{get_all, get_registration_given_stake_key_hash, get_registration_given_vote_key}, response, NoneOrRBAC, SlotNo, }; use crate::{ @@ -68,12 +68,18 @@ pub(crate) async fn cip36_registrations( .await; }, StakeAddressOrPublicKey::All => { - return response::AllRegistration::unprocessable_content(vec![ - poem::Error::from_string( - "Invalid Stake Address or Voter Key", - StatusCode::UNPROCESSABLE_ENTITY, - ), - ]); + match asat { + Some(slot_no) => return get_all(session, &slot_no).await, + None => { + return response::AllRegistration::unprocessable_content(vec![ + poem::Error::from_string( + "Must include the Slot in which Registrations are valid until." + .to_string(), + StatusCode::UNPROCESSABLE_ENTITY, + ), + ]) + }, + }; }, }; }; @@ -82,9 +88,5 @@ pub(crate) async fn cip36_registrations( // parameter. _auth not yet implemented, so put placeholder for that, and return not // found until _auth is implemented. - // return 404 for auth - // distill down to stake addr or list of stake addr - // stake addr hash for cip36 registratioin - response::Cip36Registration::NotFound.into() } diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 316c9e88dcc..273b2eb6565 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -4,6 +4,7 @@ use std::{cmp::Reverse, sync::Arc}; use futures::StreamExt; use poem::http::StatusCode; +use poem_openapi::types::Type; use tracing::error; use super::{ @@ -20,6 +21,7 @@ use super::{ }; use crate::db::index::{ queries::registrations::{ + get_all_with_limit::{GetAllWithLimitsParams, GetAllWithLimitsQuery}, get_from_stake_addr::{GetRegistrationParams, GetRegistrationQuery}, get_from_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, get_from_vote_key::{GetStakeAddrFromVoteKeyParams, GetStakeAddrFromVoteKeyQuery}, @@ -97,26 +99,24 @@ pub async fn get_registration_from_stake_addr( stake_pub_key: Ed25519HexEncodedPublicKey, asat: Option, session: Arc, vote_key: Option, ) -> AllRegistration { - let mut registrations = match get_all_registrations_from_stake_pub_key( - session.clone(), - stake_pub_key.clone(), - ) - .await - { - Ok(registration) => registration, - Err(err) => { - error!( - id="get_registration_from_stake_addr", - error=?err, - "Failed to query stake addr" - ); + let mut registrations = + match get_all_registrations_from_stake_pub_key(&session.clone(), stake_pub_key.clone()) + .await + { + Ok(registration) => registration, + Err(err) => { + error!( + id="get_registration_from_stake_addr", + error=?err, + "Failed to query stake addr" + ); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query stake addr {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }, - }; + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to query stake addr {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; // check registrations (stake addrs) are still actively associated with the voting key, // and have not been registered to another voting key. @@ -208,11 +208,58 @@ pub async fn get_registration_from_stake_addr( /// Get all cip36 registrations for a given stake address. async fn get_all_registrations_from_stake_pub_key( - session: Arc, stake_pub_key: Ed25519HexEncodedPublicKey, + session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute(&session, GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }) + let mut registrations_iter = GetRegistrationQuery::execute( + session, + GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }, + ) + .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 = Cip36Details { + slot_no: SlotNo::from(slot_no), + stake_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.stake_address)?), + vote_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.vote_key)?), + nonce: Some(Nonce::from(nonce)), + txn: Some(TxnIndex::try_from(row.txn)?), + payment_address: Some(Cip19ShelleyAddress::new(hex::encode(row.payment_address))), + is_payable: row.is_payable, + cip15: !row.cip36, + errors: vec![], + }; + + registrations.push(cip36); + } + Ok(registrations) +} + +/// Get all cip36 registrations for a given stake address. +async fn get_all_registrations_from_vote_key( + session: &Arc, vote_key: Ed25519HexEncodedPublicKey, +) -> Result, anyhow::Error> { + let mut registrations_iter = GetRegistrationQuery::execute( + session, + GetRegistrationParams { + stake_address: vote_key.try_into()?, + }, + ) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { @@ -298,7 +345,7 @@ async fn get_invalid_registrations( Ok(invalid_registrations) } -/// Get registration given a stake key hash, time specific based on asat param, +/// Get registration given a vote key, time specific based on asat param, /// or latest registration returned if no asat given. pub(crate) async fn get_registration_given_vote_key( vote_key: Ed25519HexEncodedPublicKey, session: Arc, asat: Option, @@ -391,3 +438,114 @@ fn cross_reference_key( None => false, } } + +/// Get all registrations with given page contraints +pub async fn get_all(session: Arc, slot_no: &SlotNo) -> AllRegistration { + let stake_addrs = match get_all_stake_addrs(&session.clone()).await { + Ok(stake_addrs) => stake_addrs, + Err(err) => { + error!( + id="get_all_with_limit", + error=?err, + "Failed to all" + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to query ALL {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + + let mut all_registrations_after_filtering = Vec::new(); + let mut all_invalids_after_filtering = Vec::new(); + + for (stake_public_key, vote_pub_key) in &stake_addrs { + let registrations_for_given_stake_pub_key = match get_all_registrations_from_stake_pub_key( + &session, + stake_public_key.clone(), + ) + .await + { + Ok(registrations) => registrations, + Err(err) => { + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to query ALL {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]) + }, + }; + + // Any registrations that occurred after this Slot are not included in the list. + let filtered_registrations = slot_filter(registrations_for_given_stake_pub_key, slot_no); + + if filtered_registrations.len().is_empty() { + continue; + } + + all_registrations_after_filtering.push(Cip36RegistrationsForVotingPublicKey { + vote_pub_key: vote_pub_key.clone(), + registrations: filtered_registrations, + }); + + // include any erroneous registrations which occur AFTER the slot# of the last valid + // registration + let invalids_report = match get_invalid_registrations( + stake_public_key.clone(), + slot_no.clone(), + session.clone(), + ) + .await + { + Ok(invalids) => invalids, + Err(err) => { + error!( + id="get_latest_registration_from_stake_key_hash_invalid_registrations_for_stake_addr", + error=?err, + "Failed to obtain invalid registrations for given stake addr", + ); + + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to obtain invalid registrations for given stake addr {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; + all_invalids_after_filtering.push(invalids_report); + } + + AllRegistration::With(Cip36Registration::Ok(poem_openapi::payload::Json( + Cip36RegistrationList { + slot: slot_no.clone(), + voting_key: all_registrations_after_filtering, + invalid: vec![], + page: None, + }, + ))) +} + +/// Get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] +pub async fn get_all_stake_addrs( + session: &Arc, +) -> Result, anyhow::Error> { + let mut stake_addr_iter = + GetAllWithLimitsQuery::execute(session, GetAllWithLimitsParams {}).await?; + let mut vote_key_stake_addr_pair = Vec::new(); + while let Some(row) = stake_addr_iter.next().await { + let row = row?; + + let stake_addr = Ed25519HexEncodedPublicKey::try_from(row.stake_address)?; + let vote_key = Ed25519HexEncodedPublicKey::try_from(row.vote_key)?; + + vote_key_stake_addr_pair.push((stake_addr, vote_key)); + } + Ok(vote_key_stake_addr_pair) +} + +/// Filter out any registrations that occurred after this Slot no +fn slot_filter(registrations: Vec, slot_no: &SlotNo) -> Vec { + registrations + .into_iter() + .filter(|registration| registration.slot_no < *slot_no) + .collect() +} From 0252f8c577790728706425d912298b0e0d2b8743 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 09:26:07 +0000 Subject: [PATCH 13/46] refactor(query all): snapshot --- .../src/service/api/cardano/cip36/filter.rs | 92 +++++-------------- 1 file changed, 25 insertions(+), 67 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 273b2eb6565..fd778638006 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -210,56 +210,9 @@ pub async fn get_registration_from_stake_addr( async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute( - session, - GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }, - ) - .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 = Cip36Details { - slot_no: SlotNo::from(slot_no), - stake_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.stake_address)?), - vote_pub_key: Some(Ed25519HexEncodedPublicKey::try_from(row.vote_key)?), - nonce: Some(Nonce::from(nonce)), - txn: Some(TxnIndex::try_from(row.txn)?), - payment_address: Some(Cip19ShelleyAddress::new(hex::encode(row.payment_address))), - is_payable: row.is_payable, - cip15: !row.cip36, - errors: vec![], - }; - - registrations.push(cip36); - } - Ok(registrations) -} - -/// Get all cip36 registrations for a given stake address. -async fn get_all_registrations_from_vote_key( - session: &Arc, vote_key: Ed25519HexEncodedPublicKey, -) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute( - session, - GetRegistrationParams { - stake_address: vote_key.try_into()?, - }, - ) + let mut registrations_iter = GetRegistrationQuery::execute(session, GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { @@ -350,7 +303,7 @@ async fn get_invalid_registrations( pub(crate) async fn get_registration_given_vote_key( vote_key: Ed25519HexEncodedPublicKey, session: Arc, asat: Option, ) -> AllRegistration { - let vote_key: Vec = match vote_key.try_into() { + let voting_key: Vec = match vote_key.clone().try_into() { Ok(vote_key) => vote_key, Err(err) => { return AllRegistration::unprocessable_content(vec![poem::Error::from_string( @@ -363,7 +316,7 @@ pub(crate) async fn get_registration_given_vote_key( // Get stake addr associated voting key. let mut stake_addr_iter = match GetStakeAddrFromVoteKeyQuery::execute( &session, - GetStakeAddrFromVoteKeyParams::new(vote_key), + GetStakeAddrFromVoteKeyParams::new(voting_key), ) .await { @@ -409,7 +362,8 @@ pub(crate) async fn get_registration_given_vote_key( }, }; - return get_registration_from_stake_addr(stake_pub_key, asat, session, None).await; + return get_registration_from_stake_addr(stake_pub_key, asat, session, Some(vote_key)) + .await; } AllRegistration::unprocessable_content(vec![poem::Error::from_string( @@ -461,20 +415,24 @@ pub async fn get_all(session: Arc, slot_no: &SlotNo) -> AllReg let mut all_invalids_after_filtering = Vec::new(); for (stake_public_key, vote_pub_key) in &stake_addrs { - let registrations_for_given_stake_pub_key = match get_all_registrations_from_stake_pub_key( - &session, - stake_public_key.clone(), - ) - .await - { - Ok(registrations) => registrations, - Err(err) => { - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query ALL {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]) - }, - }; + let mut registrations_for_given_stake_pub_key = + match get_all_registrations_from_stake_pub_key(&session, stake_public_key.clone()).await + { + Ok(registrations) => registrations, + Err(err) => { + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!("Failed to query ALL {err:?}"), + StatusCode::UNPROCESSABLE_ENTITY, + )]) + }, + }; + + // check registrations (stake addrs) are still actively associated with the voting key, + // and have not been registered to another voting key. + registrations_for_given_stake_pub_key = check_stake_addr_voting_key_association( + registrations_for_given_stake_pub_key, + vote_pub_key, + ); // Any registrations that occurred after this Slot are not included in the list. let filtered_registrations = slot_filter(registrations_for_given_stake_pub_key, slot_no); From b808898f982098e6cf98032140b45301a28be1da Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 09:34:30 +0000 Subject: [PATCH 14/46] refactor(query all): snapshot --- catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index fd778638006..3f1ec206572 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -4,7 +4,6 @@ use std::{cmp::Reverse, sync::Arc}; use futures::StreamExt; use poem::http::StatusCode; -use poem_openapi::types::Type; use tracing::error; use super::{ @@ -437,7 +436,7 @@ pub async fn get_all(session: Arc, slot_no: &SlotNo) -> AllReg // Any registrations that occurred after this Slot are not included in the list. let filtered_registrations = slot_filter(registrations_for_given_stake_pub_key, slot_no); - if filtered_registrations.len().is_empty() { + if filtered_registrations.is_empty() { continue; } From 33966f62c43626033551132a8546b6223ab51fa2 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 09:45:25 +0000 Subject: [PATCH 15/46] refactor(query all): snapshot --- .../index/queries/cql/get_registations_w_vote_key.cql | 10 ---------- .../index/queries/registrations/get_all_with_limit.rs | 4 ++-- .../bin/src/service/api/cardano/cip36/filter.rs | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) delete mode 100644 catalyst-gateway/bin/src/db/index/queries/cql/get_registations_w_vote_key.cql diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_registations_w_vote_key.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_registations_w_vote_key.cql deleted file mode 100644 index 911a80f8bd7..00000000000 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_registations_w_vote_key.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 vote_key = :vote_key \ No newline at end of file diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs index c08473a31b5..ec66b938af0 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs @@ -14,7 +14,7 @@ use crate::db::index::{ }; /// Get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] -const GET_ALL_WITH_LIMIT: &str = include_str!("../cql/get_all_stake_addrs.cql"); +const GET_ALL_WITH_STAKES_AND_VOTE_KEYS: &str = include_str!("../cql/get_all_stake_addrs.cql"); /// Get registration #[derive(SerializeRow)] @@ -34,7 +34,7 @@ impl GetAllWithLimitsQuery { pub(crate) async fn prepare(session: Arc) -> anyhow::Result { let get_registrations_query = PreparedQueries::prepare( session, - GET_ALL_WITH_LIMIT, + GET_ALL_WITH_STAKES_AND_VOTE_KEYS, scylla::statement::Consistency::All, true, ) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 3f1ec206572..20cd9de46e0 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -475,7 +475,7 @@ pub async fn get_all(session: Arc, slot_no: &SlotNo) -> AllReg Cip36RegistrationList { slot: slot_no.clone(), voting_key: all_registrations_after_filtering, - invalid: vec![], + invalid: all_invalids_after_filtering.into_iter().flatten().collect(), page: None, }, ))) From a062f81b996f78cc288023856d1cd82066445b44 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 13:24:29 +0000 Subject: [PATCH 16/46] refactor(query all): snapshot --- .../src/service/api/cardano/cip36/endpoint.rs | 20 +----- .../src/service/api/cardano/cip36/filter.rs | 61 +++++++++++++------ .../bin/src/service/api/cardano/cip36/mod.rs | 4 -- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index 6288beb66ba..77620c7de9d 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -7,7 +7,7 @@ use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddres use super::{ cardano::{self}, filter::{get_all, get_registration_given_stake_key_hash, get_registration_given_vote_key}, - response, NoneOrRBAC, SlotNo, + response, SlotNo, }; use crate::{ db::index::session::CassandraSession, @@ -21,8 +21,7 @@ use crate::{ pub(crate) async fn cip36_registrations( lookup: Option, asat: Option, _page: common::types::generic::query::pagination::Page, - _limit: common::types::generic::query::pagination::Limit, _auth: NoneOrRBAC, - _headers: &HeaderMap, + _limit: common::types::generic::query::pagination::Limit, _headers: &HeaderMap, ) -> response::AllRegistration { let Some(session) = CassandraSession::get(true) else { error!("Failed to acquire db session"); @@ -67,20 +66,7 @@ pub(crate) async fn cip36_registrations( ) .await; }, - StakeAddressOrPublicKey::All => { - match asat { - Some(slot_no) => return get_all(session, &slot_no).await, - None => { - return response::AllRegistration::unprocessable_content(vec![ - poem::Error::from_string( - "Must include the Slot in which Registrations are valid until." - .to_string(), - StatusCode::UNPROCESSABLE_ENTITY, - ), - ]) - }, - }; - }, + StakeAddressOrPublicKey::All => return get_all(session, asat).await, }; }; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 20cd9de46e0..052b31a7e10 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -174,8 +174,12 @@ pub async fn get_registration_from_stake_addr( // include any erroneous registrations which occur AFTER the slot# of the last valid // registration - let invalids_report = match get_invalid_registrations(stake_pub_key, slot_no.clone(), session) - .await + let invalids_report = match get_invalid_registrations( + stake_pub_key, + Some(slot_no.clone()), + session, + ) + .await { Ok(invalids) => invalids, Err(err) => { @@ -209,9 +213,12 @@ pub async fn get_registration_from_stake_addr( async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute(session, GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }) + let mut registrations_iter = GetRegistrationQuery::execute( + session, + GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }, + ) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { @@ -266,8 +273,17 @@ fn get_registration_given_slot_no( /// Get invalid registrations for stake addr after given slot no async fn get_invalid_registrations( - stake_pub_key: Ed25519HexEncodedPublicKey, slot_no: SlotNo, session: Arc, + stake_pub_key: Ed25519HexEncodedPublicKey, slot_no: Option, + session: Arc, ) -> anyhow::Result> { + // include any erroneous registrations which occur AFTER the slot# of the last valid + // registration or return all invalids if no slot# declared. + let slot_no = if let Some(slot_no) = slot_no { + slot_no + } else { + SlotNo::from(0) + }; + let mut invalid_registrations_iter = GetInvalidRegistrationQuery::execute( &session, GetInvalidRegistrationParams::new(stake_pub_key.try_into()?, slot_no.clone()), @@ -393,7 +409,7 @@ fn cross_reference_key( } /// Get all registrations with given page contraints -pub async fn get_all(session: Arc, slot_no: &SlotNo) -> AllRegistration { +pub async fn get_all(session: Arc, slot_no: Option) -> AllRegistration { let stake_addrs = match get_all_stake_addrs(&session.clone()).await { Ok(stake_addrs) => stake_addrs, Err(err) => { @@ -433,20 +449,29 @@ pub async fn get_all(session: Arc, slot_no: &SlotNo) -> AllReg vote_pub_key, ); - // Any registrations that occurred after this Slot are not included in the list. - let filtered_registrations = slot_filter(registrations_for_given_stake_pub_key, slot_no); + if let Some(ref slot_no) = slot_no { + // Any registrations that occurred after this Slot are not included in the list. + let filtered_registrations = + slot_filter(registrations_for_given_stake_pub_key, slot_no); - if filtered_registrations.is_empty() { - continue; - } + if filtered_registrations.is_empty() { + continue; + } - all_registrations_after_filtering.push(Cip36RegistrationsForVotingPublicKey { - vote_pub_key: vote_pub_key.clone(), - registrations: filtered_registrations, - }); + all_registrations_after_filtering.push(Cip36RegistrationsForVotingPublicKey { + vote_pub_key: vote_pub_key.clone(), + registrations: filtered_registrations, + }); + } else { + // no slot filtering, return ALL registrations without constraints. + all_registrations_after_filtering.push(Cip36RegistrationsForVotingPublicKey { + vote_pub_key: vote_pub_key.clone(), + registrations: registrations_for_given_stake_pub_key, + }); + } // include any erroneous registrations which occur AFTER the slot# of the last valid - // registration + // registration or return all if no slot# declared. let invalids_report = match get_invalid_registrations( stake_public_key.clone(), slot_no.clone(), @@ -473,7 +498,7 @@ pub async fn get_all(session: Arc, slot_no: &SlotNo) -> AllReg AllRegistration::With(Cip36Registration::Ok(poem_openapi::payload::Json( Cip36RegistrationList { - slot: slot_no.clone(), + slot: slot_no.unwrap_or(SlotNo::from(0)), voting_key: all_registrations_after_filtering, invalid: all_invalids_after_filtering.into_iter().flatten().collect(), page: None, diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs index 6bd32eded28..85e40c762c4 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs @@ -7,7 +7,6 @@ use self::cardano::slot_no::SlotNo; use super::Ed25519HexEncodedPublicKey; use crate::service::common::{ self, - auth::none_or_rbac::NoneOrRBAC, tags::ApiTags, types::cardano::{self}, }; @@ -48,8 +47,6 @@ impl Api { asat: Query>, page: Query>, limit: Query>, - /// No Authorization required, but Token permitted. - auth: NoneOrRBAC, /// Headers, used if the query is requesting ALL to determine if the secret API /// Key is also defined. headers: &HeaderMap, @@ -73,7 +70,6 @@ impl Api { SlotNo::into_option(asat.0), page.0.unwrap_or_default(), limit.0.unwrap_or_default(), - auth, headers, ) .await From f54ac6c4738c00c923a2b3454f05fcf6866d500d Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 13:32:27 +0000 Subject: [PATCH 17/46] refactor(query all): snapshot --- .../bin/src/service/api/cardano/cip36/endpoint.rs | 10 ++++++++-- .../bin/src/service/api/cardano/cip36/filter.rs | 13 +++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index 77620c7de9d..fab53770fcb 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -6,7 +6,7 @@ use tracing::error; use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddressOrPublicKey}; use super::{ cardano::{self}, - filter::{get_all, get_registration_given_stake_key_hash, get_registration_given_vote_key}, + filter::{get_registration_given_stake_key_hash, get_registration_given_vote_key, snapshot}, response, SlotNo, }; use crate::{ @@ -66,7 +66,13 @@ pub(crate) async fn cip36_registrations( ) .await; }, - StakeAddressOrPublicKey::All => return get_all(session, asat).await, + StakeAddressOrPublicKey::All => + // Snapshot replacement, returns all registrations or returns a subset of registrations + // constrained by slot no if given. + // Any registrations that occurred after this Slot are not included in the list. + { + return snapshot(session, asat).await + }, }; }; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 052b31a7e10..30ed20b1662 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -213,12 +213,9 @@ pub async fn get_registration_from_stake_addr( async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute( - session, - GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }, - ) + let mut registrations_iter = GetRegistrationQuery::execute(session, GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { @@ -408,8 +405,8 @@ fn cross_reference_key( } } -/// Get all registrations with given page contraints -pub async fn get_all(session: Arc, slot_no: Option) -> AllRegistration { +/// Get all registrations or contrain if slot no given +pub async fn snapshot(session: Arc, slot_no: Option) -> AllRegistration { let stake_addrs = match get_all_stake_addrs(&session.clone()).await { Ok(stake_addrs) => stake_addrs, Err(err) => { From 41f9c0aeee9a28032929eab2b53b77b66d1bf2da Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 13:38:45 +0000 Subject: [PATCH 18/46] refactor(query all): snapshot --- .../src/service/api/cardano/cip36/endpoint.rs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index fab53770fcb..5047d7b3bd5 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -35,12 +35,12 @@ pub(crate) async fn cip36_registrations( match StakeAddressOrPublicKey::from(stake_or_voter) { StakeAddressOrPublicKey::Address(cip19_stake_address) => { // Typically, a stake address will start with 'stake1', - // for example: stake1ux7k5ztvhwj7ykv5v7vwjjzdfckjk0v74z9p9m5w0t5534clf62eq, + // for example: stake1ux7k5ztvhwj7ykv5v7vwjjzdfckjk0v74z9p9m5w0t5534clf62eq // We need to convert this to a stake hash as per our data model to then find the, - // Full Stake Public Key (32 byte Ed25519 Public key, not hashed) - // Get latest registration given address or Restrict the query to a - // time; which can be represented as either the blockchains slot number or unix - // timestamp. + // Full Stake Public Key (32 byte Ed25519 Public key, not hashed). + // We then get the latest registration or from a specific time as optionally + // specified in the query parameter. This can be represented as either + // the blockchains slot number or a unix timestamp. let stake_hash: HexEncodedHash28 = match cip19_stake_address.try_into() { Ok(stake_hash) => stake_hash, Err(err) => { @@ -56,9 +56,8 @@ pub(crate) async fn cip36_registrations( return get_registration_given_stake_key_hash(stake_hash, session, asat).await; }, StakeAddressOrPublicKey::PublicKey(ed25519_hex_encoded_public_key) => { - // Get latest registration given voting key or Restrict the query to a - // time; which can be represented as either the blockchains slot number or unix time - // ts. + // As above... + // Except using a voting key. return get_registration_given_vote_key( ed25519_hex_encoded_public_key, session, @@ -67,9 +66,9 @@ pub(crate) async fn cip36_registrations( .await; }, StakeAddressOrPublicKey::All => - // Snapshot replacement, returns all registrations or returns a subset of registrations - // constrained by slot no if given. - // Any registrations that occurred after this Slot are not included in the list. + // As above... + // Snapshot replacement, returns all registrations or returns a + // subset of registrations if constrained by a given time. { return snapshot(session, asat).await }, From c7a1fb7ecc119d09d2465da0b26147852bdcf4e0 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 13:39:37 +0000 Subject: [PATCH 19/46] refactor(query all): snapshot --- catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs index 85e40c762c4..ae69fa1dccc 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs @@ -58,7 +58,7 @@ impl Api { if lookup.is_all(headers).is_err() { return response::AllRegistration::unprocessable_content(vec![ poem::Error::from_string( - "Invalid Stake Address or Voter Keyz", + "Invalid Stake Address or Voter key", StatusCode::UNPROCESSABLE_ENTITY, ), ]); From 750cae062238fc38cdc49931f5a67d2ab19e3029 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 13:40:53 +0000 Subject: [PATCH 20/46] refactor(query all): snapshot --- .../service/common/types/cardano/query/mod.rs | 2 +- .../types/cardano/query/stake_or_voter.rs | 20 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/query/mod.rs b/catalyst-gateway/bin/src/service/common/types/cardano/query/mod.rs index ee7e88ee2aa..00d75839075 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/query/mod.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/query/mod.rs @@ -4,7 +4,7 @@ //! `OpenAPI` pub(crate) mod as_at; -#[allow(dead_code)] + pub(crate) mod stake_or_voter; pub(crate) use as_at::AsAt; diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs index e2537d2d798..c2ee9571e11 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs @@ -52,7 +52,7 @@ impl TryFrom<&str> for StakeAddressOrPublicKey { fn try_from(value: &str) -> std::result::Result { /// Regex to parse the parameter #[allow(clippy::unwrap_used)] // Safe because the Regex is constant. Can never panic in prod. - static RE: LazyLock = LazyLock::new(|| Regex::new(PATTERN).unwrap()); + static _RE: LazyLock = LazyLock::new(|| Regex::new(PATTERN).unwrap()); // First check it is the special "ALL" parameter. if value == "ALL" { @@ -105,16 +105,14 @@ const FORMAT: &str = concatcp!( ); /// Schema. -static SCHEMA: LazyLock = LazyLock::new(|| { - MetaSchema { - title: Some(TITLE.to_owned()), - description: Some(DESCRIPTION), - example: Some(Value::String(EXAMPLE.to_string())), - max_length: Some(*MAX_LENGTH), - min_length: Some(*MIN_LENGTH), - pattern: Some(PATTERN.to_string()), - ..poem_openapi::registry::MetaSchema::ANY - } +static SCHEMA: LazyLock = LazyLock::new(|| MetaSchema { + title: Some(TITLE.to_owned()), + description: Some(DESCRIPTION), + example: Some(Value::String(EXAMPLE.to_string())), + max_length: Some(*MAX_LENGTH), + min_length: Some(*MIN_LENGTH), + pattern: Some(PATTERN.to_string()), + ..poem_openapi::registry::MetaSchema::ANY }); /// Either a Stake Address or a ED25519 Public key. From 25ddf950e8d77287a13b74b11abe1227158f9284 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 13:42:12 +0000 Subject: [PATCH 21/46] refactor(query all): snapshot --- .../common/objects/generic/pagination.rs | 2 +- .../types/cardano/query/stake_or_voter.rs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/catalyst-gateway/bin/src/service/common/objects/generic/pagination.rs b/catalyst-gateway/bin/src/service/common/objects/generic/pagination.rs index 4e3e0a99964..ee92db00ae2 100644 --- a/catalyst-gateway/bin/src/service/common/objects/generic/pagination.rs +++ b/catalyst-gateway/bin/src/service/common/objects/generic/pagination.rs @@ -43,7 +43,7 @@ impl CurrentPage { /// - Invalid `limit` value, must be in range /// - Invalid `remaining` value, must be in range #[allow(dead_code)] - pub fn new(page: u64, limit: u64, remaining: u64) -> anyhow::Result { + fn new(page: u64, limit: u64, remaining: u64) -> anyhow::Result { Ok(Self { page: page.try_into()?, limit: limit.try_into()?, diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs index c2ee9571e11..68d899fadef 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs @@ -105,14 +105,16 @@ const FORMAT: &str = concatcp!( ); /// Schema. -static SCHEMA: LazyLock = LazyLock::new(|| MetaSchema { - title: Some(TITLE.to_owned()), - description: Some(DESCRIPTION), - example: Some(Value::String(EXAMPLE.to_string())), - max_length: Some(*MAX_LENGTH), - min_length: Some(*MIN_LENGTH), - pattern: Some(PATTERN.to_string()), - ..poem_openapi::registry::MetaSchema::ANY +static SCHEMA: LazyLock = LazyLock::new(|| { + MetaSchema { + title: Some(TITLE.to_owned()), + description: Some(DESCRIPTION), + example: Some(Value::String(EXAMPLE.to_string())), + max_length: Some(*MAX_LENGTH), + min_length: Some(*MIN_LENGTH), + pattern: Some(PATTERN.to_string()), + ..poem_openapi::registry::MetaSchema::ANY + } }); /// Either a Stake Address or a ED25519 Public key. From 542763d6c057a3d5bf6cd28d038d99a587538084 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 13:59:55 +0000 Subject: [PATCH 22/46] refactor(query all): snapshot --- catalyst-gateway/bin/src/service/common/auth/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/catalyst-gateway/bin/src/service/common/auth/mod.rs b/catalyst-gateway/bin/src/service/common/auth/mod.rs index b3b36b681c8..a45315bf836 100644 --- a/catalyst-gateway/bin/src/service/common/auth/mod.rs +++ b/catalyst-gateway/bin/src/service/common/auth/mod.rs @@ -1,6 +1,5 @@ //! Catalyst RBAC Token Authentication -#[allow(dead_code)] pub(crate) mod api_key; pub(crate) mod none; pub(crate) mod none_or_rbac; From 7f1d5af573c1de341d6343d0f93f06ecb9746b33 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 14:25:44 +0000 Subject: [PATCH 23/46] refactor(query all): snapshot --- .../bin/src/db/index/queries/mod.rs | 23 +++++++++-------- ...mit.rs => get_all_stakes_and_vote_keys.rs} | 25 ++++++++++--------- .../src/db/index/queries/registrations/mod.rs | 2 +- .../src/service/api/cardano/cip36/filter.rs | 6 +++-- 4 files changed, 31 insertions(+), 25 deletions(-) rename catalyst-gateway/bin/src/db/index/queries/registrations/{get_all_with_limit.rs => get_all_stakes_and_vote_keys.rs} (62%) diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index fa19e3587a7..e3f1a57c81a 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -16,9 +16,9 @@ use rbac::{ get_role0_chain_root::GetRole0ChainRootQuery, }; use registrations::{ - get_all_with_limit::GetAllWithLimitsQuery, get_from_stake_addr::GetRegistrationQuery, - get_from_stake_hash::GetStakeAddrQuery, get_from_vote_key::GetStakeAddrFromVoteKeyQuery, - get_invalid::GetInvalidRegistrationQuery, + get_all_stakes_and_vote_keys::GetAllStakesAndVoteKeysQuery, + 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, @@ -97,8 +97,8 @@ pub(crate) enum PreparedSelectQuery { RegistrationsByChainRoot, /// Get chain root by role0 key ChainRootByRole0Key, - /// Get all with limit - GetAllWithLimit, + /// Get all stake and vote keys for snapshot + GetAllStakesAndVoteKeys, } /// All prepared UPSERT query statements (inserts/updates a single value of data). @@ -160,8 +160,8 @@ pub(crate) struct PreparedQueries { registrations_by_chain_root_query: PreparedStatement, /// Get chain root by role0 key chain_root_by_role0_key_query: PreparedStatement, - /// Get all with limit - get_all_with_limit_query: PreparedStatement, + /// Get all stake and vote keys (`stake_key,vote_key`) for snapshot + get_all_stakes_and_vote_keys_query: PreparedStatement, } /// An individual query response that can fail @@ -199,7 +199,8 @@ impl PreparedQueries { let registrations_by_chain_root = GetRegistrationsByChainRootQuery::prepare(session.clone()).await; let chain_root_by_role0_key = GetRole0ChainRootQuery::prepare(session.clone()).await; - let get_all_with_limit = GetAllWithLimitsQuery::prepare(session).await; + let get_all_stakes_and_vote_keys_query = + GetAllStakesAndVoteKeysQuery::prepare(session).await; let ( txo_insert_queries, @@ -247,7 +248,7 @@ impl PreparedQueries { chain_root_by_stake_address_query: chain_root_by_stake_address?, registrations_by_chain_root_query: registrations_by_chain_root?, chain_root_by_role0_key_query: chain_root_by_role0_key?, - get_all_with_limit_query: get_all_with_limit?, + get_all_stakes_and_vote_keys_query: get_all_stakes_and_vote_keys_query?, }) } @@ -338,7 +339,9 @@ impl PreparedQueries { &self.registrations_by_chain_root_query }, PreparedSelectQuery::ChainRootByRole0Key => &self.chain_root_by_role0_key_query, - PreparedSelectQuery::GetAllWithLimit => &self.get_all_with_limit_query, + PreparedSelectQuery::GetAllStakesAndVoteKeys => { + &self.get_all_stakes_and_vote_keys_query + }, }; session diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs similarity index 62% rename from catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs rename to catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs index ec66b938af0..aeafc90eece 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_with_limit.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs @@ -13,26 +13,27 @@ use crate::db::index::{ session::CassandraSession, }; -/// Get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] +/// Get all (`stake_addr,vote` keys) +/// [(`stake_addr,vote_key`)] const GET_ALL_WITH_STAKES_AND_VOTE_KEYS: &str = include_str!("../cql/get_all_stake_addrs.cql"); /// Get registration #[derive(SerializeRow)] -pub(crate) struct GetAllWithLimitsParams {} +pub(crate) struct GetAllStakesAndVoteKeysParams {} /// Get registration query. #[derive(DeserializeRow)] -pub(crate) struct GetAllWithLimitsQuery { +pub(crate) struct GetAllStakesAndVoteKeysQuery { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). pub stake_address: Vec, /// Voting Public Key pub vote_key: Vec, } -impl GetAllWithLimitsQuery { +impl GetAllStakesAndVoteKeysQuery { /// Prepares get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] pub(crate) async fn prepare(session: Arc) -> anyhow::Result { - let get_registrations_query = PreparedQueries::prepare( + let get_all_stake_and_vote_keys = PreparedQueries::prepare( session, GET_ALL_WITH_STAKES_AND_VOTE_KEYS, scylla::statement::Consistency::All, @@ -40,21 +41,21 @@ impl GetAllWithLimitsQuery { ) .await; - if let Err(ref error) = get_registrations_query { - error!(error=%error, "Failed to prepare get all stake addrs"); + if let Err(ref error) = get_all_stake_and_vote_keys { + error!(error=%error, "Failed to prepare get all (stake addrs, vote_keys)"); }; - get_registrations_query + get_all_stake_and_vote_keys } /// Executes get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] pub(crate) async fn execute( - session: &CassandraSession, params: GetAllWithLimitsParams, - ) -> anyhow::Result> { + session: &CassandraSession, params: GetAllStakesAndVoteKeysParams, + ) -> anyhow::Result> { let iter = session - .execute_iter(PreparedSelectQuery::GetAllWithLimit, params) + .execute_iter(PreparedSelectQuery::GetAllStakesAndVoteKeys, params) .await? - .rows_stream::()?; + .rows_stream::()?; 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 ec58759c888..c4e66038a79 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/mod.rs @@ -1,5 +1,5 @@ //! Registration related queries. -pub(crate) mod get_all_with_limit; +pub(crate) mod get_all_stakes_and_vote_keys; pub(crate) mod get_from_stake_addr; pub(crate) mod get_from_stake_hash; pub(crate) mod get_from_vote_key; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 30ed20b1662..a63018c926b 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -20,7 +20,9 @@ use super::{ }; use crate::db::index::{ queries::registrations::{ - get_all_with_limit::{GetAllWithLimitsParams, GetAllWithLimitsQuery}, + get_all_stakes_and_vote_keys::{ + GetAllStakesAndVoteKeysParams, GetAllStakesAndVoteKeysQuery, + }, get_from_stake_addr::{GetRegistrationParams, GetRegistrationQuery}, get_from_stake_hash::{GetStakeAddrParams, GetStakeAddrQuery}, get_from_vote_key::{GetStakeAddrFromVoteKeyParams, GetStakeAddrFromVoteKeyQuery}, @@ -508,7 +510,7 @@ pub async fn get_all_stake_addrs( session: &Arc, ) -> Result, anyhow::Error> { let mut stake_addr_iter = - GetAllWithLimitsQuery::execute(session, GetAllWithLimitsParams {}).await?; + GetAllStakesAndVoteKeysQuery::execute(session, GetAllStakesAndVoteKeysParams {}).await?; let mut vote_key_stake_addr_pair = Vec::new(); while let Some(row) = stake_addr_iter.next().await { let row = row?; From 779c94ddb32a4c0b3809f68ce5d19b76a05234b5 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 14:27:06 +0000 Subject: [PATCH 24/46] refactor(query all): snapshot --- catalyst-gateway/bin/src/db/index/queries/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index e3f1a57c81a..3bcbc4cd5bd 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -97,7 +97,7 @@ pub(crate) enum PreparedSelectQuery { RegistrationsByChainRoot, /// Get chain root by role0 key ChainRootByRole0Key, - /// Get all stake and vote keys for snapshot + /// Get all stake and vote keys for snapshot (stake_pub_key,vote_key) GetAllStakesAndVoteKeys, } @@ -301,7 +301,9 @@ impl PreparedQueries { pub(crate) async fn execute_upsert

( &self, session: Arc, upsert_query: PreparedUpsertQuery, params: P, ) -> anyhow::Result<()> - where P: SerializeRow { + where + P: SerializeRow, + { let prepared_stmt = match upsert_query { PreparedUpsertQuery::SyncStatusInsert => &self.sync_status_insert, }; @@ -321,7 +323,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::TxoByStakeAddress => &self.txo_by_stake_address_query, PreparedSelectQuery::TxiByTransactionHash => &self.txi_by_txn_hash_query, From 0af590483c2feb9a265a5285247ed29c6c9e0b46 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 14:28:40 +0000 Subject: [PATCH 25/46] refactor(query all): snapshot --- .../queries/registrations/get_all_stakes_and_vote_keys.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs index aeafc90eece..747e3de6657 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs @@ -1,4 +1,4 @@ -//! Get stake addr registrations +//! Get all stake and vote keys (stake_pub_key,vote_key) for snapshot use std::sync::Arc; @@ -21,7 +21,7 @@ const GET_ALL_WITH_STAKES_AND_VOTE_KEYS: &str = include_str!("../cql/get_all_sta #[derive(SerializeRow)] pub(crate) struct GetAllStakesAndVoteKeysParams {} -/// Get registration query. +/// Get stakes and vote keys for snapshot. #[derive(DeserializeRow)] pub(crate) struct GetAllStakesAndVoteKeysQuery { /// Full Stake Address (not hashed, 32 byte ED25519 Public key). From ee6effc057b0a8af2d39445c8ae98352000823a4 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 14:34:25 +0000 Subject: [PATCH 26/46] refactor(query all): snapshot --- .../src/db/index/queries/cql/get_all_stake_addrs.cql | 3 ++- catalyst-gateway/bin/src/db/index/queries/mod.rs | 10 +++------- .../registrations/get_all_stakes_and_vote_keys.rs | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/cql/get_all_stake_addrs.cql b/catalyst-gateway/bin/src/db/index/queries/cql/get_all_stake_addrs.cql index 9930ae7f1c6..717a0cca59f 100644 --- a/catalyst-gateway/bin/src/db/index/queries/cql/get_all_stake_addrs.cql +++ b/catalyst-gateway/bin/src/db/index/queries/cql/get_all_stake_addrs.cql @@ -1,2 +1,3 @@ -SELECT stake_address,vote_key +SELECT + stake_address,vote_key FROM cip36_registration; \ 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 3bcbc4cd5bd..3496f9338a0 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -97,7 +97,7 @@ pub(crate) enum PreparedSelectQuery { RegistrationsByChainRoot, /// Get chain root by role0 key ChainRootByRole0Key, - /// Get all stake and vote keys for snapshot (stake_pub_key,vote_key) + /// Get all stake and vote keys for snapshot (`stake_pub_key,vote_key`) GetAllStakesAndVoteKeys, } @@ -301,9 +301,7 @@ impl PreparedQueries { pub(crate) async fn execute_upsert

( &self, session: Arc, upsert_query: PreparedUpsertQuery, params: P, ) -> anyhow::Result<()> - where - P: SerializeRow, - { + where P: SerializeRow { let prepared_stmt = match upsert_query { PreparedUpsertQuery::SyncStatusInsert => &self.sync_status_insert, }; @@ -323,9 +321,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/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs index 747e3de6657..80d949fe148 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs @@ -1,4 +1,4 @@ -//! Get all stake and vote keys (stake_pub_key,vote_key) for snapshot +//! Get all stake and vote keys (`stake_pub_key,vote_key`) for snapshot use std::sync::Arc; From 4bab7eae9ab6b99c65ee95ec6075e4e7d1035356 Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 14:38:14 +0000 Subject: [PATCH 27/46] refactor(query all): snapshot --- .../bin/src/service/api/cardano/cip36/filter.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index a63018c926b..296a72390a9 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -215,9 +215,12 @@ pub async fn get_registration_from_stake_addr( async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute(session, GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }) + let mut registrations_iter = GetRegistrationQuery::execute( + session, + GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }, + ) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { @@ -276,7 +279,7 @@ async fn get_invalid_registrations( session: Arc, ) -> anyhow::Result> { // include any erroneous registrations which occur AFTER the slot# of the last valid - // registration or return all invalids if no slot# declared. + // registration or return all invalids if NO slot# declared. let slot_no = if let Some(slot_no) = slot_no { slot_no } else { @@ -470,7 +473,7 @@ pub async fn snapshot(session: Arc, slot_no: Option) - } // include any erroneous registrations which occur AFTER the slot# of the last valid - // registration or return all if no slot# declared. + // registration or return all if NO slot# declared. let invalids_report = match get_invalid_registrations( stake_public_key.clone(), slot_no.clone(), From 85f91493d3ab605e8e4a79efc5c28b2061aea2df Mon Sep 17 00:00:00 2001 From: cong-or Date: Wed, 8 Jan 2025 14:44:50 +0000 Subject: [PATCH 28/46] refactor(query all): snapshot --- catalyst-gateway/bin/src/db/index/queries/mod.rs | 8 ++++++-- .../queries/registrations/get_all_stakes_and_vote_keys.rs | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index 3496f9338a0..87bb4d0ad19 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -301,7 +301,9 @@ impl PreparedQueries { pub(crate) async fn execute_upsert

( &self, session: Arc, upsert_query: PreparedUpsertQuery, params: P, ) -> anyhow::Result<()> - where P: SerializeRow { + where + P: SerializeRow, + { let prepared_stmt = match upsert_query { PreparedUpsertQuery::SyncStatusInsert => &self.sync_status_insert, }; @@ -321,7 +323,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::TxoByStakeAddress => &self.txo_by_stake_address_query, PreparedSelectQuery::TxiByTransactionHash => &self.txi_by_txn_hash_query, diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs index 80d949fe148..6a2dfa3517c 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs @@ -1,4 +1,5 @@ -//! Get all stake and vote keys (`stake_pub_key,vote_key`) for snapshot +//! Get all stake and vote keys (`stake_pub_key,vote_key`) +//! Result is used to compose various query registrations for snapshot. use std::sync::Arc; From 571f4d84fc7341bb500bc65dec2b5af64c62ba1f Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 10:05:33 +0000 Subject: [PATCH 29/46] refactor(query all): snapshot --- catalyst-gateway/bin/src/db/index/queries/mod.rs | 8 ++------ .../registrations/get_all_stakes_and_vote_keys.rs | 4 ++-- .../bin/src/service/api/cardano/cip36/filter.rs | 9 +++------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/mod.rs b/catalyst-gateway/bin/src/db/index/queries/mod.rs index 87bb4d0ad19..3496f9338a0 100644 --- a/catalyst-gateway/bin/src/db/index/queries/mod.rs +++ b/catalyst-gateway/bin/src/db/index/queries/mod.rs @@ -301,9 +301,7 @@ impl PreparedQueries { pub(crate) async fn execute_upsert

( &self, session: Arc, upsert_query: PreparedUpsertQuery, params: P, ) -> anyhow::Result<()> - where - P: SerializeRow, - { + where P: SerializeRow { let prepared_stmt = match upsert_query { PreparedUpsertQuery::SyncStatusInsert => &self.sync_status_insert, }; @@ -323,9 +321,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/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs index 6a2dfa3517c..3b44ad66468 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs @@ -16,7 +16,7 @@ use crate::db::index::{ /// Get all (`stake_addr,vote` keys) /// [(`stake_addr,vote_key`)] -const GET_ALL_WITH_STAKES_AND_VOTE_KEYS: &str = include_str!("../cql/get_all_stake_addrs.cql"); +const GET_ALL_STAKES_AND_VOTE_KEYS: &str = include_str!("../cql/get_all_stake_addrs.cql"); /// Get registration #[derive(SerializeRow)] @@ -36,7 +36,7 @@ impl GetAllStakesAndVoteKeysQuery { pub(crate) async fn prepare(session: Arc) -> anyhow::Result { let get_all_stake_and_vote_keys = PreparedQueries::prepare( session, - GET_ALL_WITH_STAKES_AND_VOTE_KEYS, + GET_ALL_STAKES_AND_VOTE_KEYS, scylla::statement::Consistency::All, true, ) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 296a72390a9..fe942300a2b 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -215,12 +215,9 @@ pub async fn get_registration_from_stake_addr( async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute( - session, - GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }, - ) + let mut registrations_iter = GetRegistrationQuery::execute(session, GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { From 73235014ebb85f2854a71614dc1cc7db33705e50 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 10:30:51 +0000 Subject: [PATCH 30/46] refactor(query all): snapshot --- .../src/service/api/cardano/cip36/filter.rs | 159 ++++++++++-------- 1 file changed, 85 insertions(+), 74 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index fe942300a2b..1906c58836d 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -31,8 +31,8 @@ use crate::db::index::{ session::CassandraSession, }; -/// Get registration given a stake key hash, time specific based on asat param, -/// or latest registration returned if no asat given. +/// Get registration given a stake key hash, it can time specific based on asat param, +/// or the latest registration returned if no asat given. pub(crate) async fn get_registration_given_stake_key_hash( stake_hash: HexEncodedHash28, session: Arc, asat: Option, ) -> AllRegistration { @@ -46,7 +46,7 @@ pub(crate) async fn get_registration_given_stake_key_hash( Ok(stake_addr) => stake_addr, Err(err) => { error!( - id="get_latest_registration_from_stake_key_hash_query_stake_addr", + id="get_registration_from_stake_key_hash_query_stake_addr", error=?err, "Failed to query stake addr from stake hash" ); @@ -63,13 +63,13 @@ pub(crate) async fn get_registration_given_stake_key_hash( Ok(r) => r, Err(err) => { error!( - id="get_latest_registration_from_stake_key_hash_latest_registration", + id="get_registration_from_stake_key_hash_registration", error=?err, "Failed to get latest registration" ); return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to get latest registration {err:?}"), + format!("Failed to get stake addr from stake hash {err:?}"), StatusCode::UNPROCESSABLE_ENTITY, )]); }, @@ -100,6 +100,7 @@ pub async fn get_registration_from_stake_addr( stake_pub_key: Ed25519HexEncodedPublicKey, asat: Option, session: Arc, vote_key: Option, ) -> AllRegistration { + // Get all registrations from given stake pub key. let mut registrations = match get_all_registrations_from_stake_pub_key(&session.clone(), stake_pub_key.clone()) .await @@ -107,24 +108,25 @@ pub async fn get_registration_from_stake_addr( Ok(registration) => registration, Err(err) => { error!( - id="get_registration_from_stake_addr", + id="get_registration_from_stake_pub_key", error=?err, - "Failed to query stake addr" + "Failed to query stake stake pub key" ); return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query stake addr {err:?}"), + format!("Failed to query stake stake pub key{err:?}"), StatusCode::UNPROCESSABLE_ENTITY, )]); }, }; - // check registrations (stake addrs) are still actively associated with the voting key, + // check registrations are still actively associated with the voting key, // and have not been registered to another voting key. if let Some(vote_key) = vote_key { registrations = check_stake_addr_voting_key_association(registrations, &vote_key); } + // Query requires the registration to be bound by time. let registration = if let Some(slot_no) = asat { match get_registration_given_slot_no(registrations, &slot_no) { Ok(registration) => registration, @@ -142,6 +144,7 @@ pub async fn get_registration_from_stake_addr( }, } } else { + // Query not bound by time, return latest registration. match sort_latest_registration(registrations) { Ok(registration) => registration, Err(err) => { @@ -159,6 +162,7 @@ pub async fn get_registration_from_stake_addr( } }; + // Registration found, now find invalids. let slot_no = registration.clone().slot_no; let Some(stake_pub_key) = registration.clone().stake_pub_key else { return AllRegistration::unprocessable_content(vec![poem::Error::from_string( @@ -176,27 +180,24 @@ pub async fn get_registration_from_stake_addr( // include any erroneous registrations which occur AFTER the slot# of the last valid // registration - let invalids_report = match get_invalid_registrations( - stake_pub_key, - Some(slot_no.clone()), - session, - ) - .await - { - Ok(invalids) => invalids, - Err(err) => { - error!( - id="get_latest_registration_from_stake_key_hash_invalid_registrations_for_stake_addr", - error=?err, - "Failed to obtain invalid registrations for given stake addr", - ); + let invalids_report = + match get_invalid_registrations(stake_pub_key, Some(slot_no.clone()), session).await { + Ok(invalids) => invalids, + Err(err) => { + error!( + id="get_registration_from_stake_key_hash_invalid_registrations_for_stake_addr", + error=?err, + "Failed to obtain invalid registrations for given stake pub key", + ); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to obtain invalid registrations for given stake addr {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); - }, - }; + return AllRegistration::unprocessable_content(vec![poem::Error::from_string( + format!( + "Failed to obtain invalid registrations for given stake pub key {err:?}" + ), + StatusCode::UNPROCESSABLE_ENTITY, + )]); + }, + }; AllRegistration::With(Cip36Registration::Ok(poem_openapi::payload::Json( Cip36RegistrationList { @@ -211,6 +212,27 @@ pub async fn get_registration_from_stake_addr( ))) } +/// 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: &Ed25519HexEncodedPublicKey, +) -> Vec { + registrations + .into_iter() + .filter(|registration| cross_reference_key(associated_voting_key, registration)) + .collect() +} + +/// Check associated voting key matches registration voting key +fn cross_reference_key( + associated_voting_key: &Ed25519HexEncodedPublicKey, r: &Cip36Details, +) -> bool { + match r.vote_pub_key.clone() { + Some(key) => key == *associated_voting_key, + None => false, + } +} + /// Get all cip36 registrations for a given stake address. async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, @@ -252,7 +274,8 @@ async fn get_all_registrations_from_stake_pub_key( Ok(registrations) } -/// Sort latest registrations for a given stake address, sort by slot no. +/// Sort latest registrations for a given stake address, sort by slot no and return +/// latest. fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { registrations.sort_by_key(|registration| Reverse(registration.slot_no.clone())); registrations.into_iter().next().ok_or(anyhow::anyhow!( @@ -260,7 +283,7 @@ fn sort_latest_registration(mut registrations: Vec) -> anyhow::Res )) } -/// Get registration given slot no. +/// Get registration given slot# fn get_registration_given_slot_no( registrations: Vec, slot_no: &SlotNo, ) -> anyhow::Result { @@ -270,7 +293,7 @@ fn get_registration_given_slot_no( .ok_or(anyhow::anyhow!("Unable to get registration given slot no")) } -/// Get invalid registrations for stake addr after given slot no +/// Get invalid registrations for stake addr after given slot# async fn get_invalid_registrations( stake_pub_key: Ed25519HexEncodedPublicKey, slot_no: Option, session: Arc, @@ -386,36 +409,16 @@ pub(crate) async fn get_registration_given_vote_key( )]) } -/// 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: &Ed25519HexEncodedPublicKey, -) -> Vec { - registrations - .into_iter() - .filter(|registration| cross_reference_key(associated_voting_key, registration)) - .collect() -} - -/// Check associated voting key matches registration voting key -fn cross_reference_key( - associated_voting_key: &Ed25519HexEncodedPublicKey, r: &Cip36Details, -) -> bool { - match r.vote_pub_key.clone() { - Some(key) => key == *associated_voting_key, - None => false, - } -} - -/// Get all registrations or contrain if slot no given +/// ALL +/// Get all registrations or contrain if slot# given. pub async fn snapshot(session: Arc, slot_no: Option) -> AllRegistration { - let stake_addrs = match get_all_stake_addrs(&session.clone()).await { - Ok(stake_addrs) => stake_addrs, + let all_stakes_and_vote_keys = match get_all_stake_addrs(&session.clone()).await { + Ok(key_pairs) => key_pairs, Err(err) => { error!( - id="get_all_with_limit", + id="get_all_stake_and_vote_keys", error=?err, - "Failed to all" + "Failed to obtain all" ); return AllRegistration::unprocessable_content(vec![poem::Error::from_string( @@ -428,7 +431,11 @@ pub async fn snapshot(session: Arc, slot_no: Option) - let mut all_registrations_after_filtering = Vec::new(); let mut all_invalids_after_filtering = Vec::new(); - for (stake_public_key, vote_pub_key) in &stake_addrs { + // We now have all stake pub keys and vote keys for cip36 registrations. + // Iterate through them and individually get all valid and invalid registrations. + // Compose the result into a snapshot. + // TODO: Optimise: Can be done parallel. + for (stake_public_key, vote_pub_key) in &all_stakes_and_vote_keys { let mut registrations_for_given_stake_pub_key = match get_all_registrations_from_stake_pub_key(&session, stake_public_key.clone()).await { @@ -441,13 +448,14 @@ pub async fn snapshot(session: Arc, slot_no: Option) - }, }; - // check registrations (stake addrs) are still actively associated with the voting key, - // and have not been registered to another voting key. + // check the registrations stake pub key are still actively associated with the voting + // key, and have not been registered to another voting key. registrations_for_given_stake_pub_key = check_stake_addr_voting_key_association( registrations_for_given_stake_pub_key, vote_pub_key, ); + // ALL: Snapshot can be contrained into a subset with a time contraint or NOT. if let Some(ref slot_no) = slot_no { // Any registrations that occurred after this Slot are not included in the list. let filtered_registrations = @@ -462,7 +470,7 @@ pub async fn snapshot(session: Arc, slot_no: Option) - registrations: filtered_registrations, }); } else { - // no slot filtering, return ALL registrations without constraints. + // No slot filtering, return ALL registrations without constraints. all_registrations_after_filtering.push(Cip36RegistrationsForVotingPublicKey { vote_pub_key: vote_pub_key.clone(), registrations: registrations_for_given_stake_pub_key, @@ -481,13 +489,13 @@ pub async fn snapshot(session: Arc, slot_no: Option) - Ok(invalids) => invalids, Err(err) => { error!( - id="get_latest_registration_from_stake_key_hash_invalid_registrations_for_stake_addr", + id="get_invalid_registrations_snapshot", error=?err, - "Failed to obtain invalid registrations for given stake addr", + "Failed to obtain invalid registrations for given stake addr in snapshot", ); return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to obtain invalid registrations for given stake addr {err:?}"), + format!("Failed to obtain invalid registrations for given stake addr {err:?} in snapshot"), StatusCode::UNPROCESSABLE_ENTITY, )]); }, @@ -505,13 +513,24 @@ pub async fn snapshot(session: Arc, slot_no: Option) - ))) } -/// Get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] +/// Filter out any registrations that occurred after this Slot no +fn slot_filter(registrations: Vec, slot_no: &SlotNo) -> Vec { + registrations + .into_iter() + .filter(|registration| registration.slot_no < *slot_no) + .collect() +} + +/// Get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] from cip36 +/// registrations. pub async fn get_all_stake_addrs( session: &Arc, ) -> Result, anyhow::Error> { let mut stake_addr_iter = GetAllStakesAndVoteKeysQuery::execute(session, GetAllStakesAndVoteKeysParams {}).await?; + let mut vote_key_stake_addr_pair = Vec::new(); + while let Some(row) = stake_addr_iter.next().await { let row = row?; @@ -522,11 +541,3 @@ pub async fn get_all_stake_addrs( } Ok(vote_key_stake_addr_pair) } - -/// Filter out any registrations that occurred after this Slot no -fn slot_filter(registrations: Vec, slot_no: &SlotNo) -> Vec { - registrations - .into_iter() - .filter(|registration| registration.slot_no < *slot_no) - .collect() -} From fe928ae9406306017dea1dbc872b1c9d7393f1b0 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 10:32:40 +0000 Subject: [PATCH 31/46] refactor(query all): snapshot --- .../index/queries/registrations/get_all_stakes_and_vote_keys.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs index 3b44ad66468..d3593872942 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs @@ -18,7 +18,7 @@ use crate::db::index::{ /// [(`stake_addr,vote_key`)] const GET_ALL_STAKES_AND_VOTE_KEYS: &str = include_str!("../cql/get_all_stake_addrs.cql"); -/// Get registration +/// Get all stake and vote keys from cip36 registration #[derive(SerializeRow)] pub(crate) struct GetAllStakesAndVoteKeysParams {} From ac52d82650888a717f7ab4598982073b77562a68 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 10:43:35 +0000 Subject: [PATCH 32/46] refactor(query all): snapshot --- .../bin/src/service/api/cardano/cip36/filter.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 1906c58836d..ad4958af65c 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -31,7 +31,7 @@ use crate::db::index::{ session::CassandraSession, }; -/// Get registration given a stake key hash, it can time specific based on asat param, +/// Get registration given a stake key hash, it can be time specific based on asat param, /// or the latest registration returned if no asat given. pub(crate) async fn get_registration_given_stake_key_hash( stake_hash: HexEncodedHash28, session: Arc, asat: Option, @@ -185,7 +185,7 @@ pub async fn get_registration_from_stake_addr( Ok(invalids) => invalids, Err(err) => { error!( - id="get_registration_from_stake_key_hash_invalid_registrations_for_stake_addr", + id="get_registration_from_stake_key_hash_invalid_registrations_lookup", error=?err, "Failed to obtain invalid registrations for given stake pub key", ); @@ -227,10 +227,10 @@ fn check_stake_addr_voting_key_association( fn cross_reference_key( associated_voting_key: &Ed25519HexEncodedPublicKey, r: &Cip36Details, ) -> bool { - match r.vote_pub_key.clone() { - Some(key) => key == *associated_voting_key, - None => false, - } + r.vote_pub_key + .clone() + .map(|key| key == *associated_voting_key) + .is_some() } /// Get all cip36 registrations for a given stake address. From 7734f1025dae4df8673d4ca0fc775e92b64c8e25 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 10:45:11 +0000 Subject: [PATCH 33/46] refactor(query all): snapshot --- .../bin/src/service/api/cardano/cip36/filter.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index ad4958af65c..6f9279d0ad9 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -237,9 +237,12 @@ fn cross_reference_key( async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute(session, GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }) + let mut registrations_iter = GetRegistrationQuery::execute( + session, + GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }, + ) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { @@ -274,8 +277,7 @@ async fn get_all_registrations_from_stake_pub_key( Ok(registrations) } -/// Sort latest registrations for a given stake address, sort by slot no and return -/// latest. +/// Sort latest registrations for a given stake address, sort by slot no and return latest. fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { registrations.sort_by_key(|registration| Reverse(registration.slot_no.clone())); registrations.into_iter().next().ok_or(anyhow::anyhow!( From e9ba17d1ef1547947b97b6f032314c4e296e6854 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 10:50:40 +0000 Subject: [PATCH 34/46] refactor(query all): snapshot --- catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 6f9279d0ad9..b59bafe0b8e 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -414,7 +414,7 @@ pub(crate) async fn get_registration_given_vote_key( /// ALL /// Get all registrations or contrain if slot# given. pub async fn snapshot(session: Arc, slot_no: Option) -> AllRegistration { - let all_stakes_and_vote_keys = match get_all_stake_addrs(&session.clone()).await { + let all_stakes_and_vote_keys = match get_all_stake_addrs_and_vote_keys(&session.clone()).await { Ok(key_pairs) => key_pairs, Err(err) => { error!( @@ -525,7 +525,7 @@ fn slot_filter(registrations: Vec, slot_no: &SlotNo) -> Vec, ) -> Result, anyhow::Error> { let mut stake_addr_iter = From 164390b8a584e3e57d76501eddf7cf49acad4715 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 10:51:12 +0000 Subject: [PATCH 35/46] refactor(query all): snapshot --- catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index b59bafe0b8e..7b5c692274e 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -523,8 +523,7 @@ fn slot_filter(registrations: Vec, slot_no: &SlotNo) -> Vec, ) -> Result, anyhow::Error> { From a7f4a5821f8eb1577e4502b32d17ba3096dab601 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 11:37:53 +0000 Subject: [PATCH 36/46] refactor(query all): snapshot --- .../bin/src/service/api/cardano/cip36/filter.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 7b5c692274e..f2b0b3f2cde 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -237,12 +237,9 @@ fn cross_reference_key( async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute( - session, - GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }, - ) + let mut registrations_iter = GetRegistrationQuery::execute(session, GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { @@ -277,7 +274,8 @@ async fn get_all_registrations_from_stake_pub_key( Ok(registrations) } -/// Sort latest registrations for a given stake address, sort by slot no and return latest. +/// Sort latest registrations for a given stake address, sort by slot no and return +/// latest. fn sort_latest_registration(mut registrations: Vec) -> anyhow::Result { registrations.sort_by_key(|registration| Reverse(registration.slot_no.clone())); registrations.into_iter().next().ok_or(anyhow::anyhow!( @@ -523,7 +521,8 @@ fn slot_filter(registrations: Vec, slot_no: &SlotNo) -> Vec, ) -> Result, anyhow::Error> { From bc5bdae9eeec80ac760bba6e9d555f2034db096b Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 11:47:41 +0000 Subject: [PATCH 37/46] refactor(housekeeping): local --- catalyst-gateway/bin/src/settings/cassandra_db.rs | 4 ++-- catalyst-gateway/bin/src/settings/mod.rs | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/catalyst-gateway/bin/src/settings/cassandra_db.rs b/catalyst-gateway/bin/src/settings/cassandra_db.rs index 8d4aa620dc7..6ce6e220a26 100644 --- a/catalyst-gateway/bin/src/settings/cassandra_db.rs +++ b/catalyst-gateway/bin/src/settings/cassandra_db.rs @@ -9,13 +9,13 @@ use crate::db::{ }; /// Default Cassandra DB URL for the Persistent DB. -pub(super) const PERSISTENT_URL_DEFAULT: &str = "172.17.0.2:9042"; +pub(super) const PERSISTENT_URL_DEFAULT: &str = "127.0.0.1:9042"; /// Default Cassandra DB URL for the Persistent DB. pub(super) const PERSISTENT_NAMESPACE_DEFAULT: &str = "persistent"; /// Default Cassandra DB URL for the Persistent DB. -pub(super) const VOLATILE_URL_DEFAULT: &str = "172.17.0.2:9042"; +pub(super) const VOLATILE_URL_DEFAULT: &str = "127.0.0.1:9042"; /// Default Cassandra DB URL for the Persistent DB. pub(super) const VOLATILE_NAMESPACE_DEFAULT: &str = "volatile"; diff --git a/catalyst-gateway/bin/src/settings/mod.rs b/catalyst-gateway/bin/src/settings/mod.rs index 23b179d79dd..6be03945050 100644 --- a/catalyst-gateway/bin/src/settings/mod.rs +++ b/catalyst-gateway/bin/src/settings/mod.rs @@ -1,6 +1,5 @@ //! Command line and environment variable settings for the service use std::{ - env, net::{IpAddr, Ipv4Addr, SocketAddr}, path::PathBuf, str::FromStr, @@ -171,8 +170,6 @@ static ENV_VARS: LazyLock = LazyLock::new(|| { }, }; - env::set_var("INTERNAL_API_KEY", "catv1.UP6jrII9HzfRdP9CbJGBMa1QAZRA9fjV8GH-0BQZ0H4GxVhAjPgtsUqyvIbNEMX24Mhgyz4miy7J5q84lxE8AJ2kzfRPw9Cz9tWUTHe1ebNw-rYjBbxspBr2ajr__7dood7pDw"); - EnvVars { github_repo_owner: StringEnvVar::new("GITHUB_REPO_OWNER", GITHUB_REPO_OWNER_DEFAULT.into()), github_repo_name: StringEnvVar::new("GITHUB_REPO_NAME", GITHUB_REPO_NAME_DEFAULT.into()), From a718f1f7d701e86168d01a782e330932264a83d6 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 11:57:06 +0000 Subject: [PATCH 38/46] refactor(housekeeping): local --- .../bin/src/db/index/tests/scylla_queries.rs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/tests/scylla_queries.rs b/catalyst-gateway/bin/src/db/index/tests/scylla_queries.rs index 4c83e087e85..c233b9d02df 100644 --- a/catalyst-gateway/bin/src/db/index/tests/scylla_queries.rs +++ b/catalyst-gateway/bin/src/db/index/tests/scylla_queries.rs @@ -4,16 +4,19 @@ use futures::StreamExt; use super::*; -use crate::db::index::queries::{ - rbac::{get_chain_root::*, get_registrations::*, get_role0_chain_root::*}, - registrations::{ - get_from_stake_addr::*, get_from_stake_hash::*, get_from_vote_key::*, get_invalid::*, +use crate::{ + db::index::queries::{ + rbac::{get_chain_root::*, get_registrations::*, get_role0_chain_root::*}, + registrations::{ + get_from_stake_addr::*, get_from_stake_hash::*, get_from_vote_key::*, get_invalid::*, + }, + staked_ada::{ + get_assets_by_stake_address::*, get_txi_by_txn_hash::*, get_txo_by_stake_address::*, + update_txo_spent::*, + }, + sync_status::update::*, }, - staked_ada::{ - get_assets_by_stake_address::*, get_txi_by_txn_hash::*, get_txo_by_stake_address::*, - update_txo_spent::*, - }, - sync_status::update::*, + service::common::types::cardano::slot_no::SlotNo, }; #[ignore = "An integration test which requires a running Scylla node instance, disabled from `testunit` CI run"] @@ -58,7 +61,7 @@ async fn test_get_invalid_registration_w_stake_addr() { let mut row_stream = GetInvalidRegistrationQuery::execute( &session, - GetInvalidRegistrationParams::new(vec![], num_bigint::BigInt::from(i64::MAX)), + GetInvalidRegistrationParams::new(vec![], SlotNo::from(u64::MAX)), ) .await .unwrap(); From 6065f2f71a2d1abf26a8b626c7de952fbe277b7c Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 12:43:02 +0000 Subject: [PATCH 39/46] refactor(housekeeping): spellcheck --- .config/dictionaries/project.dic | 2 ++ .../bin/src/service/api/cardano/cip36/endpoint.rs | 1 - .../bin/src/service/api/cardano/cip36/filter.rs | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic index 013d85f1b88..5b2c5999bd6 100644 --- a/.config/dictionaries/project.dic +++ b/.config/dictionaries/project.dic @@ -349,3 +349,5 @@ xpub xpublic xvfb yoroi +stake1u94ullc9nj9gawc08990nx8hwgw80l9zpmr8re44kydqy9cdjq6rq +invalid1u9nlq5nmuzthw3vhgakfpxyq4r0zl2c0p8uqy24gpyjsa6c3df4h6 \ No newline at end of file diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index 5047d7b3bd5..6d7b12c5c9a 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -35,7 +35,6 @@ pub(crate) async fn cip36_registrations( match StakeAddressOrPublicKey::from(stake_or_voter) { StakeAddressOrPublicKey::Address(cip19_stake_address) => { // Typically, a stake address will start with 'stake1', - // for example: stake1ux7k5ztvhwj7ykv5v7vwjjzdfckjk0v74z9p9m5w0t5534clf62eq // We need to convert this to a stake hash as per our data model to then find the, // Full Stake Public Key (32 byte Ed25519 Public key, not hashed). // We then get the latest registration or from a specific time as optionally diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index f2b0b3f2cde..1732120daac 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -410,7 +410,7 @@ pub(crate) async fn get_registration_given_vote_key( } /// ALL -/// Get all registrations or contrain if slot# given. +/// Get all registrations or constrain if slot# given. pub async fn snapshot(session: Arc, slot_no: Option) -> AllRegistration { let all_stakes_and_vote_keys = match get_all_stake_addrs_and_vote_keys(&session.clone()).await { Ok(key_pairs) => key_pairs, @@ -434,7 +434,7 @@ pub async fn snapshot(session: Arc, slot_no: Option) - // We now have all stake pub keys and vote keys for cip36 registrations. // Iterate through them and individually get all valid and invalid registrations. // Compose the result into a snapshot. - // TODO: Optimise: Can be done parallel. + // TODO: Optimize: Can be done parallel. for (stake_public_key, vote_pub_key) in &all_stakes_and_vote_keys { let mut registrations_for_given_stake_pub_key = match get_all_registrations_from_stake_pub_key(&session, stake_public_key.clone()).await @@ -455,7 +455,7 @@ pub async fn snapshot(session: Arc, slot_no: Option) - vote_pub_key, ); - // ALL: Snapshot can be contrained into a subset with a time contraint or NOT. + // ALL: Snapshot can be constrained into a subset with a time constraint or NOT. if let Some(ref slot_no) = slot_no { // Any registrations that occurred after this Slot are not included in the list. let filtered_registrations = From acfb7bebcbb4c7ecb5947cab5825a57836f84bc6 Mon Sep 17 00:00:00 2001 From: cong-or Date: Thu, 9 Jan 2025 16:01:41 +0000 Subject: [PATCH 40/46] refactor(no auth): rm auth --- catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs index ae69fa1dccc..b404f52d3e4 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/mod.rs @@ -3,7 +3,7 @@ use poem::http::{HeaderMap, StatusCode}; use poem_openapi::{param::Query, OpenApi}; -use self::cardano::slot_no::SlotNo; +use self::{cardano::slot_no::SlotNo, common::auth::none::NoAuthorization}; use super::Ed25519HexEncodedPublicKey; use crate::service::common::{ self, @@ -50,6 +50,7 @@ impl Api { /// Headers, used if the query is requesting ALL to determine if the secret API /// Key is also defined. headers: &HeaderMap, + _auth: NoAuthorization, ) -> response::AllRegistration { // Special validation for the `lookup` parameter. // If the parameter is ALL, BUT we do not have a valid API Key, just report the parameter From 0a2286d94f904f3365956f29e53dddcabb130802 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 13 Jan 2025 15:05:26 +0000 Subject: [PATCH 41/46] refactor(improved error handling): bubble up rather than log --- .../src/service/api/cardano/cip36/endpoint.rs | 22 +-- .../src/service/api/cardano/cip36/filter.rs | 180 +++++------------- 2 files changed, 62 insertions(+), 140 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs index 6d7b12c5c9a..8ad44ace7fb 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs @@ -1,6 +1,6 @@ //! Implementation of the GET `/cardano/cip36` endpoint -use poem::http::{HeaderMap, StatusCode}; +use poem::http::HeaderMap; use tracing::error; use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddressOrPublicKey}; @@ -13,7 +13,7 @@ use crate::{ db::index::session::CassandraSession, service::{ api::cardano::cip36::response::AllRegistration, - common::{self}, + common::{self, types::headers::retry_after::RetryAfterOption}, }, }; @@ -25,10 +25,10 @@ pub(crate) async fn cip36_registrations( ) -> response::AllRegistration { let Some(session) = CassandraSession::get(true) else { error!("Failed to acquire db session"); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Connection issue", - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::service_unavailable( + &anyhow::anyhow!("Failed to acquire db session"), + RetryAfterOption::Default, + ); }; if let Some(stake_or_voter) = lookup { @@ -43,12 +43,10 @@ pub(crate) async fn cip36_registrations( let stake_hash: HexEncodedHash28 = match cip19_stake_address.try_into() { Ok(stake_hash) => stake_hash, Err(err) => { - return response::AllRegistration::unprocessable_content(vec![ - poem::Error::from_string( - format!("Stake addr to stake hash conversion error: {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - ), - ]); + return AllRegistration::handle_error(&anyhow::anyhow!( + "Given stake pub key is corrupt {:?}", + err + )); }, }; diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 1732120daac..7e2805edad6 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -4,7 +4,6 @@ use std::{cmp::Reverse, sync::Arc}; use futures::StreamExt; use poem::http::StatusCode; -use tracing::error; use super::{ cardano::{ @@ -45,16 +44,9 @@ pub(crate) async fn get_registration_given_stake_key_hash( { Ok(stake_addr) => stake_addr, Err(err) => { - error!( - id="get_registration_from_stake_key_hash_query_stake_addr", - error=?err, - "Failed to query stake addr from stake hash" - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query stake addr from stake hash {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::handle_error(&anyhow::anyhow!( + "Failed to query stake addr from stake hash {err:?}", + )); }, }; @@ -62,16 +54,9 @@ pub(crate) async fn get_registration_given_stake_key_hash( let row = match row_stake_addr { Ok(r) => r, Err(err) => { - error!( - id="get_registration_from_stake_key_hash_registration", - error=?err, - "Failed to get latest registration" - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to get stake addr from stake hash {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::handle_error(&anyhow::anyhow!( + "Failed to query stake addr from stake hash {err:?}", + )); }, }; @@ -80,10 +65,9 @@ pub(crate) async fn get_registration_given_stake_key_hash( let stake_pub_key = match Ed25519HexEncodedPublicKey::try_from(row.stake_address.clone()) { Ok(key) => key, Err(err) => { - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to type stake address {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::internal_error(&anyhow::anyhow!( + "Failed to type stake address {err:?}", + )); }, }; @@ -107,16 +91,9 @@ pub async fn get_registration_from_stake_addr( { Ok(registration) => registration, Err(err) => { - error!( - id="get_registration_from_stake_pub_key", - error=?err, - "Failed to query stake stake pub key" - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query stake stake pub key{err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::handle_error(&anyhow::anyhow!( + "Failed to query stake stake pub key {err:?}", + )); }, }; @@ -131,16 +108,9 @@ pub async fn get_registration_from_stake_addr( match get_registration_given_slot_no(registrations, &slot_no) { Ok(registration) => registration, Err(err) => { - error!( - id="get_registration_given_slot_no", - error=?err, - "Failed to get registration given slot no" - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to get registration given slot no {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::internal_error(&anyhow::anyhow!( + "Failed to get registration given slot no {err:?}", + )); }, } } else { @@ -148,16 +118,9 @@ pub async fn get_registration_from_stake_addr( match sort_latest_registration(registrations) { Ok(registration) => registration, Err(err) => { - error!( - id="get_latest_registration", - error=?err, - "Failed to sort latest registration" - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to sort latest registration {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::internal_error(&anyhow::anyhow!( + "Failed to sort latest registration {err:?}", + )); }, } }; @@ -165,17 +128,15 @@ pub async fn get_registration_from_stake_addr( // Registration found, now find invalids. let slot_no = registration.clone().slot_no; let Some(stake_pub_key) = registration.clone().stake_pub_key else { - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Stake pub key not in registration {stake_pub_key:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::internal_error(&anyhow::anyhow!( + "Stake pub key not in registration {stake_pub_key:?}", + )); }; let Some(vote_pub_key) = registration.clone().vote_pub_key else { - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Vote pub key not in registration {stake_pub_key:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::internal_error(&anyhow::anyhow!( + "Vote pub key not in registration {stake_pub_key:?}", + )); }; // include any erroneous registrations which occur AFTER the slot# of the last valid @@ -184,18 +145,9 @@ pub async fn get_registration_from_stake_addr( match get_invalid_registrations(stake_pub_key, Some(slot_no.clone()), session).await { Ok(invalids) => invalids, Err(err) => { - error!( - id="get_registration_from_stake_key_hash_invalid_registrations_lookup", - error=?err, - "Failed to obtain invalid registrations for given stake pub key", - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!( - "Failed to obtain invalid registrations for given stake pub key {err:?}" - ), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::handle_error(&anyhow::anyhow!( + "Failed to obtain invalid registrations for given stake pub key {err:?}", + )); }, }; @@ -237,9 +189,12 @@ fn cross_reference_key( async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute(session, GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }) + let mut registrations_iter = GetRegistrationQuery::execute( + session, + GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }, + ) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { @@ -343,10 +298,9 @@ pub(crate) async fn get_registration_given_vote_key( let voting_key: Vec = match vote_key.clone().try_into() { Ok(vote_key) => vote_key, Err(err) => { - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to convert vote key to bytes {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::internal_error(&anyhow::anyhow!( + "Failed to convert vote key to bytes {err:?}", + )); }, }; @@ -359,15 +313,9 @@ pub(crate) async fn get_registration_given_vote_key( { Ok(stake_addr) => stake_addr, Err(err) => { - error!( - id="get_associated_vote_key_registrations_query_stake_addr_from_vote_key", - error=?err, - "Failed to query stake addr from vote key" - ); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query stake addr from vote key {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::handle_error(&anyhow::anyhow!( + "Failed to query stake addr from vote key {err:?}", + )); }, }; @@ -375,15 +323,9 @@ pub(crate) async fn get_registration_given_vote_key( let row = match row_stake_addr { Ok(r) => r, Err(err) => { - error!( - id="get_associated_vote_key_registrations_latest_registration", - error=?err, - "Failed to get latest registration" - ); - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query stake addr from vote key {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::handle_error(&anyhow::anyhow!( + "Failed to query stake addr from vote key {err:?}", + )); }, }; @@ -392,10 +334,9 @@ pub(crate) async fn get_registration_given_vote_key( let stake_pub_key = match Ed25519HexEncodedPublicKey::try_from(row.stake_address.clone()) { Ok(key) => key, Err(err) => { - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to type stake address {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::internal_error(&anyhow::anyhow!( + "Failed to type stake address {err:?}", + )); }, }; @@ -415,16 +356,7 @@ pub async fn snapshot(session: Arc, slot_no: Option) - let all_stakes_and_vote_keys = match get_all_stake_addrs_and_vote_keys(&session.clone()).await { Ok(key_pairs) => key_pairs, Err(err) => { - error!( - id="get_all_stake_and_vote_keys", - error=?err, - "Failed to obtain all" - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query ALL {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::handle_error(&anyhow::anyhow!("Failed to query ALL {err:?}",)); }, }; @@ -441,10 +373,9 @@ pub async fn snapshot(session: Arc, slot_no: Option) - { Ok(registrations) => registrations, Err(err) => { - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to query ALL {err:?}"), - StatusCode::UNPROCESSABLE_ENTITY, - )]) + return AllRegistration::handle_error(&anyhow::anyhow!( + "Failed to query ALL {err:?}", + )); }, }; @@ -488,16 +419,9 @@ pub async fn snapshot(session: Arc, slot_no: Option) - { Ok(invalids) => invalids, Err(err) => { - error!( - id="get_invalid_registrations_snapshot", - error=?err, - "Failed to obtain invalid registrations for given stake addr in snapshot", - ); - - return AllRegistration::unprocessable_content(vec![poem::Error::from_string( - format!("Failed to obtain invalid registrations for given stake addr {err:?} in snapshot"), - StatusCode::UNPROCESSABLE_ENTITY, - )]); + return AllRegistration::handle_error(&anyhow::anyhow!( + "Failed to obtain invalid registrations for given stake addr {err:?} in snapshot", + )); }, }; all_invalids_after_filtering.push(invalids_report); From 1b6f0f6dd817b4dc766df8e821ed59768321b3f0 Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 13 Jan 2025 15:05:40 +0000 Subject: [PATCH 42/46] refactor(improved error handling): bubble up rather than log --- .../bin/src/service/api/cardano/cip36/filter.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index 7e2805edad6..d8124f6061b 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -189,12 +189,9 @@ fn cross_reference_key( async fn get_all_registrations_from_stake_pub_key( session: &Arc, stake_pub_key: Ed25519HexEncodedPublicKey, ) -> Result, anyhow::Error> { - let mut registrations_iter = GetRegistrationQuery::execute( - session, - GetRegistrationParams { - stake_address: stake_pub_key.try_into()?, - }, - ) + let mut registrations_iter = GetRegistrationQuery::execute(session, GetRegistrationParams { + stake_address: stake_pub_key.try_into()?, + }) .await?; let mut registrations = Vec::new(); while let Some(row) = registrations_iter.next().await { From 80eb55eac34922e6624aee75f98d106190de56da Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 13 Jan 2025 15:17:26 +0000 Subject: [PATCH 43/46] refactor(improved error handling): bubble up rather than log --- .../queries/registrations/get_all_stakes_and_vote_keys.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs index d3593872942..2143cdedd48 100644 --- a/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs +++ b/catalyst-gateway/bin/src/db/index/queries/registrations/get_all_stakes_and_vote_keys.rs @@ -42,11 +42,9 @@ impl GetAllStakesAndVoteKeysQuery { ) .await; - if let Err(ref error) = get_all_stake_and_vote_keys { - error!(error=%error, "Failed to prepare get all (stake addrs, vote_keys)"); - }; - - get_all_stake_and_vote_keys + get_all_stake_and_vote_keys.inspect_err( + |error| error!(error=%error, "Failed to prepare get all (stake addrs, vote_keys)"), + ) } /// Executes get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)] From 78a54c5e61dc94ec3b5f86ed7a5e0a4479a5c92c Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 13 Jan 2025 15:21:21 +0000 Subject: [PATCH 44/46] refactor(add test docs): cardano addresses context --- .../types/cardano/query/stake_or_voter.rs | 19 ++++---- .../types/generic/ed25519_public_key.rs | 43 ++++++++----------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs index 68d899fadef..30aea16ebe4 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs @@ -105,16 +105,14 @@ const FORMAT: &str = concatcp!( ); /// Schema. -static SCHEMA: LazyLock = LazyLock::new(|| { - MetaSchema { - title: Some(TITLE.to_owned()), - description: Some(DESCRIPTION), - example: Some(Value::String(EXAMPLE.to_string())), - max_length: Some(*MAX_LENGTH), - min_length: Some(*MIN_LENGTH), - pattern: Some(PATTERN.to_string()), - ..poem_openapi::registry::MetaSchema::ANY - } +static SCHEMA: LazyLock = LazyLock::new(|| MetaSchema { + title: Some(TITLE.to_owned()), + description: Some(DESCRIPTION), + example: Some(Value::String(EXAMPLE.to_string())), + max_length: Some(*MAX_LENGTH), + min_length: Some(*MIN_LENGTH), + pattern: Some(PATTERN.to_string()), + ..poem_openapi::registry::MetaSchema::ANY }); /// Either a Stake Address or a ED25519 Public key. @@ -210,6 +208,7 @@ mod tests { #[test] fn hex_to_stake_or_voter() { + // https://cexplorer.io/article/understanding-cardano-addresses assert!(StakeAddressOrPublicKey::try_from( "stake1u94ullc9nj9gawc08990nx8hwgw80l9zpmr8re44kydqy9cdjq6rq", ) diff --git a/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs b/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs index d3bcf7d2b98..c959bed8be4 100644 --- a/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs +++ b/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs @@ -34,16 +34,14 @@ pub(crate) const PATTERN: &str = "0x[A-Fa-f0-9]{64}"; pub(crate) const FORMAT: &str = "hex:ed25519-public-key"; /// Schema -static SCHEMA: LazyLock = LazyLock::new(|| { - MetaSchema { - title: Some(TITLE.to_owned()), - description: Some(DESCRIPTION), - example: Some(Value::String(EXAMPLE.to_string())), - max_length: Some(ENCODED_LENGTH), - min_length: Some(ENCODED_LENGTH), - pattern: Some(PATTERN.to_string()), - ..poem_openapi::registry::MetaSchema::ANY - } +static SCHEMA: LazyLock = LazyLock::new(|| MetaSchema { + title: Some(TITLE.to_owned()), + description: Some(DESCRIPTION), + example: Some(Value::String(EXAMPLE.to_string())), + max_length: Some(ENCODED_LENGTH), + min_length: Some(ENCODED_LENGTH), + pattern: Some(PATTERN.to_string()), + ..poem_openapi::registry::MetaSchema::ANY }); /// Because ALL the constraints are defined above, we do not ever need to define them in @@ -74,21 +72,15 @@ impl Ed25519HexEncodedPublicKey { /// Extra examples of 32 bytes ED25519 Public Key. pub(crate) fn examples(index: usize) -> Self { match index { - 0 => { - Self( - "0xDEF855AE45F3BF9640A5298A38B97617DE75600F796F17579BFB815543624B24".to_owned(), - ) - }, - 1 => { - Self( - "0x83B3B55589797EF953E24F4F0DBEE4D50B6363BCF041D15F6DBD33E014E54711".to_owned(), - ) - }, - _ => { - Self( - "0xA3E52361AFDE840918E2589DBAB9967C8027FB4431E83D36E338748CD6E3F820".to_owned(), - ) - }, + 0 => Self( + "0xDEF855AE45F3BF9640A5298A38B97617DE75600F796F17579BFB815543624B24".to_owned(), + ), + 1 => Self( + "0x83B3B55589797EF953E24F4F0DBEE4D50B6363BCF041D15F6DBD33E014E54711".to_owned(), + ), + _ => Self( + "0xA3E52361AFDE840918E2589DBAB9967C8027FB4431E83D36E338748CD6E3F820".to_owned(), + ), } } } @@ -148,6 +140,7 @@ mod tests { #[test] fn hex_to_pub_key() { + // https://cexplorer.io/article/understanding-cardano-addresses assert!(Ed25519HexEncodedPublicKey::try_from( "0x76e7ac0e460b6cdecea4be70479dab13c4adbd117421259a9b36caac007394de".to_string(), ) From 63bb5a017d48da2aecd514fc5539ba2c9f24157d Mon Sep 17 00:00:00 2001 From: cong-or Date: Mon, 13 Jan 2025 15:21:58 +0000 Subject: [PATCH 45/46] refactor(add test docs): cardano addresses context --- .../types/cardano/query/stake_or_voter.rs | 18 ++++---- .../types/generic/ed25519_public_key.rs | 42 +++++++++++-------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs index 30aea16ebe4..b1fd32b2517 100644 --- a/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs +++ b/catalyst-gateway/bin/src/service/common/types/cardano/query/stake_or_voter.rs @@ -105,14 +105,16 @@ const FORMAT: &str = concatcp!( ); /// Schema. -static SCHEMA: LazyLock = LazyLock::new(|| MetaSchema { - title: Some(TITLE.to_owned()), - description: Some(DESCRIPTION), - example: Some(Value::String(EXAMPLE.to_string())), - max_length: Some(*MAX_LENGTH), - min_length: Some(*MIN_LENGTH), - pattern: Some(PATTERN.to_string()), - ..poem_openapi::registry::MetaSchema::ANY +static SCHEMA: LazyLock = LazyLock::new(|| { + MetaSchema { + title: Some(TITLE.to_owned()), + description: Some(DESCRIPTION), + example: Some(Value::String(EXAMPLE.to_string())), + max_length: Some(*MAX_LENGTH), + min_length: Some(*MIN_LENGTH), + pattern: Some(PATTERN.to_string()), + ..poem_openapi::registry::MetaSchema::ANY + } }); /// Either a Stake Address or a ED25519 Public key. diff --git a/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs b/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs index c959bed8be4..c771bf657c7 100644 --- a/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs +++ b/catalyst-gateway/bin/src/service/common/types/generic/ed25519_public_key.rs @@ -34,14 +34,16 @@ pub(crate) const PATTERN: &str = "0x[A-Fa-f0-9]{64}"; pub(crate) const FORMAT: &str = "hex:ed25519-public-key"; /// Schema -static SCHEMA: LazyLock = LazyLock::new(|| MetaSchema { - title: Some(TITLE.to_owned()), - description: Some(DESCRIPTION), - example: Some(Value::String(EXAMPLE.to_string())), - max_length: Some(ENCODED_LENGTH), - min_length: Some(ENCODED_LENGTH), - pattern: Some(PATTERN.to_string()), - ..poem_openapi::registry::MetaSchema::ANY +static SCHEMA: LazyLock = LazyLock::new(|| { + MetaSchema { + title: Some(TITLE.to_owned()), + description: Some(DESCRIPTION), + example: Some(Value::String(EXAMPLE.to_string())), + max_length: Some(ENCODED_LENGTH), + min_length: Some(ENCODED_LENGTH), + pattern: Some(PATTERN.to_string()), + ..poem_openapi::registry::MetaSchema::ANY + } }); /// Because ALL the constraints are defined above, we do not ever need to define them in @@ -72,15 +74,21 @@ impl Ed25519HexEncodedPublicKey { /// Extra examples of 32 bytes ED25519 Public Key. pub(crate) fn examples(index: usize) -> Self { match index { - 0 => Self( - "0xDEF855AE45F3BF9640A5298A38B97617DE75600F796F17579BFB815543624B24".to_owned(), - ), - 1 => Self( - "0x83B3B55589797EF953E24F4F0DBEE4D50B6363BCF041D15F6DBD33E014E54711".to_owned(), - ), - _ => Self( - "0xA3E52361AFDE840918E2589DBAB9967C8027FB4431E83D36E338748CD6E3F820".to_owned(), - ), + 0 => { + Self( + "0xDEF855AE45F3BF9640A5298A38B97617DE75600F796F17579BFB815543624B24".to_owned(), + ) + }, + 1 => { + Self( + "0x83B3B55589797EF953E24F4F0DBEE4D50B6363BCF041D15F6DBD33E014E54711".to_owned(), + ) + }, + _ => { + Self( + "0xA3E52361AFDE840918E2589DBAB9967C8027FB4431E83D36E338748CD6E3F820".to_owned(), + ) + }, } } } From c6ef55e00bea90b9c3288c45a3d33a93a2bf5a22 Mon Sep 17 00:00:00 2001 From: cong-or Date: Tue, 14 Jan 2025 09:59:24 +0000 Subject: [PATCH 46/46] refactor(not found): response code --- .../bin/src/service/api/cardano/cip36/filter.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs index d8124f6061b..53c8cc3de33 100644 --- a/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs +++ b/catalyst-gateway/bin/src/service/api/cardano/cip36/filter.rs @@ -3,7 +3,6 @@ use std::{cmp::Reverse, sync::Arc}; use futures::StreamExt; -use poem::http::StatusCode; use super::{ cardano::{ @@ -73,10 +72,8 @@ pub(crate) async fn get_registration_given_stake_key_hash( return get_registration_from_stake_addr(stake_pub_key, asat, session, None).await; } - AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Stake hash does not exist", - StatusCode::UNPROCESSABLE_ENTITY, - )]) + + AllRegistration::With(Cip36Registration::NotFound) } /// Get registration from stake addr @@ -341,10 +338,7 @@ pub(crate) async fn get_registration_given_vote_key( .await; } - AllRegistration::unprocessable_content(vec![poem::Error::from_string( - "Vote key does not exist", - StatusCode::UNPROCESSABLE_ENTITY, - )]) + AllRegistration::With(Cip36Registration::NotFound) } /// ALL