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