Skip to content

Commit

Permalink
feat: RBAC chain root and registrations queries (#1024)
Browse files Browse the repository at this point in the history
* feat: RBAC chain root and registrations queries

* chore: PR changes

* chore: fmt

* chore: fix checks

* chore: fix openapi lints

* chore: fix types

* chore: fix fmt

* chore: fix openapi lints

* chore: regenerate openapi sdk

---------

Co-authored-by: Steven Johnson <stevenj@users.noreply.github.com>
Co-authored-by: Oleksandr Prokhorenko <djminikin@gmail.com>
Co-authored-by: Joaquín Rosales <joaquin.rosales@iohk.io>
  • Loading branch information
4 people authored Oct 31, 2024
1 parent 6fd3020 commit 5a14f87
Show file tree
Hide file tree
Showing 17 changed files with 883 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SELECT chain_root
FROM chain_root_for_stake_addr
WHERE stake_addr = :stake_address
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SELECT transaction_id
FROM RBAC509_registration
WHERE chain_root = :chain_root
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SELECT chain_root
FROM chain_root_for_role0_key
WHERE role0_key = :role0_key
31 changes: 30 additions & 1 deletion catalyst-gateway/bin/src/db/index/queries/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//!
//! This improves query execution time.

pub(crate) mod rbac;
pub(crate) mod registrations;
pub(crate) mod staked_ada;
pub(crate) mod sync_status;
Expand All @@ -10,6 +11,10 @@ use std::{fmt::Debug, sync::Arc};

use anyhow::{bail, Context};
use crossbeam_skiplist::SkipMap;
use rbac::{
get_chain_root::GetChainRootQuery, get_registrations::GetRegistrationsByChainRootQuery,
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,
Expand Down Expand Up @@ -84,6 +89,12 @@ pub(crate) enum PreparedSelectQuery {
StakeAddrFromStakeHash,
/// Get stake addr from vote key
StakeAddrFromVoteKey,
/// Get chain root by stake address
ChainRootByStakeAddress,
/// Get registrations by chain root
RegistrationsByChainRoot,
/// Get chain root by role0 key
ChainRootByRole0Key,
}

/// All prepared UPSERT query statements (inserts/updates a single value of data).
Expand Down Expand Up @@ -139,6 +150,12 @@ pub(crate) struct PreparedQueries {
invalid_registrations_from_stake_addr_query: PreparedStatement,
/// Insert Sync Status update.
sync_status_insert: PreparedStatement,
/// Get chain root by stake address
chain_root_by_stake_address_query: PreparedStatement,
/// Get registrations by chain root
registrations_by_chain_root_query: PreparedStatement,
/// Get chain root by role0 key
chain_root_by_role0_key_query: PreparedStatement,
}

/// An individual query response that can fail
Expand Down Expand Up @@ -171,7 +188,11 @@ impl PreparedQueries {
let stake_addr_from_stake_hash = GetStakeAddrQuery::prepare(session.clone()).await;
let stake_addr_from_vote_key = GetStakeAddrFromVoteKeyQuery::prepare(session.clone()).await;
let invalid_registrations = GetInvalidRegistrationQuery::prepare(session.clone()).await;
let sync_status_insert = SyncStatusInsertQuery::prepare(session).await;
let sync_status_insert = SyncStatusInsertQuery::prepare(session.clone()).await;
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 (
txo_insert_queries,
Expand Down Expand Up @@ -216,6 +237,9 @@ impl PreparedQueries {
stake_addr_from_vote_key_query: stake_addr_from_vote_key?,
invalid_registrations_from_stake_addr_query: invalid_registrations?,
sync_status_insert: sync_status_insert?,
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?,
})
}

Expand Down Expand Up @@ -301,6 +325,11 @@ impl PreparedQueries {
PreparedSelectQuery::InvalidRegistrationsFromStakeAddr => {
&self.invalid_registrations_from_stake_addr_query
},
PreparedSelectQuery::ChainRootByStakeAddress => &self.chain_root_by_stake_address_query,
PreparedSelectQuery::RegistrationsByChainRoot => {
&self.registrations_by_chain_root_query
},
PreparedSelectQuery::ChainRootByRole0Key => &self.chain_root_by_role0_key_query,
};

session
Expand Down
72 changes: 72 additions & 0 deletions catalyst-gateway/bin/src/db/index/queries/rbac/get_chain_root.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! Get chain root by stake address.
use std::sync::Arc;

use scylla::{
prepared_statement::PreparedStatement, transport::iterator::TypedRowIterator, SerializeRow,
Session,
};
use tracing::error;

use crate::db::index::{
queries::{PreparedQueries, PreparedSelectQuery},
session::CassandraSession,
};

/// Get get chain root by stake address query string.
const GET_CHAIN_ROOT: &str = include_str!("../cql/get_chain_root.cql");

/// Get chain root by stake address query params.
#[derive(SerializeRow)]
pub(crate) struct GetChainRootQueryParams {
/// Stake address to get the chain root for.
pub(crate) stake_address: Vec<u8>,
}

/// Get chain root by stake address query row result
// TODO: https://github.com/input-output-hk/catalyst-voices/issues/828
// The macro uses expect to signal an error in deserializing values.
#[allow(clippy::expect_used)]
mod result {
use scylla::FromRow;

/// Get chain root query result.
#[derive(FromRow)]
pub(crate) struct GetChainRootQuery {
/// Chain root for the queries stake address.
pub(crate) chain_root: Vec<u8>,
}
}

/// Get chain root by stake address query.
pub(crate) struct GetChainRootQuery;

impl GetChainRootQuery {
/// Prepares a get chain root by stake address query.
pub(crate) async fn prepare(session: Arc<Session>) -> anyhow::Result<PreparedStatement> {
let get_chain_root_by_stake_address_query = PreparedQueries::prepare(
session,
GET_CHAIN_ROOT,
scylla::statement::Consistency::All,
true,
)
.await;

if let Err(ref error) = get_chain_root_by_stake_address_query {
error!(error=%error, "Failed to prepare get chain root by stake address query");
};

get_chain_root_by_stake_address_query
}

/// Executes a get chain root by stake address query.
pub(crate) async fn execute(
session: &CassandraSession, params: GetChainRootQueryParams,
) -> anyhow::Result<TypedRowIterator<result::GetChainRootQuery>> {
let iter = session
.execute_iter(PreparedSelectQuery::ChainRootByStakeAddress, params)
.await?
.into_typed::<result::GetChainRootQuery>();

Ok(iter)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Get registrations by chain root.
use std::sync::Arc;

use scylla::{
prepared_statement::PreparedStatement, transport::iterator::TypedRowIterator, SerializeRow,
Session,
};
use tracing::error;

use crate::db::index::{
queries::{PreparedQueries, PreparedSelectQuery},
session::CassandraSession,
};

/// Get get chain root by stake address query string.
const GET_REGISTRATIONS_BY_CHAIN_ROOT_CQL: &str =
include_str!("../cql/get_registrations_by_chain_root.cql");

/// Get chain root by stake address query params.
#[derive(SerializeRow)]
pub(crate) struct GetRegistrationsByChainRootQueryParams {
/// Chain root to get registrations for.
pub(crate) chain_root: Vec<u8>,
}

/// Get registrations by chain root query row result
// TODO: https://github.com/input-output-hk/catalyst-voices/issues/828
// The macro uses expect to signal an error in deserializing values.
#[allow(clippy::expect_used)]
mod result {
use scylla::FromRow;

/// Get chain root query result.
#[derive(FromRow)]
pub(crate) struct GetRegistrationsByChainRootQuery {
/// Registration transaction id.
pub(crate) transaction_id: Vec<u8>,
}
}

/// Get chain root by stake address query.
pub(crate) struct GetRegistrationsByChainRootQuery;

impl GetRegistrationsByChainRootQuery {
/// Prepares a get registrations by chain root query.
pub(crate) async fn prepare(session: Arc<Session>) -> anyhow::Result<PreparedStatement> {
let get_registrations_by_chain_root_query = PreparedQueries::prepare(
session,
GET_REGISTRATIONS_BY_CHAIN_ROOT_CQL,
scylla::statement::Consistency::All,
true,
)
.await;

if let Err(ref error) = get_registrations_by_chain_root_query {
error!(error=%error, "Failed to prepare get registrations by chain root query");
};

get_registrations_by_chain_root_query
}

/// Executes a get registrations by chain root query.
pub(crate) async fn execute(
session: &CassandraSession, params: GetRegistrationsByChainRootQueryParams,
) -> anyhow::Result<TypedRowIterator<result::GetRegistrationsByChainRootQuery>> {
let iter = session
.execute_iter(PreparedSelectQuery::RegistrationsByChainRoot, params)
.await?
.into_typed::<result::GetRegistrationsByChainRootQuery>();

Ok(iter)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! Get chain root by role0 key.
use std::sync::Arc;

use scylla::{
prepared_statement::PreparedStatement, transport::iterator::TypedRowIterator, SerializeRow,
Session,
};
use tracing::error;

use crate::db::index::{
queries::{PreparedQueries, PreparedSelectQuery},
session::CassandraSession,
};

/// Get chain root by role0 key query string.
const GET_ROLE0_KEY_CHAIN_ROOT_CQL: &str = include_str!("../cql/get_role0_key_chain_root.cql");

/// Get chain root by role0 key query params.
#[derive(SerializeRow)]
pub(crate) struct GetRole0ChainRootQueryParams {
/// Role0 key to get the chain root for.
pub(crate) role0_key: Vec<u8>,
}

/// Get chain root by role0 key query row result
// TODO: https://github.com/input-output-hk/catalyst-voices/issues/828
// The macro uses expect to signal an error in deserializing values.
#[allow(clippy::expect_used)]
mod result {
use scylla::FromRow;

/// Get role0 key chain root query result.
#[derive(FromRow)]
pub(crate) struct GetRole0ChainRootQuery {
/// Chain root.
pub(crate) chain_root: Vec<u8>,
}
}

/// Get chain root by role0 key query.
pub(crate) struct GetRole0ChainRootQuery;

impl GetRole0ChainRootQuery {
/// Prepares a get chain root by role0 key query.
pub(crate) async fn prepare(session: Arc<Session>) -> anyhow::Result<PreparedStatement> {
let get_chain_root_by_role0_key_query = PreparedQueries::prepare(
session,
GET_ROLE0_KEY_CHAIN_ROOT_CQL,
scylla::statement::Consistency::All,
true,
)
.await;

if let Err(ref error) = get_chain_root_by_role0_key_query {
error!(error=%error, "Failed to prepare get chain root by role0 key query");
};

get_chain_root_by_role0_key_query
}

/// Executes a get chain root role0 key query.
pub(crate) async fn execute(
session: &CassandraSession, params: GetRole0ChainRootQueryParams,
) -> anyhow::Result<TypedRowIterator<result::GetRole0ChainRootQuery>> {
let iter = session
.execute_iter(PreparedSelectQuery::ChainRootByRole0Key, params)
.await?
.into_typed::<result::GetRole0ChainRootQuery>();

Ok(iter)
}
}
4 changes: 4 additions & 0 deletions catalyst-gateway/bin/src/db/index/queries/rbac/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! RBAC queries.
pub(crate) mod get_chain_root;
pub(crate) mod get_registrations;
pub(crate) mod get_role0_chain_root;
51 changes: 51 additions & 0 deletions catalyst-gateway/bin/src/service/api/cardano/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::service::{

mod cip36;
mod date_time_to_slot_number_get;
mod rbac;
mod registration_get;
mod staked_ada_get;
mod sync_state_get;
Expand Down Expand Up @@ -188,4 +189,54 @@ impl CardanoApi {
) -> cip36::MultipleRegistrationResponse {
cip36::get_associated_vote_key_registrations(vote_key.0, true).await
}

#[oai(
path = "/draft/rbac/chain_root/:stake_address",
method = "get",
operation_id = "rbacChainRootGet"
)]
/// Get RBAC chain root
///
/// This endpoint returns the RBAC certificate chain root for a given stake address.
async fn rbac_chain_root_get(
&self,
/// Stake address to get the chain root for.
Path(stake_address): Path<Cip19StakeAddress>,
) -> rbac::chain_root_get::AllResponses {
rbac::chain_root_get::endpoint(stake_address).await
}

#[oai(
path = "/draft/rbac/registrations/:chain_root",
method = "get",
operation_id = "rbacRegistrations"
)]
/// Get registrations by RBAC chain root
///
/// This endpoint returns the registrations for a given chain root.
async fn rbac_registrations_get(
&self,
/// Chain root to get the registrations for.
#[oai(validator(max_length = 66, min_length = 64, pattern = "0x[0-9a-f]{64}"))]
Path(chain_root): Path<String>,
) -> rbac::registrations_get::AllResponses {
rbac::registrations_get::endpoint(chain_root).await
}

#[oai(
path = "/draft/rbac/role0_chain_root/:role0_key",
method = "get",
operation_id = "rbacRole0KeyChainRoot"
)]
/// Get RBAC chain root for a given role0 key.
///
/// This endpoint returns the RBAC certificate chain root for a given role 0 key.
async fn rbac_role0_key_chain_root(
&self,
/// Role0 key to get the chain root for.
#[oai(validator(min_length = 34, max_length = 34, pattern = "0x[0-9a-f]{32}"))]
Path(role0_key): Path<String>,
) -> rbac::role0_chain_root_get::AllResponses {
rbac::role0_chain_root_get::endpoint(role0_key).await
}
}
Loading

0 comments on commit 5a14f87

Please sign in to comment.