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