cat_gateway/db/index/block/cip36/
mod.rs

1//! Index CIP-36 Registrations.
2
3pub(crate) mod insert_cip36;
4pub(crate) mod insert_cip36_for_vote_key;
5pub(crate) mod insert_cip36_invalid;
6
7use std::sync::Arc;
8
9use cardano_blockchain_types::{Cip36, MultiEraBlock, Slot, TxnIndex};
10use scylla::Session;
11
12use crate::{
13    db::index::{
14        queries::{FallibleQueryTasks, PreparedQuery, SizedBatch},
15        session::CassandraSession,
16    },
17    settings::cassandra_db,
18};
19
20/// Insert CIP-36 Registration Queries
21pub(crate) struct Cip36InsertQuery {
22    /// Stake Registration Data captured during indexing.
23    registrations: Vec<insert_cip36::Params>,
24    /// Stake Registration Data captured during indexing.
25    invalid: Vec<insert_cip36_invalid::Params>,
26    /// Stake Registration Data captured during indexing.
27    for_vote_key: Vec<insert_cip36_for_vote_key::Params>,
28}
29
30impl Cip36InsertQuery {
31    /// Create new data set for CIP-36 Registrations Insert Query Batch.
32    pub(crate) fn new() -> Self {
33        Cip36InsertQuery {
34            registrations: Vec::new(),
35            invalid: Vec::new(),
36            for_vote_key: Vec::new(),
37        }
38    }
39
40    /// Prepare Batch of Insert Cip36 Registration Data Queries
41    pub(crate) async fn prepare_batch(
42        session: &Arc<Session>, cfg: &cassandra_db::EnvVars,
43    ) -> anyhow::Result<(SizedBatch, SizedBatch, SizedBatch)> {
44        let insert_cip36_batch = insert_cip36::Params::prepare_batch(session, cfg).await;
45        let insert_cip36_invalid_batch =
46            insert_cip36_invalid::Params::prepare_batch(session, cfg).await;
47        let insert_cip36_for_vote_key_addr_batch =
48            insert_cip36_for_vote_key::Params::prepare_batch(session, cfg).await;
49
50        Ok((
51            insert_cip36_batch?,
52            insert_cip36_invalid_batch?,
53            insert_cip36_for_vote_key_addr_batch?,
54        ))
55    }
56
57    /// Index the CIP-36 registrations in a transaction.
58    pub(crate) fn index(&mut self, index: TxnIndex, slot_no: Slot, block: &MultiEraBlock) {
59        // Catalyst strict is set to true
60        match Cip36::new(block, index, true) {
61            // Check for CIP-36 validity and should be strict catalyst (only 1 voting key)
62            // Note that in `validate_voting_keys` we already checked if the array has only one
63            Ok(Some(cip36)) if cip36.is_valid() && cip36.is_cip36().unwrap_or_default() => {
64                // This should always pass, because we already checked if the array has only one
65                if let Some(voting_key) = cip36.voting_pks().first() {
66                    self.registrations.push(insert_cip36::Params::new(
67                        voting_key, slot_no, index, &cip36,
68                    ));
69
70                    self.for_vote_key
71                        .push(insert_cip36_for_vote_key::Params::new(
72                            voting_key, slot_no, index, &cip36, true,
73                        ));
74                }
75            },
76            // Invalid CIP-36 Registration
77            Ok(Some(cip36)) if cip36.is_cip36().unwrap_or_default() => {
78                // Cannot index an invalid CIP36, if there is no stake public key.
79                if cip36.stake_pk().is_some() {
80                    if cip36.voting_pks().is_empty() {
81                        self.invalid.push(insert_cip36_invalid::Params::new(
82                            None, slot_no, index, &cip36,
83                        ));
84                    } else {
85                        for voting_key in cip36.voting_pks() {
86                            self.invalid.push(insert_cip36_invalid::Params::new(
87                                Some(voting_key),
88                                slot_no,
89                                index,
90                                &cip36,
91                            ));
92                            self.for_vote_key
93                                .push(insert_cip36_for_vote_key::Params::new(
94                                    voting_key, slot_no, index, &cip36, false,
95                                ));
96                        }
97                    }
98                }
99            },
100            _ => {},
101        }
102    }
103
104    /// Execute the CIP-36 Registration Indexing Queries.
105    ///
106    /// Consumes the `self` and returns a vector of futures.
107    pub(crate) fn execute(self, session: &Arc<CassandraSession>) -> FallibleQueryTasks {
108        let mut query_handles: FallibleQueryTasks = Vec::new();
109
110        if !self.registrations.is_empty() {
111            let inner_session = session.clone();
112            query_handles.push(tokio::spawn(async move {
113                inner_session
114                    .execute_batch(
115                        PreparedQuery::Cip36RegistrationInsertQuery,
116                        self.registrations,
117                    )
118                    .await
119            }));
120        }
121
122        if !self.invalid.is_empty() {
123            let inner_session = session.clone();
124            query_handles.push(tokio::spawn(async move {
125                inner_session
126                    .execute_batch(
127                        PreparedQuery::Cip36RegistrationInsertErrorQuery,
128                        self.invalid,
129                    )
130                    .await
131            }));
132        }
133
134        if !self.for_vote_key.is_empty() {
135            let inner_session = session.clone();
136            query_handles.push(tokio::spawn(async move {
137                inner_session
138                    .execute_batch(
139                        PreparedQuery::Cip36RegistrationForStakeAddrInsertQuery,
140                        self.for_vote_key,
141                    )
142                    .await
143            }));
144        }
145
146        query_handles
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153    use crate::db::index::tests::test_utils;
154
155    #[test]
156    fn index() {
157        let block = test_utils::block_2();
158        let mut query = Cip36InsertQuery::new();
159        query.index(0.into(), 0.into(), &block);
160        assert_eq!(1, query.registrations.len());
161        assert!(query.invalid.is_empty());
162        assert_eq!(1, query.for_vote_key.len());
163    }
164}