hopr_db_sql/
registry.rs

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