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, StakeAddress, TxnIndex};
10use catalyst_types::hashes::Blake2b224Hash;
11use scylla::Session;
12
13use super::certs;
14use crate::{
15    db::index::{
16        queries::{FallibleQueryTasks, PreparedQuery, SizedBatch},
17        session::CassandraSession,
18    },
19    settings::cassandra_db,
20};
21
22/// Insert CIP-36 Registration Queries
23pub(crate) struct Cip36InsertQuery {
24    /// Stake Registration Data captured during indexing.
25    registrations: Vec<insert_cip36::Params>,
26    /// Stake Registration Data captured during indexing.
27    invalid: Vec<insert_cip36_invalid::Params>,
28    /// Stake Registration Data captured during indexing.
29    for_vote_key: Vec<insert_cip36_for_vote_key::Params>,
30    /// Stake Registration Data captured during indexing.
31    stake_regs: Vec<certs::StakeRegistrationInsertQuery>,
32}
33
34impl Cip36InsertQuery {
35    /// Create new data set for CIP-36 Registrations Insert Query Batch.
36    pub(crate) fn new() -> Self {
37        Cip36InsertQuery {
38            registrations: Vec::new(),
39            invalid: Vec::new(),
40            for_vote_key: Vec::new(),
41            stake_regs: Vec::new(),
42        }
43    }
44
45    /// Prepare Batch of Insert Cip36 Registration Data Queries
46    pub(crate) async fn prepare_batch(
47        session: &Arc<Session>, cfg: &cassandra_db::EnvVars,
48    ) -> anyhow::Result<(SizedBatch, SizedBatch, SizedBatch)> {
49        let insert_cip36_batch = insert_cip36::Params::prepare_batch(session, cfg).await;
50        let insert_cip36_invalid_batch =
51            insert_cip36_invalid::Params::prepare_batch(session, cfg).await;
52        let insert_cip36_for_vote_key_addr_batch =
53            insert_cip36_for_vote_key::Params::prepare_batch(session, cfg).await;
54        // Its a hack of inserting `stake_regs` during the indexing CIP 36 registrations.
55        // Its done because some of the CIP 36 registrations contains some stake addresses which
56        // are not actually some how registered using cardano certs.
57        // Preparation of the `stake_regs` batch done under the `certs.rs`
58        Ok((
59            insert_cip36_batch?,
60            insert_cip36_invalid_batch?,
61            insert_cip36_for_vote_key_addr_batch?,
62        ))
63    }
64
65    /// Index the CIP-36 registrations in a transaction.
66    pub(crate) fn index(
67        &mut self, index: TxnIndex, slot_no: Slot, block: &MultiEraBlock,
68    ) -> anyhow::Result<()> {
69        // Catalyst strict is set to true
70        match Cip36::new(block, index, true) {
71            // Check for CIP-36 validity and should be strict catalyst (only 1 voting key)
72            // Note that in `validate_voting_keys` we already checked if the array has only one
73            Ok(Some(cip36)) if cip36.is_valid() => {
74                // This should always pass, because we already checked if the array has only one
75                let voting_key = cip36.voting_pks().first().ok_or(anyhow::anyhow!(
76                    "Valid CIP36 registration must have one voting key"
77                ))?;
78
79                let stake_pk = cip36.stake_pk().ok_or(anyhow::anyhow!(
80                    "Valid CIP36 registration must have one stake public key"
81                ))?;
82                let stake_pk_hash = Blake2b224Hash::new(&stake_pk.to_bytes());
83                let stake_address = StakeAddress::new(block.network(), false, stake_pk_hash);
84
85                self.registrations.push(insert_cip36::Params::new(
86                    voting_key, slot_no, index, &cip36,
87                ));
88                self.for_vote_key
89                    .push(insert_cip36_for_vote_key::Params::new(
90                        voting_key, slot_no, index, &cip36, true,
91                    ));
92                self.stake_regs
93                    .push(certs::StakeRegistrationInsertQuery::new(
94                        stake_address,
95                        slot_no,
96                        index,
97                        *stake_pk,
98                        false,
99                        false,
100                        false,
101                        true,
102                        None,
103                    ));
104            },
105            // Invalid CIP-36 Registration
106            Ok(Some(cip36)) => {
107                // Cannot index an invalid CIP36, if there is no stake public key.
108                if let Some(stake_pk) = cip36.stake_pk() {
109                    if cip36.voting_pks().is_empty() {
110                        self.invalid.push(insert_cip36_invalid::Params::new(
111                            None, slot_no, index, &cip36,
112                        ));
113                    } else {
114                        for voting_key in cip36.voting_pks() {
115                            self.invalid.push(insert_cip36_invalid::Params::new(
116                                Some(voting_key),
117                                slot_no,
118                                index,
119                                &cip36,
120                            ));
121                            self.for_vote_key
122                                .push(insert_cip36_for_vote_key::Params::new(
123                                    voting_key, slot_no, index, &cip36, false,
124                                ));
125                        }
126                    }
127
128                    let stake_pk_hash = Blake2b224Hash::new(&stake_pk.to_bytes());
129                    let stake_address = StakeAddress::new(block.network(), false, stake_pk_hash);
130                    self.stake_regs
131                        .push(certs::StakeRegistrationInsertQuery::new(
132                            stake_address,
133                            slot_no,
134                            index,
135                            *stake_pk,
136                            false,
137                            false,
138                            false,
139                            true,
140                            None,
141                        ));
142                }
143            },
144            _ => {},
145        }
146        Ok(())
147    }
148
149    /// Execute the CIP-36 Registration Indexing Queries.
150    ///
151    /// Consumes the `self` and returns a vector of futures.
152    pub(crate) fn execute(self, session: &Arc<CassandraSession>) -> FallibleQueryTasks {
153        let mut query_handles: FallibleQueryTasks = Vec::new();
154
155        if !self.registrations.is_empty() {
156            let inner_session = session.clone();
157            query_handles.push(tokio::spawn(async move {
158                inner_session
159                    .execute_batch(
160                        PreparedQuery::Cip36RegistrationInsertQuery,
161                        self.registrations,
162                    )
163                    .await
164            }));
165        }
166
167        if !self.invalid.is_empty() {
168            let inner_session = session.clone();
169            query_handles.push(tokio::spawn(async move {
170                inner_session
171                    .execute_batch(
172                        PreparedQuery::Cip36RegistrationInsertErrorQuery,
173                        self.invalid,
174                    )
175                    .await
176            }));
177        }
178
179        if !self.for_vote_key.is_empty() {
180            let inner_session = session.clone();
181            query_handles.push(tokio::spawn(async move {
182                inner_session
183                    .execute_batch(
184                        PreparedQuery::Cip36RegistrationForVoteKeyInsertQuery,
185                        self.for_vote_key,
186                    )
187                    .await
188            }));
189        }
190
191        if !self.stake_regs.is_empty() {
192            let inner_session = session.clone();
193            query_handles.push(tokio::spawn(async move {
194                inner_session
195                    .execute_batch(PreparedQuery::StakeRegistrationInsertQuery, self.stake_regs)
196                    .await
197            }));
198        }
199
200        query_handles
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207    use crate::db::index::tests::test_utils;
208
209    #[test]
210    fn index() {
211        let block = test_utils::block_2();
212        let mut query = Cip36InsertQuery::new();
213        query.index(0.into(), 0.into(), &block).unwrap();
214        assert_eq!(1, query.registrations.len());
215        assert!(query.invalid.is_empty());
216        assert_eq!(1, query.for_vote_key.len());
217        assert_eq!(1, query.stake_regs.len());
218    }
219}