cat_gateway/rbac/
provider.rs

1//! An implementation of the `rbac_registration::cardano::state::RbacChainsState` trait
2
3use anyhow::Context;
4use cardano_chain_follower::{StakeAddress, hashes::TransactionId};
5use catalyst_types::catalyst_id::CatalystId;
6use ed25519_dalek::VerifyingKey;
7use futures::StreamExt;
8
9use crate::{
10    db::index::session::CassandraSession,
11    rbac::{
12        RbacBlockIndexingContext,
13        chains_cache::cached_persistent_rbac_chain,
14        get_chain::{apply_regs, build_rbac_chain, persistent_rbac_chain},
15        latest_rbac_chain,
16    },
17};
18
19/// A helper struct to handle RBAC related state from the DB and caches.
20/// Implements `rbac_registration::cardano::state::RbacChainsState` trait.
21pub(crate) struct RbacChainsProvider<'a> {
22    /// `index-db` corresponding flag
23    is_persistent: bool,
24    /// `RbacBlockIndexingContext` reference
25    context: &'a RbacBlockIndexingContext,
26}
27
28impl<'a> RbacChainsProvider<'a> {
29    /// Creates a new instance of `RbacChainsState`
30    pub fn new(
31        is_persistent: bool,
32        context: &'a RbacBlockIndexingContext,
33    ) -> Self {
34        Self {
35            is_persistent,
36            context,
37        }
38    }
39
40    /// Returns a Catalyst ID corresponding to the given transaction hash.
41    pub async fn catalyst_id_from_txn_id(
42        &self,
43        txn_id: TransactionId,
44    ) -> anyhow::Result<Option<CatalystId>> {
45        use crate::db::index::queries::rbac::get_catalyst_id_from_transaction_id::Query;
46
47        // Check the context first.
48        if let Some(catalyst_id) = self.context.find_transaction(&txn_id) {
49            return Ok(Some(catalyst_id.to_owned()));
50        }
51
52        // Then try to find in the persistent database.
53        let session =
54            CassandraSession::get(true).context("Failed to get Cassandra persistent session")?;
55        if let Some(id) = Query::get(&session, txn_id).await? {
56            return Ok(Some(id));
57        }
58
59        // Conditionally check the volatile database.
60        if !self.is_persistent {
61            let session =
62                CassandraSession::get(false).context("Failed to get Cassandra volatile session")?;
63            return Query::get(&session, txn_id).await;
64        }
65
66        Ok(None)
67    }
68}
69
70impl rbac_registration::cardano::provider::RbacChainsProvider for RbacChainsProvider<'_> {
71    async fn chain(
72        &self,
73        id: &CatalystId,
74    ) -> anyhow::Result<Option<rbac_registration::registration::cardano::RegistrationChain>> {
75        let chain = if self.is_persistent {
76            persistent_rbac_chain(id).await?
77        } else {
78            latest_rbac_chain(id).await?.map(|i| i.chain)
79        };
80
81        // Apply additional registrations from context if any.
82        if let Some(regs) = self.context.find_registrations(id) {
83            let regs = regs.iter().cloned();
84            match chain {
85                Some(c) => return apply_regs(c, regs).await.map(Some),
86                None => return build_rbac_chain(regs).await,
87            }
88        }
89
90        Ok(chain)
91    }
92
93    async fn is_chain_known(
94        &self,
95        id: &CatalystId,
96    ) -> anyhow::Result<bool> {
97        if self.context.find_registrations(id).is_some() {
98            return Ok(true);
99        }
100
101        let session =
102            CassandraSession::get(true).context("Failed to get Cassandra persistent session")?;
103
104        // We only cache persistent chains, so it is ok to check the cache regardless of the
105        // `is_persistent` parameter value.
106        if cached_persistent_rbac_chain(&session, id).is_some() {
107            return Ok(true);
108        }
109
110        if is_cat_id_known(&session, id).await? {
111            return Ok(true);
112        }
113
114        // Conditionally check the volatile database.
115        if !self.is_persistent {
116            let session =
117                CassandraSession::get(false).context("Failed to get Cassandra volatile session")?;
118            if is_cat_id_known(&session, id).await? {
119                return Ok(true);
120            }
121        }
122
123        Ok(false)
124    }
125
126    async fn chain_catalyst_id_from_stake_address(
127        &self,
128        address: &StakeAddress,
129    ) -> anyhow::Result<Option<CatalystId>> {
130        use crate::db::index::queries::rbac::get_catalyst_id_from_stake_address::Query;
131
132        // Check the context first.
133        if let Some(catalyst_id) = self.context.find_address(address) {
134            return Ok(Some(catalyst_id.to_owned()));
135        }
136
137        // Then try to find in the persistent database.
138        let session =
139            CassandraSession::get(true).context("Failed to get Cassandra persistent session")?;
140        if let Some(id) = Query::latest(&session, address).await? {
141            return Ok(Some(id));
142        }
143
144        // Conditionally check the volatile database.
145        if !self.is_persistent {
146            let session =
147                CassandraSession::get(false).context("Failed to get Cassandra volatile session")?;
148            return Query::latest(&session, address).await;
149        }
150
151        Ok(None)
152    }
153
154    async fn chain_catalyst_id_from_signing_public_key(
155        &self,
156        key: &VerifyingKey,
157    ) -> anyhow::Result<Option<CatalystId>> {
158        use crate::db::index::queries::rbac::get_catalyst_id_from_public_key::Query;
159
160        // Check the context first.
161        if let Some(catalyst_id) = self.context.find_public_key(key) {
162            return Ok(Some(catalyst_id.to_owned()));
163        }
164
165        // Then try to find in the persistent database.
166        let session =
167            CassandraSession::get(true).context("Failed to get Cassandra persistent session")?;
168        if let Some(id) = Query::get(&session, *key).await? {
169            return Ok(Some(id));
170        }
171
172        // Conditionally check the volatile database.
173        if !self.is_persistent {
174            let session =
175                CassandraSession::get(false).context("Failed to get Cassandra volatile session")?;
176            return Query::get(&session, *key).await;
177        }
178
179        Ok(None)
180    }
181}
182
183/// Returns `true` if there is at least one registration with the given Catalyst ID.
184async fn is_cat_id_known(
185    session: &CassandraSession,
186    id: &CatalystId,
187) -> anyhow::Result<bool> {
188    use crate::db::index::queries::rbac::get_rbac_registrations::{Query, QueryParams};
189
190    Ok(Query::execute(session, QueryParams {
191        catalyst_id: id.clone().into(),
192    })
193    .await?
194    .next()
195    .await
196    .is_some())
197}