hoprd_db_api/
aliases.rs

1use async_trait::async_trait;
2use sea_orm::{ColumnTrait, DbErr, EntityTrait, QueryFilter};
3
4use hoprd_db_entity::{aliases::Column, types::Alias};
5use hoprd_migration::OnConflict;
6
7use crate::{db::HoprdDb, errors::Result};
8
9pub const ME_AS_ALIAS: &str = "me";
10
11/// Defines DB API for accessing HOPR settings (mainly aliases for now)
12#[async_trait]
13pub trait HoprdDbAliasesOperations {
14    /// Retrieve the peer id for a given alias
15    async fn resolve_alias(&self, alias: String) -> Result<Option<String>>;
16
17    /// Retrieve all aliases
18    async fn get_aliases(&self) -> Result<Vec<Alias>>;
19
20    /// Create new key pair value in the db. If peer is already aliased, db entry will be updated with the new alias. If `peer` is node's PeerID, throws an error
21    async fn set_alias(&self, peer: String, alias: String) -> Result<()>;
22
23    /// Update aliases. If some peers or aliases are already in the db, db entries will be updated with the new aliases. If node's PeerID is among passed aliases, throws an error
24    async fn set_aliases(&self, aliases: Vec<Alias>) -> Result<()>;
25
26    /// Delete alias. If not found, throws an error.
27    async fn delete_alias(&self, alias: String) -> Result<()>;
28
29    /// Delete all aliases
30    async fn clear_aliases(&self) -> Result<()>;
31}
32
33#[async_trait]
34impl HoprdDbAliasesOperations for HoprdDb {
35    async fn resolve_alias(&self, alias: String) -> Result<Option<String>> {
36        let row = hoprd_db_entity::aliases::Entity::find()
37            .filter(hoprd_db_entity::aliases::Column::Alias.eq(alias))
38            .one(&self.metadata)
39            .await?;
40
41        Ok(row.map(|model| model.peer_id))
42    }
43
44    async fn get_aliases(&self) -> Result<Vec<Alias>> {
45        let rows = hoprd_db_entity::aliases::Entity::find().all(&self.metadata).await?;
46
47        let aliases: Vec<Alias> = rows.into_iter().map(Alias::from).collect();
48
49        Ok(aliases)
50    }
51
52    async fn set_aliases(&self, aliases: Vec<Alias>) -> Result<()> {
53        match self.resolve_alias(ME_AS_ALIAS.to_string()).await {
54            Ok(Some(me)) => {
55                if aliases.iter().any(|entry| entry.peer_id == me) {
56                    return Err(crate::errors::DbError::LogicalError(
57                        "own alias cannot be modified".into(),
58                    ));
59                }
60            }
61            Ok(None) => {}
62            Err(e) => return Err(e),
63        }
64
65        let new_aliases = aliases
66            .into_iter()
67            .map(|entry| hoprd_db_entity::aliases::ActiveModel {
68                id: Default::default(),
69                peer_id: sea_orm::ActiveValue::Set(entry.peer_id.clone()),
70                alias: sea_orm::ActiveValue::Set(entry.alias.clone()),
71            })
72            .collect::<Vec<_>>();
73
74        let _ = hoprd_db_entity::aliases::Entity::insert_many(new_aliases)
75            .on_conflict(
76                OnConflict::new()
77                    .update_columns([Column::PeerId, Column::Alias])
78                    .to_owned(),
79            )
80            .exec(&self.metadata)
81            .await?;
82
83        Ok(())
84    }
85
86    async fn set_alias(&self, peer: String, alias: String) -> Result<()> {
87        if let Ok(Some(me)) = self.resolve_alias(ME_AS_ALIAS.to_string()).await {
88            if me == peer {
89                return Err(crate::errors::DbError::ReAliasingSelfNotAllowed);
90            }
91        }
92
93        let new_pair = hoprd_db_entity::aliases::ActiveModel {
94            id: Default::default(),
95            peer_id: sea_orm::ActiveValue::Set(peer),
96            alias: sea_orm::ActiveValue::Set(alias),
97        };
98
99        match hoprd_db_entity::aliases::Entity::insert(new_pair)
100            .on_conflict(OnConflict::new().do_nothing().to_owned())
101            .exec(&self.metadata)
102            .await
103        {
104            Ok(_) => Ok(()),
105            Err(DbErr::RecordNotInserted) => Err(crate::errors::DbError::AliasOrPeerIdAlreadyExists),
106            Err(e) => Err(e.into()),
107        }
108    }
109
110    async fn delete_alias(&self, alias: String) -> Result<()> {
111        let res = hoprd_db_entity::aliases::Entity::delete_many()
112            .filter(hoprd_db_entity::aliases::Column::Alias.eq(alias))
113            .filter(hoprd_db_entity::aliases::Column::Alias.ne(ME_AS_ALIAS.to_string()))
114            .exec(&self.metadata)
115            .await?;
116
117        if res.rows_affected > 0 {
118            Ok(())
119        } else {
120            Err(crate::errors::DbError::LogicalError(
121                "alias cannot be removed because it does not exist or it is the node's own alias.".into(),
122            ))
123        }
124    }
125
126    async fn clear_aliases(&self) -> Result<()> {
127        let _ = hoprd_db_entity::aliases::Entity::delete_many()
128            .filter(hoprd_db_entity::aliases::Column::Alias.ne(ME_AS_ALIAS.to_string()))
129            .exec(&self.metadata)
130            .await?;
131
132        Ok(())
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139    use libp2p_identity::PeerId;
140
141    #[async_std::test]
142    async fn set_alias_should_succeed() {
143        let db = HoprdDb::new_in_memory().await;
144
145        db.set_alias(PeerId::random().to_string(), "test_alias".to_string())
146            .await
147            .expect("should add alias");
148
149        let aliases = db.get_aliases().await.expect("should get aliases");
150
151        assert_eq!(aliases.len(), 1);
152    }
153
154    #[async_std::test]
155    async fn set_alias_should_set_multiple_aliases_in_a_transaction() -> Result<()> {
156        let db: HoprdDb = HoprdDb::new_in_memory().await;
157        let entries = vec![
158            Alias {
159                peer_id: PeerId::random().to_string(),
160                alias: "test_alias".to_string(),
161            },
162            Alias {
163                peer_id: PeerId::random().to_string(),
164                alias: "test_alias_2".to_string(),
165            },
166        ];
167
168        db.set_aliases(entries.clone()).await?;
169
170        let aliases = db.get_aliases().await?;
171
172        assert_eq!(aliases.len(), 2);
173
174        Ok(())
175    }
176
177    #[async_std::test]
178    async fn set_me_among_other_alias_should_fail() {
179        let db = HoprdDb::new_in_memory().await;
180
181        let me_peer_id = PeerId::random().to_string();
182        let alias = ME_AS_ALIAS.to_string();
183
184        let res = db.set_alias(me_peer_id.clone(), alias.clone()).await;
185
186        assert!(res.is_ok());
187
188        let entries = vec![
189            Alias {
190                peer_id: me_peer_id.to_string(),
191                alias: "test_alias".to_string(),
192            },
193            Alias {
194                peer_id: PeerId::random().to_string(),
195                alias: "test_alias_2".to_string(),
196            },
197        ];
198
199        let res = db.set_aliases(entries.clone()).await;
200
201        assert!(res.is_err());
202    }
203
204    #[async_std::test]
205    async fn set_me_alias_should_fail_if_set() {
206        let db = HoprdDb::new_in_memory().await;
207
208        let me_peer_id = PeerId::random().to_string();
209        let alias = ME_AS_ALIAS.to_string();
210
211        let res = db.set_alias(me_peer_id.clone(), alias.clone()).await;
212
213        assert!(res.is_ok());
214
215        let res = db.set_alias(me_peer_id.clone(), alias.clone()).await;
216
217        assert!(res.is_err());
218    }
219
220    #[async_std::test]
221    async fn set_alias_should_fail_if_the_alias_already_is_assigned_to_any_peer_id() -> anyhow::Result<()> {
222        let db = HoprdDb::new_in_memory().await;
223
224        let peer_id = PeerId::random().to_string();
225        let peer_id_2 = PeerId::random().to_string();
226        let alias = "test_alias".to_string();
227
228        db.set_alias(peer_id.clone(), alias.clone()).await?;
229
230        assert!(db.set_alias(peer_id_2, alias.clone()).await.is_err());
231
232        let aliases = db.get_aliases().await.unwrap();
233
234        assert_eq!(aliases.len(), 1);
235        assert_eq!(aliases[0].peer_id, peer_id);
236
237        Ok(())
238    }
239
240    #[async_std::test]
241    async fn set_alias_should_fail_if_the_peerid_already_is_aliased() -> anyhow::Result<()> {
242        let db = HoprdDb::new_in_memory().await;
243
244        let peer_id = PeerId::random().to_string();
245        let alias = "test_alias".to_string();
246        let alias2 = "alias".to_string();
247
248        db.set_alias(peer_id.clone(), alias.clone()).await?;
249
250        assert!(db.set_alias(peer_id.clone(), alias2.clone()).await.is_err());
251
252        let aliases = db.get_aliases().await.unwrap();
253
254        assert_eq!(aliases.len(), 1);
255        assert_eq!(aliases[0].alias, alias);
256
257        Ok(())
258    }
259
260    #[async_std::test]
261    async fn resolve_alias_should_return_alias() {
262        let db = HoprdDb::new_in_memory().await;
263
264        let peer_id = PeerId::random().to_string();
265        let alias = "test_alias".to_string();
266
267        db.set_alias(peer_id.clone(), alias.clone())
268            .await
269            .expect("should add alias");
270
271        let alias = db.resolve_alias(alias).await.expect("should get alias");
272
273        assert!(alias.is_some());
274    }
275
276    #[async_std::test]
277    async fn resolve_not_stored_alias_should_return_none() {
278        let db = HoprdDb::new_in_memory().await;
279
280        let peer_id = PeerId::random().to_string();
281        let alias = "test_alias".to_string();
282
283        db.set_alias(peer_id.clone(), alias.clone())
284            .await
285            .expect("should add alias");
286
287        let alias = db
288            .resolve_alias(PeerId::random().to_string())
289            .await
290            .expect("should get alias");
291
292        assert!(alias.is_none());
293    }
294
295    #[async_std::test]
296    async fn delete_stored_alias() {
297        let db = HoprdDb::new_in_memory().await;
298
299        let peer_id = PeerId::random().to_string();
300        let alias = "test_alias".to_string();
301
302        db.set_alias(peer_id.clone(), alias.clone())
303            .await
304            .expect("should add alias");
305        let aliases = db.get_aliases().await.expect("should get aliases");
306        assert_eq!(aliases.len(), 1);
307
308        db.delete_alias(alias).await.expect("should delete alias");
309        let aliases = db.get_aliases().await.expect("should get aliases");
310        assert_eq!(aliases.len(), 0);
311    }
312
313    #[async_std::test]
314    async fn delete_all_aliases() {
315        let db = HoprdDb::new_in_memory().await;
316
317        let me_peer_id = PeerId::random().to_string();
318        let peer_id = PeerId::random().to_string();
319        let alias = "test_alias".to_string();
320
321        db.set_alias(peer_id.clone(), alias.clone())
322            .await
323            .expect("should add alias");
324        db.set_alias(me_peer_id.clone(), ME_AS_ALIAS.to_string())
325            .await
326            .expect("should add alias");
327
328        let aliases = db.get_aliases().await.expect("should get aliases");
329        assert_eq!(aliases.len(), 2);
330
331        db.clear_aliases()
332            .await
333            .expect(format!("should clear aliases except '{}'", ME_AS_ALIAS).as_str());
334        let aliases = db.get_aliases().await.expect("should get aliases");
335        assert_eq!(aliases.len(), 1);
336        assert_eq!(aliases[0].peer_id, me_peer_id);
337    }
338
339    #[async_std::test]
340    async fn set_aliases_with_existing_alias_should_replace_peer_id() {
341        let db = HoprdDb::new_in_memory().await;
342
343        let peer_id = PeerId::random().to_string();
344        let alias = "test_alias".to_string();
345
346        db.set_alias(peer_id.clone(), alias.clone())
347            .await
348            .expect("should add alias");
349
350        let new_peer_id = PeerId::random().to_string();
351        db.set_aliases(vec![Alias {
352            peer_id: new_peer_id.clone(),
353            alias: alias.clone(),
354        }])
355        .await
356        .expect("should replace peer_id");
357
358        let aliases = db.get_aliases().await.expect("should get aliases");
359
360        assert_eq!(aliases.len(), 1);
361        assert_eq!(aliases[0].peer_id, new_peer_id);
362    }
363
364    #[async_std::test]
365    async fn set_aliases_with_existing_peer_id_should_replace_alias() {
366        let db = HoprdDb::new_in_memory().await;
367
368        let peer_id = PeerId::random().to_string();
369        let alias = "test_alias".to_string();
370
371        db.set_alias(peer_id.clone(), alias.clone())
372            .await
373            .expect("should add alias");
374
375        db.set_aliases(vec![Alias {
376            peer_id: peer_id.clone(),
377            alias: alias.clone().to_uppercase(),
378        }])
379        .await
380        .expect("should replace alias");
381
382        let aliases = db.get_aliases().await.expect("should get aliases");
383
384        assert_eq!(aliases.len(), 1);
385        assert_eq!(aliases[0].alias, alias.to_uppercase());
386    }
387
388    #[async_std::test]
389    async fn set_aliases_with_existing_entry_should_do_nothing() {
390        let db = HoprdDb::new_in_memory().await;
391
392        let peer_id = PeerId::random().to_string();
393        let alias = "test_alias".to_string();
394
395        db.set_alias(peer_id.clone(), alias.clone())
396            .await
397            .expect("should add alias");
398
399        db.set_aliases(vec![Alias {
400            peer_id: peer_id.clone(),
401            alias: alias.clone(),
402        }])
403        .await
404        .expect("should do nothing");
405
406        let aliases = db.get_aliases().await.expect("should get aliases");
407
408        assert_eq!(aliases.len(), 1);
409    }
410}