hopr_chain_connector/backend/
mod.rs1mod tempdb;
2
3pub use hopr_api::{
4 chain::HoprKeyIdent,
5 types::{
6 crypto::prelude::OffchainPublicKey,
7 internal::{
8 account::AccountEntry,
9 channels::{ChannelEntry, ChannelId},
10 },
11 primitive::prelude::Address,
12 },
13};
14
15pub trait Backend {
17 type Error: std::error::Error + Send + Sync + 'static;
18 fn insert_account(&self, entry: AccountEntry) -> Result<Option<AccountEntry>, Self::Error>;
22 fn insert_channel(&self, channel: ChannelEntry) -> Result<Option<ChannelEntry>, Self::Error>;
26 fn get_account_by_id(&self, id: &HoprKeyIdent) -> Result<Option<AccountEntry>, Self::Error>;
28 fn get_account_by_key(&self, key: &OffchainPublicKey) -> Result<Option<AccountEntry>, Self::Error>;
30 fn get_account_by_address(&self, chain_key: &Address) -> Result<Option<AccountEntry>, Self::Error>;
32 fn get_channel_by_id(&self, id: &ChannelId) -> Result<Option<ChannelEntry>, Self::Error>;
34}
35
36pub use tempdb::{TempDbBackend, TempDbError};
37
38#[cfg(any(test, feature = "testing"))]
42#[derive(Clone)]
43pub struct InMemoryBackend {
44 accounts: std::sync::Arc<dashmap::DashMap<HoprKeyIdent, AccountEntry, ahash::RandomState>>,
45 channels: std::sync::Arc<dashmap::DashMap<ChannelId, ChannelEntry, ahash::RandomState>>,
46}
47
48#[cfg(any(test, feature = "testing"))]
49const DEFAULT_INMEMORY_BACKEND_CAPACITY: usize = 1024;
50
51#[cfg(any(test, feature = "testing"))]
52impl Default for InMemoryBackend {
53 fn default() -> Self {
54 Self {
55 accounts: dashmap::DashMap::with_capacity_and_hasher(
56 DEFAULT_INMEMORY_BACKEND_CAPACITY,
57 ahash::RandomState::default(),
58 )
59 .into(),
60 channels: dashmap::DashMap::with_capacity_and_hasher(
61 DEFAULT_INMEMORY_BACKEND_CAPACITY,
62 ahash::RandomState::default(),
63 )
64 .into(),
65 }
66 }
67}
68
69#[cfg(any(test, feature = "testing"))]
70impl Backend for InMemoryBackend {
71 type Error = std::convert::Infallible;
72
73 fn insert_account(&self, entry: AccountEntry) -> Result<Option<AccountEntry>, Self::Error> {
74 Ok(self.accounts.insert(entry.key_id, entry))
75 }
76
77 fn insert_channel(&self, channel: ChannelEntry) -> Result<Option<ChannelEntry>, Self::Error> {
78 Ok(self.channels.insert(*channel.get_id(), channel))
79 }
80
81 fn get_account_by_id(&self, id: &HoprKeyIdent) -> Result<Option<AccountEntry>, Self::Error> {
82 Ok(self.accounts.get(id).map(|e| e.value().clone()))
83 }
84
85 fn get_account_by_key(&self, key: &OffchainPublicKey) -> Result<Option<AccountEntry>, Self::Error> {
86 Ok(self
87 .accounts
88 .iter()
89 .find(|account| &account.public_key == key)
90 .map(|account| account.value().clone()))
91 }
92
93 fn get_account_by_address(&self, chain_key: &Address) -> Result<Option<AccountEntry>, Self::Error> {
94 Ok(self
95 .accounts
96 .iter()
97 .find(|account| &account.chain_addr == chain_key)
98 .map(|account| account.value().clone()))
99 }
100
101 fn get_channel_by_id(&self, id: &ChannelId) -> Result<Option<ChannelEntry>, Self::Error> {
102 Ok(self.channels.get(id).map(|e| *e.value()))
103 }
104}
105
106#[cfg(test)]
107pub(crate) mod tests {
108 use hopr_api::types::{
109 crypto::keypairs::{ChainKeypair, Keypair, OffchainKeypair},
110 internal::{
111 account::{AccountEntry, AccountType},
112 channels::{ChannelEntry, ChannelStatus, generate_channel_id},
113 },
114 primitive::{balance::HoprBalance, prelude::Address},
115 };
116
117 use crate::{Backend, InMemoryBackend};
118
119 pub(crate) fn test_backend<B: Backend>(backend: B) -> anyhow::Result<()> {
120 let kp = OffchainKeypair::random();
121 let cp = ChainKeypair::random();
122
123 let account = AccountEntry {
124 public_key: (*kp.public()),
125 chain_addr: cp.public().to_address(),
126 entry_type: AccountType::Announced(vec!["/ip4/1.2.3.4/tcp/1234".parse()?]),
127 safe_address: Some(Address::new(&[3u8; 20])),
128 key_id: 3.into(),
129 };
130
131 let src = Address::new(&[1u8; 20]);
132 let dst = Address::new(&[2u8; 20]);
133
134 let channel = ChannelEntry::builder()
135 .between(src, dst)
136 .balance(HoprBalance::new_base(1000))
137 .ticket_index(10u32.into())
138 .status(ChannelStatus::PendingToClose(std::time::SystemTime::now()))
139 .epoch(10u32)
140 .build()?;
141
142 backend.insert_account(account.clone())?;
143 backend.insert_channel(channel)?;
144
145 let a1 = backend
146 .get_account_by_id(&account.key_id)?
147 .ok_or(anyhow::anyhow!("account not found"))?;
148 let a2 = backend
149 .get_account_by_key(kp.public())?
150 .ok_or(anyhow::anyhow!("account not found"))?;
151 let a3 = backend
152 .get_account_by_address(cp.public().as_ref())?
153 .ok_or(anyhow::anyhow!("account not found"))?;
154
155 assert_eq!(a1, account);
156 assert_eq!(a2, account);
157 assert_eq!(a3, account);
158
159 let id = generate_channel_id(&src, &dst);
160 let c1 = backend
161 .get_channel_by_id(&id)?
162 .ok_or(anyhow::anyhow!("channel not found"))?;
163
164 assert_eq!(c1, channel);
165
166 Ok(())
167 }
168
169 #[test]
170 fn test_inmemory() -> anyhow::Result<()> {
171 let backend = InMemoryBackend::default();
172 test_backend(backend)
173 }
174}