1use std::sync::Arc;
2
3use bimap::BiMap;
4use futures::stream::{self, BoxStream};
5use hopr_api::{
6 chain::*,
7 types::{
8 crypto::prelude::{Hash, OffchainPublicKey},
9 internal::prelude::*,
10 primitive::{
11 balance::{Balance, Currency, HoprBalance},
12 prelude::{Address, KeyIdMapping},
13 },
14 },
15};
16
17#[derive(Debug, thiserror::Error)]
19#[error("stub error")]
20pub struct StubError;
21
22#[derive(Clone)]
30pub struct StubKeyIdMapper {
31 map: Arc<BiMap<OffchainPublicKey, HoprKeyIdent>>,
32}
33
34impl KeyIdMapping<HoprKeyIdent, OffchainPublicKey> for StubKeyIdMapper {
35 fn map_key_to_id(&self, key: &OffchainPublicKey) -> Option<HoprKeyIdent> {
36 self.map.get_by_left(key).copied()
37 }
38
39 fn map_id_to_public(&self, id: &HoprKeyIdent) -> Option<OffchainPublicKey> {
40 self.map.get_by_right(id).copied()
41 }
42}
43
44#[derive(Clone)]
53pub struct StubChainApi {
54 me: Address,
55 key_addr_map: BiMap<OffchainPublicKey, Address>,
56 channels: Vec<ChannelEntry>,
57 id_mapper: StubKeyIdMapper,
58 ticket_price: HoprBalance,
59 win_prob: WinningProbability,
60}
61
62pub struct StubChainApiBuilder {
64 me: Option<Address>,
65 key_addr_map: BiMap<OffchainPublicKey, Address>,
66 key_id_map: BiMap<OffchainPublicKey, HoprKeyIdent>,
67 channels: Vec<ChannelEntry>,
68 ticket_price: HoprBalance,
69 win_prob: WinningProbability,
70 next_key_id: u32,
71}
72
73impl Default for StubChainApiBuilder {
74 fn default() -> Self {
75 Self {
76 me: None,
77 key_addr_map: BiMap::new(),
78 key_id_map: BiMap::new(),
79 channels: Vec::new(),
80 ticket_price: HoprBalance::zero(),
81 win_prob: WinningProbability::ALWAYS,
82 next_key_id: 0,
83 }
84 }
85}
86
87impl StubChainApiBuilder {
88 pub fn me(mut self, addr: Address) -> Self {
90 self.me = Some(addr);
91 self
92 }
93
94 pub fn peer(mut self, offchain: &OffchainPublicKey, chain_addr: Address) -> Self {
96 self.key_addr_map.insert(*offchain, chain_addr);
97 self.key_id_map.insert(*offchain, self.next_key_id.into());
98 self.next_key_id += 1;
99 self
100 }
101
102 pub fn channel(mut self, entry: ChannelEntry) -> Self {
104 self.channels.push(entry);
105 self
106 }
107
108 pub fn ticket_price(mut self, price: HoprBalance) -> Self {
110 self.ticket_price = price;
111 self
112 }
113
114 pub fn win_prob(mut self, prob: WinningProbability) -> Self {
116 self.win_prob = prob;
117 self
118 }
119
120 pub fn build(self) -> StubChainApi {
125 StubChainApi {
126 me: self.me.expect("me address must be set"),
127 key_addr_map: self.key_addr_map,
128 channels: self.channels,
129 id_mapper: StubKeyIdMapper {
130 map: Arc::new(self.key_id_map),
131 },
132 ticket_price: self.ticket_price,
133 win_prob: self.win_prob,
134 }
135 }
136}
137
138impl StubChainApi {
139 pub fn builder() -> StubChainApiBuilder {
141 StubChainApiBuilder::default()
142 }
143
144 pub fn key_addr_map(&self) -> &BiMap<OffchainPublicKey, Address> {
146 &self.key_addr_map
147 }
148
149 pub fn channels(&self) -> &[ChannelEntry] {
151 &self.channels
152 }
153}
154
155impl ChainKeyOperations for StubChainApi {
158 type Error = StubError;
159 type Mapper = StubKeyIdMapper;
160
161 fn chain_key_to_packet_key(&self, chain: &Address) -> Result<Option<OffchainPublicKey>, Self::Error> {
162 Ok(self.key_addr_map.get_by_right(chain).copied())
163 }
164
165 fn packet_key_to_chain_key(&self, packet: &OffchainPublicKey) -> Result<Option<Address>, Self::Error> {
166 Ok(self.key_addr_map.get_by_left(packet).copied())
167 }
168
169 fn key_id_mapper_ref(&self) -> &Self::Mapper {
170 &self.id_mapper
171 }
172}
173
174impl ChainReadChannelOperations for StubChainApi {
177 type Error = StubError;
178
179 fn me(&self) -> &Address {
180 &self.me
181 }
182
183 fn channel_by_id(&self, channel_id: &ChannelId) -> Result<Option<ChannelEntry>, Self::Error> {
184 Ok(self.channels.iter().find(|c| c.get_id() == channel_id).cloned())
185 }
186
187 fn stream_channels<'a>(&'a self, _selector: ChannelSelector) -> Result<BoxStream<'a, ChannelEntry>, Self::Error> {
188 Ok(Box::pin(stream::iter(self.channels.clone())))
189 }
190}
191
192impl ChainReadTicketOperations for StubChainApi {
193 type Error = StubError;
194
195 fn outgoing_ticket_values(
196 &self,
197 configured_wp: Option<WinningProbability>,
198 configured_price: Option<HoprBalance>,
199 ) -> Result<(WinningProbability, HoprBalance), Self::Error> {
200 Ok((
201 configured_wp.unwrap_or(self.win_prob),
202 configured_price.unwrap_or(self.ticket_price),
203 ))
204 }
205
206 fn incoming_ticket_values(&self) -> Result<(WinningProbability, HoprBalance), Self::Error> {
207 Ok((self.win_prob, self.ticket_price))
208 }
209}
210
211#[async_trait::async_trait]
214impl ChainValues for StubChainApi {
215 type Error = StubError;
216
217 async fn balance<C: Currency, A: Into<Address> + Send>(&self, _address: A) -> Result<Balance<C>, Self::Error> {
218 Ok(Balance::new_base(1_000_000))
219 }
220
221 async fn domain_separators(&self) -> Result<DomainSeparators, Self::Error> {
222 Ok(DomainSeparators {
223 ledger: Hash::default(),
224 safe_registry: Hash::default(),
225 channel: Hash::default(),
226 })
227 }
228
229 async fn minimum_incoming_ticket_win_prob(&self) -> Result<WinningProbability, Self::Error> {
230 Ok(self.win_prob)
231 }
232
233 async fn minimum_ticket_price(&self) -> Result<HoprBalance, Self::Error> {
234 Ok(self.ticket_price)
235 }
236
237 async fn key_binding_fee(&self) -> Result<HoprBalance, Self::Error> {
238 Ok(HoprBalance::zero())
239 }
240
241 async fn channel_closure_notice_period(&self) -> Result<std::time::Duration, Self::Error> {
242 Ok(std::time::Duration::from_secs(300))
243 }
244
245 async fn chain_info(&self) -> Result<ChainInfo, Self::Error> {
246 Ok(ChainInfo {
247 chain_id: 100,
248 hopr_network_name: "stub".to_string(),
249 contract_addresses: ContractAddresses::default(),
250 })
251 }
252
253 async fn redemption_stats<A: Into<Address> + Send>(&self, _: A) -> Result<RedemptionStats, Self::Error> {
254 Ok(RedemptionStats {
255 redeemed_count: 0,
256 redeemed_value: HoprBalance::zero(),
257 })
258 }
259
260 async fn typical_resolution_time(&self) -> Result<std::time::Duration, Self::Error> {
261 Ok(std::time::Duration::from_secs(15))
262 }
263}
264
265pub struct StubPathResolver {
272 key_addr_map: BiMap<OffchainPublicKey, Address>,
273 channels: Vec<ChannelEntry>,
274}
275
276impl StubPathResolver {
277 pub fn from_chain_api(api: &StubChainApi) -> Self {
279 Self {
280 key_addr_map: api.key_addr_map().clone(),
281 channels: api.channels().to_vec(),
282 }
283 }
284}
285
286#[async_trait::async_trait]
287impl PathAddressResolver for StubPathResolver {
288 async fn resolve_transport_address(
289 &self,
290 address: &Address,
291 ) -> Result<Option<OffchainPublicKey>, hopr_api::types::internal::errors::PathError> {
292 Ok(self.key_addr_map.get_by_right(address).copied())
293 }
294
295 async fn resolve_chain_address(
296 &self,
297 key: &OffchainPublicKey,
298 ) -> Result<Option<Address>, hopr_api::types::internal::errors::PathError> {
299 Ok(self.key_addr_map.get_by_left(key).copied())
300 }
301
302 async fn get_channel(
303 &self,
304 src: &Address,
305 dst: &Address,
306 ) -> Result<Option<ChannelEntry>, hopr_api::types::internal::errors::PathError> {
307 Ok(self
308 .channels
309 .iter()
310 .find(|c| &c.source == src && &c.destination == dst)
311 .cloned())
312 }
313}