hopr_db_sql/
registry.rs

1use async_trait::async_trait;
2use hopr_db_entity::{network_eligibility, network_registry};
3use hopr_primitive_types::prelude::{Address, ToHex};
4use sea_orm::{ColumnTrait, DbErr, EntityTrait, QueryFilter, Set};
5use sea_query::OnConflict;
6
7use crate::{
8    HoprDbGeneralModelOperations, OptTx,
9    db::HoprDb,
10    errors::{DbSqlError, Result},
11};
12
13/// Defines DB access API for network registry operations.
14#[async_trait]
15pub trait HoprDbRegistryOperations {
16    /// Sets the given node as allowed or denied in network registry.
17    async fn set_access_in_network_registry<'a>(&'a self, tx: OptTx<'a>, address: Address, allowed: bool)
18    -> Result<()>;
19
20    /// Returns `true` if the given node is allowed in network registry.
21    async fn is_allowed_in_network_registry<'a, T>(&'a self, tx: OptTx<'a>, address_like: &T) -> Result<bool>
22    where
23        Address: TryFrom<T>,
24        T: Clone + Sync;
25
26    /// Sets or unsets Safe NR eligibility.
27    async fn set_safe_eligibility<'a>(&'a self, tx: OptTx<'a>, address: Address, eligible: bool) -> Result<()>;
28
29    /// Returns `true` if the given Safe is NR eligible.
30    async fn is_safe_eligible<'a>(&'a self, tx: OptTx<'a>, address: Address) -> Result<bool>;
31}
32
33#[async_trait]
34impl HoprDbRegistryOperations for HoprDb {
35    async fn set_access_in_network_registry<'a>(
36        &'a self,
37        tx: OptTx<'a>,
38        address: Address,
39        allowed: bool,
40    ) -> Result<()> {
41        self.nest_transaction(tx)
42            .await?
43            .perform(|tx| {
44                Box::pin(async move {
45                    if allowed {
46                        let entry = network_registry::ActiveModel {
47                            chain_address: Set(address.to_hex()),
48                            ..Default::default()
49                        };
50
51                        match network_registry::Entity::insert(entry)
52                            .on_conflict(
53                                OnConflict::column(network_registry::Column::ChainAddress)
54                                    .do_nothing()
55                                    .to_owned(),
56                            )
57                            .exec(tx.as_ref())
58                            .await
59                        {
60                            Ok(_) | Err(DbErr::RecordNotInserted) => Ok::<_, DbSqlError>(()),
61                            Err(e) => Err(e.into()),
62                        }
63                    } else {
64                        network_registry::Entity::delete_many()
65                            .filter(network_registry::Column::ChainAddress.eq(address.to_hex()))
66                            .exec(tx.as_ref())
67                            .await?;
68                        Ok::<_, DbSqlError>(())
69                    }
70                })
71            })
72            .await
73    }
74
75    async fn is_allowed_in_network_registry<'a, T>(&'a self, tx: OptTx<'a>, address_like: &T) -> Result<bool>
76    where
77        Address: TryFrom<T>,
78        T: Clone + Sync,
79    {
80        let address = Address::try_from((*address_like).clone()).map_err(|_| DbSqlError::DecodingError)?;
81
82        self.nest_transaction(tx)
83            .await?
84            .perform(|tx| {
85                Box::pin(async move {
86                    Ok::<_, DbSqlError>(
87                        network_registry::Entity::find()
88                            .filter(network_registry::Column::ChainAddress.eq(address.to_hex()))
89                            .one(tx.as_ref())
90                            .await?
91                            .is_some(),
92                    )
93                })
94            })
95            .await
96    }
97
98    async fn set_safe_eligibility<'a>(&'a self, tx: OptTx<'a>, address: Address, eligible: bool) -> Result<()> {
99        self.nest_transaction(tx)
100            .await?
101            .perform(|tx| {
102                Box::pin(async move {
103                    if eligible {
104                        let new_entry = network_eligibility::ActiveModel {
105                            safe_address: Set(address.to_hex()),
106                            ..Default::default()
107                        };
108
109                        match network_eligibility::Entity::insert(new_entry)
110                            .on_conflict(
111                                OnConflict::column(network_eligibility::Column::SafeAddress)
112                                    .do_nothing()
113                                    .to_owned(),
114                            )
115                            .exec(tx.as_ref())
116                            .await
117                        {
118                            Ok(_) | Err(DbErr::RecordNotInserted) => Ok::<_, DbSqlError>(()),
119                            Err(e) => Err(e.into()),
120                        }
121                    } else {
122                        network_eligibility::Entity::delete_many()
123                            .filter(network_eligibility::Column::SafeAddress.eq(address.to_hex()))
124                            .exec(tx.as_ref())
125                            .await?;
126                        Ok::<_, DbSqlError>(())
127                    }
128                })
129            })
130            .await
131    }
132
133    async fn is_safe_eligible<'a>(&'a self, tx: OptTx<'a>, address: Address) -> Result<bool> {
134        self.nest_transaction(tx)
135            .await?
136            .perform(|tx| {
137                Box::pin(async move {
138                    Ok::<_, DbSqlError>(
139                        network_eligibility::Entity::find()
140                            .filter(network_eligibility::Column::SafeAddress.eq(address.to_hex()))
141                            .one(tx.as_ref())
142                            .await?
143                            .is_some(),
144                    )
145                })
146            })
147            .await
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use hopr_crypto_types::{keypairs::ChainKeypair, prelude::Keypair};
154    use hopr_primitive_types::prelude::Address;
155    use lazy_static::lazy_static;
156
157    use crate::{db::HoprDb, registry::HoprDbRegistryOperations};
158
159    lazy_static! {
160        static ref ADDR_1: Address = "4331eaa9542b6b034c43090d9ec1c2198758dbc3"
161            .parse()
162            .expect("lazy static address should be valid");
163        static ref ADDR_2: Address = "47d1677e018e79dcdd8a9c554466cb1556fa5007"
164            .parse()
165            .expect("lazy static address should be valid");
166    }
167
168    #[tokio::test]
169    async fn test_network_registry_db() -> anyhow::Result<()> {
170        let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
171
172        assert!(!db.is_allowed_in_network_registry(None, &ADDR_1.as_ref()).await?);
173        assert!(!db.is_allowed_in_network_registry(None, &ADDR_2.as_ref()).await?);
174
175        db.set_access_in_network_registry(None, *ADDR_1, true).await?;
176
177        assert!(db.is_allowed_in_network_registry(None, &ADDR_1.as_ref()).await?);
178        assert!(!db.is_allowed_in_network_registry(None, &ADDR_2.as_ref()).await?);
179
180        db.set_access_in_network_registry(None, *ADDR_1, true).await?;
181
182        assert!(db.is_allowed_in_network_registry(None, &ADDR_1.as_ref()).await?);
183        assert!(!db.is_allowed_in_network_registry(None, &ADDR_2.as_ref()).await?);
184
185        db.set_access_in_network_registry(None, *ADDR_1, false).await?;
186
187        assert!(!db.is_allowed_in_network_registry(None, &ADDR_1.as_ref()).await?);
188        assert!(!db.is_allowed_in_network_registry(None, &ADDR_2.as_ref()).await?);
189
190        db.set_access_in_network_registry(None, *ADDR_1, false).await?;
191
192        assert!(!db.is_allowed_in_network_registry(None, &ADDR_1.as_ref()).await?);
193        assert!(!db.is_allowed_in_network_registry(None, &ADDR_2.as_ref()).await?);
194        Ok(())
195    }
196
197    #[tokio::test]
198    async fn test_network_eligiblity_db() -> anyhow::Result<()> {
199        let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
200
201        assert!(!db.is_safe_eligible(None, *ADDR_1).await?);
202        assert!(!db.is_safe_eligible(None, *ADDR_2).await?);
203
204        db.set_safe_eligibility(None, *ADDR_1, true).await?;
205
206        assert!(db.is_safe_eligible(None, *ADDR_1).await?);
207        assert!(!db.is_safe_eligible(None, *ADDR_2).await?);
208
209        db.set_safe_eligibility(None, *ADDR_1, true).await?;
210
211        assert!(db.is_safe_eligible(None, *ADDR_1).await?);
212        assert!(!db.is_safe_eligible(None, *ADDR_2).await?);
213
214        db.set_safe_eligibility(None, *ADDR_1, false).await?;
215
216        assert!(!db.is_safe_eligible(None, *ADDR_1).await?);
217        assert!(!db.is_safe_eligible(None, *ADDR_2).await?);
218
219        db.set_safe_eligibility(None, *ADDR_1, false).await?;
220
221        assert!(!db.is_safe_eligible(None, *ADDR_1).await?);
222        assert!(!db.is_safe_eligible(None, *ADDR_2).await?);
223        Ok(())
224    }
225}