1use async_trait::async_trait;
2use futures::TryFutureExt;
3use hopr_crypto_types::prelude::OffchainPublicKey;
4use hopr_db_entity::{
5 account, announcement,
6 prelude::{Account, Announcement},
7};
8use hopr_internal_types::{account::AccountType, prelude::AccountEntry};
9use hopr_primitive_types::{
10 errors::GeneralError,
11 prelude::{Address, ToHex},
12};
13use multiaddr::Multiaddr;
14use sea_orm::{
15 ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, IntoActiveModel, ModelTrait, QueryFilter, QueryOrder, Related,
16 Set, sea_query::Expr,
17};
18use sea_query::{Condition, IntoCondition, OnConflict};
19
20use crate::{
21 HoprDbGeneralModelOperations, OptTx,
22 db::HoprDb,
23 errors::{DbSqlError, DbSqlError::MissingAccount, Result},
24};
25
26#[allow(clippy::large_enum_variant)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
29pub enum ChainOrPacketKey {
30 ChainKey(Address),
32 PacketKey(OffchainPublicKey),
34}
35
36impl From<Address> for ChainOrPacketKey {
37 fn from(value: Address) -> Self {
38 Self::ChainKey(value)
39 }
40}
41
42impl From<OffchainPublicKey> for ChainOrPacketKey {
43 fn from(value: OffchainPublicKey) -> Self {
44 Self::PacketKey(value)
45 }
46}
47
48impl TryFrom<ChainOrPacketKey> for OffchainPublicKey {
49 type Error = GeneralError;
50
51 fn try_from(value: ChainOrPacketKey) -> std::result::Result<Self, Self::Error> {
52 match value {
53 ChainOrPacketKey::ChainKey(_) => Err(GeneralError::InvalidInput),
54 ChainOrPacketKey::PacketKey(k) => Ok(k),
55 }
56 }
57}
58
59impl TryFrom<ChainOrPacketKey> for Address {
60 type Error = GeneralError;
61
62 fn try_from(value: ChainOrPacketKey) -> std::result::Result<Self, Self::Error> {
63 match value {
64 ChainOrPacketKey::ChainKey(k) => Ok(k),
65 ChainOrPacketKey::PacketKey(_) => Err(GeneralError::InvalidInput),
66 }
67 }
68}
69
70impl IntoCondition for ChainOrPacketKey {
71 fn into_condition(self) -> Condition {
72 match self {
73 ChainOrPacketKey::ChainKey(chain_key) => {
74 account::Column::ChainKey.eq(chain_key.to_string()).into_condition()
75 }
76 ChainOrPacketKey::PacketKey(packet_key) => {
77 account::Column::PacketKey.eq(packet_key.to_string()).into_condition()
78 }
79 }
80 }
81}
82
83#[async_trait]
88pub trait HoprDbAccountOperations {
89 async fn get_account<'a, T>(&'a self, tx: OptTx<'a>, key: T) -> Result<Option<AccountEntry>>
91 where
92 T: Into<ChainOrPacketKey> + Send + Sync;
93
94 async fn get_self_account<'a>(&'a self, tx: OptTx<'a>) -> Result<AccountEntry>;
97
98 async fn get_accounts<'a>(&'a self, tx: OptTx<'a>, public_only: bool) -> Result<Vec<AccountEntry>>;
101
102 async fn insert_account<'a>(&'a self, tx: OptTx<'a>, account: AccountEntry) -> Result<()>;
105
106 async fn insert_announcement<'a, T>(
113 &'a self,
114 tx: OptTx<'a>,
115 key: T,
116 multiaddr: Multiaddr,
117 at_block: u32,
118 ) -> Result<AccountEntry>
119 where
120 T: Into<ChainOrPacketKey> + Send + Sync;
121
122 async fn delete_all_announcements<'a, T>(&'a self, tx: OptTx<'a>, key: T) -> Result<()>
124 where
125 T: Into<ChainOrPacketKey> + Send + Sync;
126
127 async fn delete_account<'a, T>(&'a self, tx: OptTx<'a>, key: T) -> Result<()>
129 where
130 T: Into<ChainOrPacketKey> + Send + Sync;
131
132 async fn translate_key<'a, T>(&'a self, tx: OptTx<'a>, key: T) -> Result<Option<ChainOrPacketKey>>
137 where
138 T: Into<ChainOrPacketKey> + Send + Sync;
139}
140
141pub(crate) fn model_to_account_entry(
143 account: account::Model,
144 announcements: Vec<announcement::Model>,
145) -> Result<AccountEntry> {
146 let announcement = announcements.first();
148
149 Ok(AccountEntry {
150 public_key: OffchainPublicKey::from_hex(&account.packet_key)?,
151 chain_addr: account.chain_key.parse()?,
152 published_at: account.published_at as u32,
153 entry_type: match announcement {
154 None => AccountType::NotAnnounced,
155 Some(a) => AccountType::Announced {
156 multiaddr: a.multiaddress.parse().map_err(|_| DbSqlError::DecodingError)?,
157 updated_block: a.at_block as u32,
158 },
159 },
160 })
161}
162
163#[async_trait]
164impl HoprDbAccountOperations for HoprDb {
165 async fn get_account<'a, T: Into<ChainOrPacketKey> + Send + Sync>(
166 &'a self,
167 tx: OptTx<'a>,
168 key: T,
169 ) -> Result<Option<AccountEntry>> {
170 let cpk = key.into();
171 self.nest_transaction(tx)
172 .await?
173 .perform(|tx| {
174 Box::pin(async move {
175 let maybe_model = Account::find()
176 .find_with_related(Announcement)
177 .filter(cpk)
178 .order_by_desc(announcement::Column::AtBlock)
179 .all(tx.as_ref())
180 .await?
181 .pop();
182
183 Ok::<_, DbSqlError>(if let Some((account, announcements)) = maybe_model {
184 Some(model_to_account_entry(account, announcements)?)
185 } else {
186 None
187 })
188 })
189 })
190 .await
191 }
192
193 async fn get_self_account<'a>(&'a self, tx: OptTx<'a>) -> Result<AccountEntry> {
194 self.get_account(tx, self.me_onchain)
195 .await?
196 .ok_or(DbSqlError::MissingAccount)
197 }
198
199 async fn get_accounts<'a>(&'a self, tx: OptTx<'a>, public_only: bool) -> Result<Vec<AccountEntry>> {
200 self.nest_transaction(tx)
201 .await?
202 .perform(|tx| {
203 Box::pin(async move {
204 Account::find()
205 .find_with_related(Announcement)
206 .filter(if public_only {
207 announcement::Column::Multiaddress.ne("")
208 } else {
209 Expr::value(1)
210 })
211 .order_by_desc(announcement::Column::AtBlock)
212 .all(tx.as_ref())
213 .await?
214 .into_iter()
215 .map(|(a, b)| model_to_account_entry(a, b))
216 .collect()
217 })
218 })
219 .await
220 }
221
222 async fn insert_account<'a>(&'a self, tx: OptTx<'a>, account: AccountEntry) -> Result<()> {
223 let myself = self.clone();
224 self.nest_transaction(tx)
225 .await?
226 .perform(|tx| {
227 Box::pin(async move {
228 match account::Entity::insert(account::ActiveModel {
229 chain_key: Set(account.chain_addr.to_hex()),
230 packet_key: Set(account.public_key.to_hex()),
231 published_at: Set(account.published_at as i32),
232 ..Default::default()
233 })
234 .on_conflict(
235 OnConflict::columns([account::Column::ChainKey, account::Column::PacketKey])
236 .do_nothing()
237 .to_owned(),
238 )
239 .exec(tx.as_ref())
240 .await
241 {
242 res @ Ok(_) | res @ Err(DbErr::RecordNotInserted) => {
244 myself
245 .caches
246 .chain_to_offchain
247 .insert(account.chain_addr, Some(account.public_key))
248 .await;
249 myself
250 .caches
251 .offchain_to_chain
252 .insert(account.public_key, Some(account.chain_addr))
253 .await;
254
255 if res.is_ok() {
258 if let Err(error) = myself.caches.key_id_mapper.update_key_id_binding(&account) {
259 tracing::warn!(?account, %error, "keybinding not updated")
260 }
261 }
262
263 if let AccountType::Announced {
264 multiaddr,
265 updated_block,
266 } = account.entry_type
267 {
268 myself
269 .insert_announcement(Some(tx), account.chain_addr, multiaddr, updated_block)
270 .await?;
271 }
272
273 Ok::<(), DbSqlError>(())
274 }
275 Err(e) => Err(e.into()),
276 }
277 })
278 })
279 .await
280 }
281
282 async fn insert_announcement<'a, T>(
283 &'a self,
284 tx: OptTx<'a>,
285 key: T,
286 multiaddr: Multiaddr,
287 at_block: u32,
288 ) -> Result<AccountEntry>
289 where
290 T: Into<ChainOrPacketKey> + Send + Sync,
291 {
292 let cpk = key.into();
293 self.nest_transaction(tx)
294 .await?
295 .perform(|tx| {
296 Box::pin(async move {
297 let (existing_account, mut existing_announcements) = account::Entity::find()
298 .find_with_related(announcement::Entity)
299 .filter(cpk)
300 .order_by_desc(announcement::Column::AtBlock)
301 .all(tx.as_ref())
302 .await?
303 .pop()
304 .ok_or(MissingAccount)?;
305
306 if let Some((index, _)) = existing_announcements
307 .iter()
308 .enumerate()
309 .find(|(_, announcement)| announcement.multiaddress == multiaddr.to_string())
310 {
311 let mut existing_announcement = existing_announcements.remove(index).into_active_model();
312 existing_announcement.at_block = Set(at_block as i32);
313 let updated_announcement = existing_announcement.update(tx.as_ref()).await?;
314
315 existing_announcements.insert(index, updated_announcement);
317 } else {
318 let new_announcement = announcement::ActiveModel {
319 account_id: Set(existing_account.id),
320 multiaddress: Set(multiaddr.to_string()),
321 at_block: Set(at_block as i32),
322 ..Default::default()
323 }
324 .insert(tx.as_ref())
325 .await?;
326
327 existing_announcements.insert(0, new_announcement);
329 }
330
331 model_to_account_entry(existing_account, existing_announcements)
332 })
333 })
334 .await
335 }
336
337 async fn delete_all_announcements<'a, T>(&'a self, tx: OptTx<'a>, key: T) -> Result<()>
338 where
339 T: Into<ChainOrPacketKey> + Send + Sync,
340 {
341 let cpk = key.into();
342 self.nest_transaction(tx)
343 .await?
344 .perform(|tx| {
345 Box::pin(async move {
346 let to_delete = account::Entity::find_related()
347 .filter(cpk)
348 .all(tx.as_ref())
349 .await?
350 .into_iter()
351 .map(|x| x.id)
352 .collect::<Vec<_>>();
353
354 if !to_delete.is_empty() {
355 announcement::Entity::delete_many()
356 .filter(announcement::Column::Id.is_in(to_delete))
357 .exec(tx.as_ref())
358 .await?;
359
360 Ok::<_, DbSqlError>(())
361 } else {
362 Err(MissingAccount)
363 }
364 })
365 })
366 .await
367 }
368
369 async fn delete_account<'a, T>(&'a self, tx: OptTx<'a>, key: T) -> Result<()>
370 where
371 T: Into<ChainOrPacketKey> + Send + Sync,
372 {
373 let myself = self.clone();
374 let cpk = key.into();
375 self.nest_transaction(tx)
376 .await?
377 .perform(|tx| {
378 Box::pin(async move {
379 if let Some(entry) = account::Entity::find().filter(cpk).one(tx.as_ref()).await? {
380 let account_entry = model_to_account_entry(entry.clone(), vec![])?;
381 entry.delete(tx.as_ref()).await?;
382
383 myself
384 .caches
385 .chain_to_offchain
386 .invalidate(&account_entry.chain_addr)
387 .await;
388 myself
389 .caches
390 .offchain_to_chain
391 .invalidate(&account_entry.public_key)
392 .await;
393 Ok::<_, DbSqlError>(())
394 } else {
395 Err(MissingAccount)
396 }
397 })
398 })
399 .await
400 }
401
402 async fn translate_key<'a, T: Into<ChainOrPacketKey> + Send + Sync>(
403 &'a self,
404 tx: OptTx<'a>,
405 key: T,
406 ) -> Result<Option<ChainOrPacketKey>> {
407 Ok(match key.into() {
408 ChainOrPacketKey::ChainKey(chain_key) => self
409 .caches
410 .chain_to_offchain
411 .try_get_with_by_ref(
412 &chain_key,
413 self.nest_transaction(tx).and_then(|op| {
414 op.perform(|tx| {
415 Box::pin(async move {
416 let maybe_model = Account::find()
417 .filter(account::Column::ChainKey.eq(chain_key.to_string()))
418 .one(tx.as_ref())
419 .await?;
420 if let Some(m) = maybe_model {
421 Ok(Some(OffchainPublicKey::from_hex(&m.packet_key)?))
422 } else {
423 Ok(None)
424 }
425 })
426 })
427 }),
428 )
429 .await?
430 .map(ChainOrPacketKey::PacketKey),
431 ChainOrPacketKey::PacketKey(packey_key) => self
432 .caches
433 .offchain_to_chain
434 .try_get_with_by_ref(
435 &packey_key,
436 self.nest_transaction(tx).and_then(|op| {
437 op.perform(|tx| {
438 Box::pin(async move {
439 let maybe_model = Account::find()
440 .filter(account::Column::PacketKey.eq(packey_key.to_string()))
441 .one(tx.as_ref())
442 .await?;
443 if let Some(m) = maybe_model {
444 Ok(Some(Address::from_hex(&m.chain_key)?))
445 } else {
446 Ok(None)
447 }
448 })
449 })
450 }),
451 )
452 .await?
453 .map(ChainOrPacketKey::ChainKey),
454 })
455 }
456}
457
458#[cfg(test)]
459mod tests {
460 use anyhow::Context;
461 use hopr_crypto_types::prelude::{ChainKeypair, Keypair, OffchainKeypair};
462 use hopr_internal_types::prelude::AccountType::NotAnnounced;
463
464 use super::*;
465 use crate::{
466 HoprDbGeneralModelOperations,
467 errors::{DbSqlError, DbSqlError::DecodingError},
468 };
469
470 #[tokio::test]
471 async fn test_insert_account_announcement() -> anyhow::Result<()> {
472 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
473
474 let chain_1 = ChainKeypair::random().public().to_address();
475 let packet_1 = *OffchainKeypair::random().public();
476
477 db.insert_account(
478 None,
479 AccountEntry {
480 public_key: packet_1,
481 chain_addr: chain_1,
482 published_at: 1,
483 entry_type: AccountType::NotAnnounced,
484 },
485 )
486 .await?;
487
488 let acc = db.get_account(None, chain_1).await?.expect("should contain account");
489 assert_eq!(packet_1, acc.public_key, "pub keys must match");
490 assert_eq!(AccountType::NotAnnounced, acc.entry_type.clone());
491 assert_eq!(1, acc.published_at);
492
493 let maddr: Multiaddr = "/ip4/1.2.3.4/tcp/8000".parse()?;
494 let block = 100;
495
496 let db_acc = db.insert_announcement(None, chain_1, maddr.clone(), block).await?;
497
498 let acc = db.get_account(None, chain_1).await?.context("should contain account")?;
499 assert_eq!(Some(maddr.clone()), acc.get_multiaddr(), "multiaddress must match");
500 assert_eq!(Some(block), acc.updated_at());
501 assert_eq!(acc, db_acc);
502
503 let block = 200;
504 let db_acc = db.insert_announcement(None, chain_1, maddr.clone(), block).await?;
505
506 let acc = db.get_account(None, chain_1).await?.expect("should contain account");
507 assert_eq!(Some(maddr), acc.get_multiaddr(), "multiaddress must match");
508 assert_eq!(Some(block), acc.updated_at());
509 assert_eq!(acc, db_acc);
510
511 let maddr: Multiaddr = "/dns4/useful.domain/tcp/56".parse()?;
512 let block = 300;
513 let db_acc = db.insert_announcement(None, chain_1, maddr.clone(), block).await?;
514
515 let acc = db.get_account(None, chain_1).await?.expect("should contain account");
516 assert_eq!(Some(maddr), acc.get_multiaddr(), "multiaddress must match");
517 assert_eq!(Some(block), acc.updated_at());
518 assert_eq!(acc, db_acc);
519
520 Ok(())
521 }
522
523 #[tokio::test]
524 async fn test_should_allow_reannouncement() -> anyhow::Result<()> {
525 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
526
527 let chain_1 = ChainKeypair::random().public().to_address();
528 let packet_1 = *OffchainKeypair::random().public();
529
530 db.insert_account(
531 None,
532 AccountEntry {
533 public_key: packet_1,
534 chain_addr: chain_1,
535 published_at: 1,
536 entry_type: AccountType::NotAnnounced,
537 },
538 )
539 .await?;
540
541 db.insert_announcement(None, chain_1, "/ip4/1.2.3.4/tcp/8000".parse()?, 100)
542 .await?;
543
544 let ae = db.get_account(None, chain_1).await?.ok_or(MissingAccount)?;
545
546 assert_eq!(
547 "/ip4/1.2.3.4/tcp/8000",
548 ae.get_multiaddr().expect("has multiaddress").to_string()
549 );
550
551 db.insert_announcement(None, chain_1, "/ip4/1.2.3.4/tcp/8001".parse()?, 110)
552 .await?;
553
554 let ae = db.get_account(None, chain_1).await?.ok_or(MissingAccount)?;
555
556 assert_eq!(
557 "/ip4/1.2.3.4/tcp/8001",
558 ae.get_multiaddr().expect("has multiaddress").to_string()
559 );
560
561 Ok(())
562 }
563
564 #[tokio::test]
565 async fn test_should_not_insert_account_announcement_to_nonexisting_account() -> anyhow::Result<()> {
566 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
567
568 let chain_1 = ChainKeypair::random().public().to_address();
569
570 let maddr: Multiaddr = "/ip4/1.2.3.4/tcp/8000".parse()?;
571 let block = 100;
572
573 let r = db.insert_announcement(None, chain_1, maddr.clone(), block).await;
574 assert!(
575 matches!(r, Err(MissingAccount)),
576 "should not insert announcement to non-existing account"
577 );
578
579 Ok(())
580 }
581
582 #[tokio::test]
583 async fn test_should_allow_duplicate_announcement_per_different_accounts() -> anyhow::Result<()> {
584 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
585
586 let chain_1 = ChainKeypair::random().public().to_address();
587 let packet_1 = *OffchainKeypair::random().public();
588
589 db.insert_account(
590 None,
591 AccountEntry {
592 public_key: packet_1,
593 chain_addr: chain_1,
594 published_at: 1,
595 entry_type: AccountType::NotAnnounced,
596 },
597 )
598 .await?;
599
600 let chain_2 = ChainKeypair::random().public().to_address();
601 let packet_2 = *OffchainKeypair::random().public();
602
603 db.insert_account(
604 None,
605 AccountEntry {
606 public_key: packet_2,
607 chain_addr: chain_2,
608 published_at: 2,
609 entry_type: AccountType::NotAnnounced,
610 },
611 )
612 .await?;
613
614 let maddr: Multiaddr = "/ip4/1.2.3.4/tcp/8000".parse()?;
615 let block = 100;
616
617 let db_acc_1 = db.insert_announcement(None, chain_1, maddr.clone(), block).await?;
618 let db_acc_2 = db.insert_announcement(None, chain_2, maddr.clone(), block).await?;
619
620 let acc = db.get_account(None, chain_1).await?.expect("should contain account");
621 assert_eq!(Some(maddr.clone()), acc.get_multiaddr(), "multiaddress must match");
622 assert_eq!(Some(block), acc.updated_at());
623 assert_eq!(acc, db_acc_1);
624
625 let acc = db.get_account(None, chain_2).await?.expect("should contain account");
626 assert_eq!(Some(maddr.clone()), acc.get_multiaddr(), "multiaddress must match");
627 assert_eq!(Some(block), acc.updated_at());
628 assert_eq!(acc, db_acc_2);
629
630 Ok(())
631 }
632
633 #[tokio::test]
634 async fn test_delete_account() -> anyhow::Result<()> {
635 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
636
637 let packet_1 = *OffchainKeypair::random().public();
638 let chain_1 = ChainKeypair::random().public().to_address();
639 db.insert_account(
640 None,
641 AccountEntry {
642 public_key: packet_1,
643 chain_addr: chain_1,
644 published_at: 1,
645 entry_type: AccountType::Announced {
646 multiaddr: "/ip4/1.2.3.4/tcp/1234".parse()?,
647 updated_block: 10,
648 },
649 },
650 )
651 .await?;
652
653 assert!(db.get_account(None, chain_1).await?.is_some());
654
655 db.delete_account(None, chain_1).await?;
656
657 assert!(db.get_account(None, chain_1).await?.is_none());
658
659 Ok(())
660 }
661
662 #[tokio::test]
663 async fn test_should_fail_to_delete_nonexistent_account() -> anyhow::Result<()> {
664 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
665
666 let chain_1 = ChainKeypair::random().public().to_address();
667
668 let r = db.delete_account(None, chain_1).await;
669 assert!(
670 matches!(r, Err(MissingAccount)),
671 "should not delete non-existing account"
672 );
673
674 Ok(())
675 }
676
677 #[tokio::test]
678 async fn test_should_not_fail_on_duplicate_account_insert() -> anyhow::Result<()> {
679 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
680
681 let chain_1 = ChainKeypair::random().public().to_address();
682 let packet_1 = *OffchainKeypair::random().public();
683
684 db.insert_account(
685 None,
686 AccountEntry {
687 public_key: packet_1,
688 chain_addr: chain_1,
689 published_at: 1,
690 entry_type: AccountType::NotAnnounced,
691 },
692 )
693 .await?;
694
695 db.insert_account(
696 None,
697 AccountEntry {
698 public_key: packet_1,
699 chain_addr: chain_1,
700 published_at: 1,
701 entry_type: AccountType::NotAnnounced,
702 },
703 )
704 .await?;
705
706 Ok(())
707 }
708
709 #[tokio::test]
710 async fn test_delete_announcements() -> anyhow::Result<()> {
711 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
712
713 let packet_1 = *OffchainKeypair::random().public();
714 let chain_1 = ChainKeypair::random().public().to_address();
715 let mut entry = AccountEntry {
716 public_key: packet_1,
717 chain_addr: chain_1,
718 published_at: 1,
719 entry_type: AccountType::Announced {
720 multiaddr: "/ip4/1.2.3.4/tcp/1234".parse()?,
721 updated_block: 10,
722 },
723 };
724
725 db.insert_account(None, entry.clone()).await?;
726
727 assert_eq!(Some(entry.clone()), db.get_account(None, chain_1).await?);
728
729 db.delete_all_announcements(None, chain_1).await?;
730
731 entry.entry_type = NotAnnounced;
732
733 assert_eq!(Some(entry), db.get_account(None, chain_1).await?);
734
735 Ok(())
736 }
737
738 #[tokio::test]
739 async fn test_should_fail_to_delete_nonexistent_account_announcements() -> anyhow::Result<()> {
740 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
741
742 let chain_1 = ChainKeypair::random().public().to_address();
743
744 let r = db.delete_all_announcements(None, chain_1).await;
745 assert!(
746 matches!(r, Err(MissingAccount)),
747 "should not delete non-existing account"
748 );
749
750 Ok(())
751 }
752
753 #[tokio::test]
754 async fn test_translate_key() -> anyhow::Result<()> {
755 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
756
757 let chain_1 = ChainKeypair::random().public().to_address();
758 let packet_1 = *OffchainKeypair::random().public();
759
760 let chain_2 = ChainKeypair::random().public().to_address();
761 let packet_2 = *OffchainKeypair::random().public();
762
763 let db_clone = db.clone();
764 db.begin_transaction()
765 .await?
766 .perform(|tx| {
767 Box::pin(async move {
768 db_clone
769 .insert_account(
770 tx.into(),
771 AccountEntry {
772 public_key: packet_1,
773 chain_addr: chain_1,
774 published_at: 1,
775 entry_type: AccountType::NotAnnounced,
776 },
777 )
778 .await?;
779 db_clone
780 .insert_account(
781 tx.into(),
782 AccountEntry {
783 public_key: packet_2,
784 chain_addr: chain_2,
785 published_at: 2,
786 entry_type: AccountType::NotAnnounced,
787 },
788 )
789 .await?;
790 Ok::<(), DbSqlError>(())
791 })
792 })
793 .await?;
794
795 let a: Address = db
796 .translate_key(None, packet_1)
797 .await?
798 .context("must contain key")?
799 .try_into()?;
800
801 let b: OffchainPublicKey = db
802 .translate_key(None, chain_2)
803 .await?
804 .context("must contain key")?
805 .try_into()?;
806
807 assert_eq!(chain_1, a, "chain keys must match");
808 assert_eq!(packet_2, b, "chain keys must match");
809
810 Ok(())
811 }
812
813 #[tokio::test]
814 async fn test_translate_key_no_cache() -> anyhow::Result<()> {
815 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
816
817 let chain_1 = ChainKeypair::random().public().to_address();
818 let packet_1 = *OffchainKeypair::random().public();
819
820 let chain_2 = ChainKeypair::random().public().to_address();
821 let packet_2 = *OffchainKeypair::random().public();
822
823 let db_clone = db.clone();
824 db.begin_transaction()
825 .await?
826 .perform(|tx| {
827 Box::pin(async move {
828 db_clone
829 .insert_account(
830 tx.into(),
831 AccountEntry {
832 public_key: packet_1,
833 chain_addr: chain_1,
834 published_at: 1,
835 entry_type: AccountType::NotAnnounced,
836 },
837 )
838 .await?;
839 db_clone
840 .insert_account(
841 tx.into(),
842 AccountEntry {
843 public_key: packet_2,
844 chain_addr: chain_2,
845 published_at: 2,
846 entry_type: AccountType::NotAnnounced,
847 },
848 )
849 .await?;
850 Ok::<(), DbSqlError>(())
851 })
852 })
853 .await?;
854
855 db.caches.invalidate_all();
856
857 let a: Address = db
858 .translate_key(None, packet_1)
859 .await?
860 .context("must contain key")?
861 .try_into()?;
862
863 let b: OffchainPublicKey = db
864 .translate_key(None, chain_2)
865 .await?
866 .context("must contain key")?
867 .try_into()?;
868
869 assert_eq!(chain_1, a, "chain keys must match");
870 assert_eq!(packet_2, b, "chain keys must match");
871
872 Ok(())
873 }
874
875 #[tokio::test]
876 async fn test_get_accounts() -> anyhow::Result<()> {
877 let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
878
879 let chain_1 = ChainKeypair::random().public().to_address();
880 let chain_2 = ChainKeypair::random().public().to_address();
881 let chain_3 = ChainKeypair::random().public().to_address();
882
883 let db_clone = db.clone();
884 db.begin_transaction()
885 .await?
886 .perform(|tx| {
887 Box::pin(async move {
888 db_clone
889 .insert_account(
890 Some(tx),
891 AccountEntry {
892 public_key: *OffchainKeypair::random().public(),
893 chain_addr: chain_1,
894 entry_type: AccountType::NotAnnounced,
895 published_at: 1,
896 },
897 )
898 .await?;
899 db_clone
900 .insert_account(
901 Some(tx),
902 AccountEntry {
903 public_key: *OffchainKeypair::random().public(),
904 chain_addr: chain_2,
905 entry_type: AccountType::Announced {
906 multiaddr: "/ip4/10.10.10.10/tcp/1234".parse().map_err(|_| DecodingError)?,
907 updated_block: 10,
908 },
909 published_at: 2,
910 },
911 )
912 .await?;
913 db_clone
914 .insert_account(
915 Some(tx),
916 AccountEntry {
917 public_key: *OffchainKeypair::random().public(),
918 chain_addr: chain_3,
919 entry_type: AccountType::NotAnnounced,
920 published_at: 3,
921 },
922 )
923 .await?;
924
925 db_clone
926 .insert_announcement(
927 Some(tx),
928 chain_3,
929 "/ip4/1.2.3.4/tcp/1234".parse().map_err(|_| DecodingError)?,
930 12,
931 )
932 .await?;
933 db_clone
934 .insert_announcement(
935 Some(tx),
936 chain_3,
937 "/ip4/8.8.1.1/tcp/1234".parse().map_err(|_| DecodingError)?,
938 15,
939 )
940 .await?;
941 db_clone
942 .insert_announcement(
943 Some(tx),
944 chain_3,
945 "/ip4/1.2.3.0/tcp/234".parse().map_err(|_| DecodingError)?,
946 14,
947 )
948 .await
949 })
950 })
951 .await?;
952
953 let all_accounts = db.get_accounts(None, false).await?;
954 let public_only = db.get_accounts(None, true).await?;
955
956 assert_eq!(3, all_accounts.len());
957
958 assert_eq!(2, public_only.len());
959 let acc_1 = public_only
960 .iter()
961 .find(|a| a.chain_addr.eq(&chain_2))
962 .expect("should contain 1");
963
964 let acc_2 = public_only
965 .iter()
966 .find(|a| a.chain_addr.eq(&chain_3))
967 .expect("should contain 2");
968
969 assert_eq!(
970 "/ip4/10.10.10.10/tcp/1234",
971 acc_1.get_multiaddr().expect("should have a multiaddress").to_string()
972 );
973 assert_eq!(
974 "/ip4/8.8.1.1/tcp/1234",
975 acc_2.get_multiaddr().expect("should have a multiaddress").to_string()
976 );
977
978 Ok(())
979 }
980}