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