Skip to content

Commit 30a5aad

Browse files
shumkovlklimek
andauthored
feat(sdk)!: ban addresses failed in sdk (#2351)
Co-authored-by: lklimek <[email protected]>
1 parent d8eb951 commit 30a5aad

File tree

17 files changed

+245
-194
lines changed

17 files changed

+245
-194
lines changed

packages/rs-dapi-client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ lru = { version = "0.12.3" }
3737
serde = { version = "1.0.197", optional = true, features = ["derive"] }
3838
serde_json = { version = "1.0.120", optional = true }
3939
chrono = { version = "0.4.38", features = ["serde"] }
40+
4041
[dev-dependencies]
4142
tokio = { version = "1.40", features = ["macros"] }

packages/rs-dapi-client/src/address_list.rs

Lines changed: 97 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,20 @@ use chrono::Utc;
44
use dapi_grpc::tonic::codegen::http;
55
use dapi_grpc::tonic::transport::Uri;
66
use rand::{rngs::SmallRng, seq::IteratorRandom, SeedableRng};
7-
use std::collections::HashSet;
7+
use std::collections::hash_map::Entry;
8+
use std::collections::HashMap;
89
use std::hash::{Hash, Hasher};
10+
use std::mem;
911
use std::str::FromStr;
12+
use std::sync::{Arc, RwLock};
1013
use std::time::Duration;
1114

1215
const DEFAULT_BASE_BAN_PERIOD: Duration = Duration::from_secs(60);
1316

1417
/// DAPI address.
1518
#[derive(Debug, Clone, Eq)]
1619
#[cfg_attr(feature = "mocks", derive(serde::Serialize, serde::Deserialize))]
17-
pub struct Address {
18-
ban_count: usize,
19-
banned_until: Option<chrono::DateTime<Utc>>,
20-
#[cfg_attr(feature = "mocks", serde(with = "http_serde::uri"))]
21-
uri: Uri,
22-
}
20+
pub struct Address(#[cfg_attr(feature = "mocks", serde(with = "http_serde::uri"))] Uri);
2321

2422
impl FromStr for Address {
2523
type Err = AddressListError;
@@ -33,35 +31,46 @@ impl FromStr for Address {
3331

3432
impl PartialEq<Self> for Address {
3533
fn eq(&self, other: &Self) -> bool {
36-
self.uri == other.uri
34+
self.0 == other.0
3735
}
3836
}
3937

4038
impl PartialEq<Uri> for Address {
4139
fn eq(&self, other: &Uri) -> bool {
42-
self.uri == *other
40+
self.0 == *other
4341
}
4442
}
4543

4644
impl Hash for Address {
4745
fn hash<H: Hasher>(&self, state: &mut H) {
48-
self.uri.hash(state);
46+
self.0.hash(state);
4947
}
5048
}
5149

5250
impl From<Uri> for Address {
5351
fn from(uri: Uri) -> Self {
54-
Address {
55-
ban_count: 0,
56-
banned_until: None,
57-
uri,
58-
}
52+
Address(uri)
5953
}
6054
}
6155

6256
impl Address {
57+
/// Get [Uri] of a node.
58+
pub fn uri(&self) -> &Uri {
59+
&self.0
60+
}
61+
}
62+
63+
/// Address status
64+
/// Contains information about the number of bans and the time until the next ban is lifted.
65+
#[derive(Debug, Default, Clone)]
66+
pub struct AddressStatus {
67+
ban_count: usize,
68+
banned_until: Option<chrono::DateTime<Utc>>,
69+
}
70+
71+
impl AddressStatus {
6372
/// Ban the [Address] so it won't be available through [AddressList::get_live_address] for some time.
64-
fn ban(&mut self, base_ban_period: &Duration) {
73+
pub fn ban(&mut self, base_ban_period: &Duration) {
6574
let coefficient = (self.ban_count as f64).exp();
6675
let ban_period = Duration::from_secs_f64(base_ban_period.as_secs_f64() * coefficient);
6776

@@ -75,24 +84,16 @@ impl Address {
7584
}
7685

7786
/// Clears ban record.
78-
fn unban(&mut self) {
87+
pub fn unban(&mut self) {
7988
self.ban_count = 0;
8089
self.banned_until = None;
8190
}
82-
83-
/// Get [Uri] of a node.
84-
pub fn uri(&self) -> &Uri {
85-
&self.uri
86-
}
8791
}
8892

8993
/// [AddressList] errors
9094
#[derive(Debug, thiserror::Error)]
9195
#[cfg_attr(feature = "mocks", derive(serde::Serialize, serde::Deserialize))]
9296
pub enum AddressListError {
93-
/// Specified address is not present in the list
94-
#[error("address {0} not found in the list")]
95-
AddressNotFound(#[cfg_attr(feature = "mocks", serde(with = "http_serde::uri"))] Uri),
9697
/// A valid uri is required to create an Address
9798
#[error("unable parse address: {0}")]
9899
#[cfg_attr(feature = "mocks", serde(skip))]
@@ -103,7 +104,7 @@ pub enum AddressListError {
103104
/// for [DapiRequest](crate::DapiRequest) execution.
104105
#[derive(Debug, Clone)]
105106
pub struct AddressList {
106-
addresses: HashSet<Address>,
107+
addresses: Arc<RwLock<HashMap<Address, AddressStatus>>>,
107108
base_ban_period: Duration,
108109
}
109110

@@ -115,7 +116,7 @@ impl Default for AddressList {
115116

116117
impl std::fmt::Display for Address {
117118
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118-
self.uri.fmt(f)
119+
self.0.fmt(f)
119120
}
120121
}
121122

@@ -128,43 +129,70 @@ impl AddressList {
128129
/// Creates an empty [AddressList] with adjustable base ban time.
129130
pub fn with_settings(base_ban_period: Duration) -> Self {
130131
AddressList {
131-
addresses: HashSet::new(),
132+
addresses: Arc::new(RwLock::new(HashMap::new())),
132133
base_ban_period,
133134
}
134135
}
135136

136137
/// Bans address
137-
pub(crate) fn ban_address(&mut self, address: &Address) -> Result<(), AddressListError> {
138-
if !self.addresses.remove(address) {
139-
return Err(AddressListError::AddressNotFound(address.uri.clone()));
140-
};
138+
/// Returns false if the address is not in the list.
139+
pub fn ban(&self, address: &Address) -> bool {
140+
let mut guard = self.addresses.write().unwrap();
141141

142-
let mut banned_address = address.clone();
143-
banned_address.ban(&self.base_ban_period);
142+
let Some(status) = guard.get_mut(address) else {
143+
return false;
144+
};
144145

145-
self.addresses.insert(banned_address);
146+
status.ban(&self.base_ban_period);
146147

147-
Ok(())
148+
true
148149
}
149150

150151
/// Clears address' ban record
151-
pub(crate) fn unban_address(&mut self, address: &Address) -> Result<(), AddressListError> {
152-
if !self.addresses.remove(address) {
153-
return Err(AddressListError::AddressNotFound(address.uri.clone()));
152+
/// Returns false if the address is not in the list.
153+
pub fn unban(&self, address: &Address) -> bool {
154+
let mut guard = self.addresses.write().unwrap();
155+
156+
let Some(status) = guard.get_mut(address) else {
157+
return false;
154158
};
155159

156-
let mut unbanned_address = address.clone();
157-
unbanned_address.unban();
160+
status.unban();
161+
162+
true
163+
}
158164

159-
self.addresses.insert(unbanned_address);
165+
/// Check if the address is banned.
166+
pub fn is_banned(&self, address: &Address) -> bool {
167+
let guard = self.addresses.read().unwrap();
160168

161-
Ok(())
169+
guard
170+
.get(address)
171+
.map(|status| status.is_banned())
172+
.unwrap_or(false)
162173
}
163174

164175
/// Adds a node [Address] to [AddressList]
165176
/// Returns false if the address is already in the list.
166177
pub fn add(&mut self, address: Address) -> bool {
167-
self.addresses.insert(address)
178+
let mut guard = self.addresses.write().unwrap();
179+
180+
match guard.entry(address) {
181+
Entry::Occupied(_) => false,
182+
Entry::Vacant(e) => {
183+
e.insert(AddressStatus::default());
184+
185+
true
186+
}
187+
}
188+
}
189+
190+
/// Remove address from the list
191+
/// Returns [AddressStatus] if the address was in the list.
192+
pub fn remove(&mut self, address: &Address) -> Option<AddressStatus> {
193+
let mut guard = self.addresses.write().unwrap();
194+
195+
guard.remove(address)
168196
}
169197

170198
// TODO: this is the most simple way to add an address
@@ -173,46 +201,53 @@ impl AddressList {
173201
/// Add a node [Address] to [AddressList] by [Uri].
174202
/// Returns false if the address is already in the list.
175203
pub fn add_uri(&mut self, uri: Uri) -> bool {
176-
self.addresses.insert(uri.into())
204+
self.add(Address::from(uri))
177205
}
178206

179207
/// Randomly select a not banned address.
180-
pub fn get_live_address(&self) -> Option<&Address> {
181-
let mut rng = SmallRng::from_entropy();
208+
pub fn get_live_address(&self) -> Option<Address> {
209+
let guard = self.addresses.read().unwrap();
182210

183-
self.unbanned().into_iter().choose(&mut rng)
184-
}
211+
let mut rng = SmallRng::from_entropy();
185212

186-
/// Get all addresses that are not banned.
187-
fn unbanned(&self) -> Vec<&Address> {
188213
let now = chrono::Utc::now();
189214

190-
self.addresses
215+
guard
191216
.iter()
192-
.filter(|addr| {
193-
addr.banned_until
217+
.filter(|(_, status)| {
218+
status
219+
.banned_until
194220
.map(|banned_until| banned_until < now)
195221
.unwrap_or(true)
196222
})
197-
.collect()
198-
}
199-
200-
/// Get number of available, not banned addresses.
201-
pub fn available(&self) -> usize {
202-
self.unbanned().len()
223+
.choose(&mut rng)
224+
.map(|(addr, _)| addr.clone())
203225
}
204226

205227
/// Get number of all addresses, both banned and not banned.
206228
pub fn len(&self) -> usize {
207-
self.addresses.len()
229+
self.addresses.read().unwrap().len()
208230
}
209231

210232
/// Check if the list is empty.
211233
/// Returns true if there are no addresses in the list.
212234
/// Returns false if there is at least one address in the list.
213235
/// Banned addresses are also counted.
214236
pub fn is_empty(&self) -> bool {
215-
self.addresses.is_empty()
237+
self.addresses.read().unwrap().is_empty()
238+
}
239+
}
240+
241+
impl IntoIterator for AddressList {
242+
type Item = (Address, AddressStatus);
243+
type IntoIter = std::collections::hash_map::IntoIter<Address, AddressStatus>;
244+
245+
fn into_iter(self) -> Self::IntoIter {
246+
let mut guard = self.addresses.write().unwrap();
247+
248+
let addresses_map = mem::take(&mut *guard);
249+
250+
addresses_map.into_iter()
216251
}
217252
}
218253

@@ -238,12 +273,3 @@ impl FromIterator<Uri> for AddressList {
238273
address_list
239274
}
240275
}
241-
242-
impl IntoIterator for AddressList {
243-
type Item = Address;
244-
type IntoIter = std::collections::hash_set::IntoIter<Address>;
245-
246-
fn into_iter(self) -> Self::IntoIter {
247-
self.addresses.into_iter()
248-
}
249-
}

0 commit comments

Comments
 (0)