Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cat-voices): cip36 registrations consolidated endpoint #1494

Merged
merged 49 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
7c08bf0
refactor(ignore): wip
cong-or Jan 4, 2025
9200bc5
refactor(cip36): remove legacy endpoints
cong-or Jan 5, 2025
6a241a7
refactor(stake addr): get registration by latest or slot no
cong-or Jan 5, 2025
17d074c
refactor(fix types): incoming stake pub key conversion
cong-or Jan 6, 2025
f1cb7e5
refactor(invalid registrations): fix
cong-or Jan 6, 2025
a969fe0
refactor(fix as at time from query string parameter ): asat
cong-or Jan 6, 2025
8c25f7c
refactor(voting key): cross reference activity with stake addr
cong-or Jan 6, 2025
648893e
refactor(voting key): cross reference activity with stake addr
cong-or Jan 6, 2025
3cdf422
refactor(slot no to big int): into
cong-or Jan 6, 2025
4896250
refactor(snapshot): auth
cong-or Jan 7, 2025
a65bb81
refactor(snapshot): auth
cong-or Jan 7, 2025
14791fc
refactor(query all): snapshot
cong-or Jan 7, 2025
0252f8c
refactor(query all): snapshot
cong-or Jan 8, 2025
b808898
refactor(query all): snapshot
cong-or Jan 8, 2025
33966f6
refactor(query all): snapshot
cong-or Jan 8, 2025
a062f81
refactor(query all): snapshot
cong-or Jan 8, 2025
f54ac6c
refactor(query all): snapshot
cong-or Jan 8, 2025
41f9c0a
refactor(query all): snapshot
cong-or Jan 8, 2025
c7a1fb7
refactor(query all): snapshot
cong-or Jan 8, 2025
750cae0
refactor(query all): snapshot
cong-or Jan 8, 2025
25ddf95
refactor(query all): snapshot
cong-or Jan 8, 2025
542763d
refactor(query all): snapshot
cong-or Jan 8, 2025
7f1d5af
refactor(query all): snapshot
cong-or Jan 8, 2025
779c94d
refactor(query all): snapshot
cong-or Jan 8, 2025
0af5904
refactor(query all): snapshot
cong-or Jan 8, 2025
ee6effc
refactor(query all): snapshot
cong-or Jan 8, 2025
4bab7ea
refactor(query all): snapshot
cong-or Jan 8, 2025
85f9149
refactor(query all): snapshot
cong-or Jan 8, 2025
571f4d8
refactor(query all): snapshot
cong-or Jan 9, 2025
7323501
refactor(query all): snapshot
cong-or Jan 9, 2025
fe928ae
refactor(query all): snapshot
cong-or Jan 9, 2025
ac52d82
refactor(query all): snapshot
cong-or Jan 9, 2025
7734f10
refactor(query all): snapshot
cong-or Jan 9, 2025
e9ba17d
refactor(query all): snapshot
cong-or Jan 9, 2025
164390b
refactor(query all): snapshot
cong-or Jan 9, 2025
a7f4a58
refactor(query all): snapshot
cong-or Jan 9, 2025
d348f67
Merge branch 'main' into f14-cip36
cong-or Jan 9, 2025
bc5bdae
refactor(housekeeping): local
cong-or Jan 9, 2025
a718f1f
refactor(housekeeping): local
cong-or Jan 9, 2025
6065f2f
refactor(housekeeping): spellcheck
cong-or Jan 9, 2025
acfb7be
refactor(no auth): rm auth
cong-or Jan 9, 2025
0a2286d
refactor(improved error handling): bubble up rather than log
cong-or Jan 13, 2025
1b6f0f6
refactor(improved error handling): bubble up rather than log
cong-or Jan 13, 2025
80eb55e
refactor(improved error handling): bubble up rather than log
cong-or Jan 13, 2025
867f1eb
Merge branch 'main' into f14-cip36
cong-or Jan 13, 2025
78a54c5
refactor(add test docs): cardano addresses context
cong-or Jan 13, 2025
63bb5a0
refactor(add test docs): cardano addresses context
cong-or Jan 13, 2025
8a33736
Merge branch 'main' into f14-cip36
cong-or Jan 13, 2025
c6ef55e
refactor(not found): response code
cong-or Jan 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,5 @@ xpub
xpublic
xvfb
yoroi
stake1u94ullc9nj9gawc08990nx8hwgw80l9zpmr8re44kydqy9cdjq6rq
invalid1u9nlq5nmuzthw3vhgakfpxyq4r0zl2c0p8uqy24gpyjsa6c3df4h6
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SELECT
stake_address,vote_key
FROM cip36_registration;
13 changes: 12 additions & 1 deletion catalyst-gateway/bin/src/db/index/queries/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use rbac::{
get_role0_chain_root::GetRole0ChainRootQuery,
};
use registrations::{
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,
};
Expand Down Expand Up @@ -96,6 +97,8 @@ 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`)
GetAllStakesAndVoteKeys,
}

/// All prepared UPSERT query statements (inserts/updates a single value of data).
Expand Down Expand Up @@ -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 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
Expand Down Expand Up @@ -193,7 +198,9 @@ 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_stakes_and_vote_keys_query =
GetAllStakesAndVoteKeysQuery::prepare(session).await;

let (
txo_insert_queries,
Expand Down Expand Up @@ -241,6 +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_stakes_and_vote_keys_query: get_all_stakes_and_vote_keys_query?,
})
}

Expand Down Expand Up @@ -331,6 +339,9 @@ impl PreparedQueries {
&self.registrations_by_chain_root_query
},
PreparedSelectQuery::ChainRootByRole0Key => &self.chain_root_by_role0_key_query,
PreparedSelectQuery::GetAllStakesAndVoteKeys => {
&self.get_all_stakes_and_vote_keys_query
},
};

session
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! 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;

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,vote` keys)
/// [(`stake_addr,vote_key`)]
const GET_ALL_STAKES_AND_VOTE_KEYS: &str = include_str!("../cql/get_all_stake_addrs.cql");

/// Get all stake and vote keys from cip36 registration
#[derive(SerializeRow)]
pub(crate) struct GetAllStakesAndVoteKeysParams {}

/// Get stakes and vote keys for snapshot.
#[derive(DeserializeRow)]
pub(crate) struct GetAllStakesAndVoteKeysQuery {
/// Full Stake Address (not hashed, 32 byte ED25519 Public key).
pub stake_address: Vec<u8>,
/// Voting Public Key
pub vote_key: Vec<u8>,
}

impl GetAllStakesAndVoteKeysQuery {
/// Prepares get all `stake_addr` paired with vote keys [(`stake_addr,vote_key`)]
pub(crate) async fn prepare(session: Arc<Session>) -> anyhow::Result<PreparedStatement> {
let get_all_stake_and_vote_keys = PreparedQueries::prepare(
session,
GET_ALL_STAKES_AND_VOTE_KEYS,
scylla::statement::Consistency::All,
true,
)
.await;

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`)]
pub(crate) async fn execute(
session: &CassandraSession, params: GetAllStakesAndVoteKeysParams,
) -> anyhow::Result<TypedRowStream<GetAllStakesAndVoteKeysQuery>> {
let iter = session
.execute_iter(PreparedSelectQuery::GetAllStakesAndVoteKeys, params)
.await?
.rows_stream::<GetAllStakesAndVoteKeysQuery>()?;

Ok(iter)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ pub(crate) struct GetRegistrationParams {
pub stake_address: Vec<u8>,
}

impl From<&ed25519_dalek::VerifyingKey> for GetRegistrationParams {
fn from(value: &ed25519_dalek::VerifyingKey) -> Self {
impl From<Vec<u8>> for GetRegistrationParams {
fn from(value: Vec<u8>) -> Self {
GetRegistrationParams {
stake_address: value.as_bytes().to_vec(),
stake_address: value,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -28,12 +31,10 @@ pub(crate) struct GetInvalidRegistrationParams {

impl GetInvalidRegistrationParams {
/// Create a new instance of [`GetInvalidRegistrationParams`]
pub(crate) fn new(
stake_address: Vec<u8>, slot_no: num_bigint::BigInt,
) -> GetInvalidRegistrationParams {
pub(crate) fn new(stake_address: Vec<u8>, slot_no: SlotNo) -> GetInvalidRegistrationParams {
Self {
stake_address,
slot_no,
slot_no: slot_no.into(),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Registration related queries.
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
23 changes: 13 additions & 10 deletions catalyst-gateway/bin/src/db/index/tests/scylla_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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();
Expand Down
72 changes: 60 additions & 12 deletions catalyst-gateway/bin/src/service/api/cardano/cip36/endpoint.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,77 @@
//! Implementation of the GET `/cardano/cip36` endpoint

use std::time::Duration;

use poem::http::HeaderMap;
use tokio::time::sleep;
use tracing::error;

use self::cardano::{hash28::HexEncodedHash28, query::stake_or_voter::StakeAddressOrPublicKey};
use super::{
cardano::{self},
response, NoneOrRBAC, SlotNo,
filter::{get_registration_given_stake_key_hash, get_registration_given_vote_key, snapshot},
response, SlotNo,
};
use crate::{
db::index::session::CassandraSession,
service::{
api::cardano::cip36::response::AllRegistration,
common::{self, types::headers::retry_after::RetryAfterOption},
},
};
use crate::service::common::{self};

/// Process the endpoint operation
pub(crate) async fn cip36_registrations(
_lookup: Option<cardano::query::stake_or_voter::StakeOrVoter>, _asat: Option<SlotNo>,
lookup: Option<cardano::query::stake_or_voter::StakeOrVoter>, asat: Option<SlotNo>,
_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 {
// Dummy sleep, remove it
sleep(Duration::from_millis(1)).await;
let Some(session) = CassandraSession::get(true) else {
error!("Failed to acquire db session");
cong-or marked this conversation as resolved.
Show resolved Hide resolved
return AllRegistration::service_unavailable(
&anyhow::anyhow!("Failed to acquire db session"),
RetryAfterOption::Default,
);
};

if let Some(stake_or_voter) = lookup {
match StakeAddressOrPublicKey::from(stake_or_voter) {
StakeAddressOrPublicKey::Address(cip19_stake_address) => {
// Typically, a stake address will start with 'stake1',
// 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
// 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) => {
return AllRegistration::handle_error(&anyhow::anyhow!(
"Given stake pub key is corrupt {:?}",
err
));
},
};

// Todo: refactor the below into a single operation here.
return get_registration_given_stake_key_hash(stake_hash, session, asat).await;
},
StakeAddressOrPublicKey::PublicKey(ed25519_hex_encoded_public_key) => {
// As above...
// Except using a voting key.
return get_registration_given_vote_key(
ed25519_hex_encoded_public_key,
session,
asat,
)
.await;
},
StakeAddressOrPublicKey::All =>
// As above...
// Snapshot replacement, returns all registrations or returns a
// subset of registrations if constrained by a given time.
{
return snapshot(session, asat).await
},
};
};

// 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.
Expand Down
Loading
Loading