1use std::{
6 hash::{DefaultHasher, Hash, Hasher},
7 io::Read,
8 net::IpAddr,
9 time::{Duration, SystemTime, UNIX_EPOCH},
10};
11
12use bitcoin::{
13 consensus,
14 p2p::{address::AddrV2, ServiceFlags},
15};
16pub mod io;
18
19const ONE_MINUTE: Duration = Duration::from_secs(60);
20const ONE_WEEK: Duration = Duration::from_secs(604800);
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct Record {
25 addr: AddrV2,
26 port: u16,
27 source: SourceId,
28 services: ServiceFlags,
29 failed_attempts: u8,
30 last_connection: Option<Duration>,
31 last_attempt: Option<Duration>,
32}
33
34impl Record {
35 fn compute_size(&self) -> u8 {
36 let mut size = 0;
37 match &self.addr {
38 AddrV2::I2p(_) => size += 34,
39 AddrV2::Ipv4(_) => size += 6,
40 AddrV2::Ipv6(_) => size += 18,
41 AddrV2::TorV2(_) => size += 12,
42 AddrV2::TorV3(_) => size += 34,
43 AddrV2::Cjdns(_) => size += 18,
44 AddrV2::Unknown(len, _) => size += *len,
45 }
46 size += 2;
48 size += self.source.0.len() as u8;
50 size += 8;
52 size += 1;
54 size += 9;
56 size += 9;
58 size
59 }
60
61 pub fn new(addr: AddrV2, port: u16, services: ServiceFlags, source: &IpAddr) -> Self {
63 let source = source.source_id();
64 Self {
65 addr,
66 port,
67 source,
68 services,
69 failed_attempts: 0,
70 last_connection: None,
71 last_attempt: None,
72 }
73 }
74
75 pub fn new_from_addrv2_source(
76 addr: AddrV2,
77 port: u16,
78 services: ServiceFlags,
79 source: &AddrV2,
80 ) -> Self {
81 let source = match source {
82 AddrV2::Ipv4(ip) => IpAddr::V4(*ip).source_id(),
83 AddrV2::Ipv6(ip) => IpAddr::V6(*ip).source_id(),
84 _ => SourceId([1u8; 4]),
86 };
87 Self {
88 addr,
89 port,
90 source,
91 services,
92 failed_attempts: 0,
93 last_connection: None,
94 last_attempt: None,
95 }
96 }
97
98 pub fn deserialize<R: Read>(reader: &mut R) -> Result<Self, std::io::Error> {
100 let mut size_buf = [0u8; 1];
101 reader.read_exact(&mut size_buf)?;
102 let size = u8::from_le_bytes(size_buf);
103 let mut content_buf = vec![0u8; size as usize];
104 reader.read_exact(&mut content_buf)?;
105 let (addr, len) =
106 consensus::deserialize_partial::<AddrV2>(&content_buf).expect("must have 33 bytes");
107 let mut content_slice = &content_buf[len..];
108 let mut port_buf = [0u8; 2];
109 content_slice.read_exact(&mut port_buf)?;
110 let port = u16::from_le_bytes(port_buf);
111 let mut source_buf = [0u8; 4];
112 content_slice.read_exact(&mut source_buf)?;
113 let source = SourceId(source_buf);
114 let mut service_buf = [0u8; 8];
115 content_slice.read_exact(&mut service_buf)?;
116 let services = ServiceFlags::from(u64::from_le_bytes(service_buf));
117 let mut failed_buf = [0u8; 1];
118 content_slice.read_exact(&mut failed_buf)?;
119 let failed_attempts = u8::from_le_bytes(failed_buf);
120 let mut record = Record {
121 addr,
122 port,
123 source,
124 services,
125 failed_attempts,
126 last_connection: None,
127 last_attempt: None,
128 };
129 let mut last_attempt_buf = [0u8; 1];
130 content_slice.read_exact(&mut last_attempt_buf)?;
131 let should_read = u8::from_le_bytes(last_attempt_buf);
132 match should_read {
133 0u8 => {
134 content_slice.read_exact(&mut [0u8; 8])?;
135 }
136 1u8 => {
137 let mut time_buf = [0u8; 8];
138 content_slice.read_exact(&mut time_buf)?;
139 let secs = u64::from_le_bytes(time_buf);
140 record.last_attempt = Some(Duration::from_secs(secs));
141 }
142 _ => panic!("invalid time encoding"),
143 }
144 let mut last_conn_buf = [0u8; 1];
145 content_slice.read_exact(&mut last_conn_buf)?;
146 let should_read = u8::from_le_bytes(last_conn_buf);
147 match should_read {
148 0u8 => {
149 content_slice.read_exact(&mut [0u8; 8])?;
150 }
151 1u8 => {
152 let mut time_buf = [0u8; 8];
153 content_slice.read_exact(&mut time_buf)?;
154 let secs = u64::from_le_bytes(time_buf);
155 record.last_connection = Some(Duration::from_secs(secs));
156 }
157 _ => panic!("invalid time encoding"),
158 }
159 Ok(record)
160 }
161
162 fn destination_id(&self) -> DestinationId {
163 self.addr.destination_id()
164 }
165
166 pub fn network_addr(&self) -> (AddrV2, u16) {
168 (self.addr.clone(), self.port)
169 }
170
171 pub fn service_flags(&self) -> ServiceFlags {
173 self.services
174 }
175
176 pub fn update_service_flags(&mut self, flags: ServiceFlags) {
178 self.services = flags;
179 }
180
181 pub fn serialize(self) -> Vec<u8> {
183 let len = self.compute_size();
184 let mut buf = Vec::with_capacity(len.into());
185 buf.push(len);
186 let addr = consensus::serialize(&self.addr);
187 buf.extend(addr);
188 buf.extend(self.port.to_le_bytes());
189 buf.extend(self.source.0);
190 buf.extend(self.services.to_u64().to_le_bytes());
191 buf.push(self.failed_attempts);
192 if let Some(last_attempt) = self.last_attempt {
193 buf.push(0x01);
194 let secs = last_attempt.as_secs().to_le_bytes();
195 buf.extend(secs);
196 } else {
197 buf.extend([0u8; 9]);
198 }
199 if let Some(last_conn) = self.last_connection {
200 buf.push(0x01);
201 let secs = last_conn.as_secs().to_le_bytes();
202 buf.extend(secs);
203 } else {
204 buf.extend([0u8; 9]);
205 }
206 buf
207 }
208
209 pub fn is_terrible(&self, maximum_tries: u8, maximum_weekly_tries: u8) -> bool {
212 if let Some(attempt) = self.last_attempt {
213 if attempt < ONE_MINUTE {
214 return false;
215 }
216 if self.failed_attempts > maximum_weekly_tries && attempt < ONE_WEEK {
217 return true;
218 }
219 }
220 if self.failed_attempts > maximum_tries {
221 return true;
222 }
223 false
224 }
225}
226
227#[derive(Debug)]
242pub struct Table<const B: usize, const S: usize, const W: usize> {
243 buckets: [Bucket<S>; B],
244}
245
246impl<const B: usize, const S: usize, const W: usize> Table<B, S, W> {
247 const RUN: usize = B / W;
249
250 fn derive_bucket(record: &Record) -> usize {
258 let salt = u32::from_le_bytes(record.source.0) as usize % W;
259 let range = (salt * Self::RUN) % B;
260 let index = u32::from_le_bytes(record.destination_id().0) as usize % W;
261 (range + index) % B
262 }
263
264 fn random_bucket() -> usize {
266 let mut hasher = DefaultHasher::new();
267 SystemTime::now().hash(&mut hasher);
268 u32::from_le_bytes(
269 hasher.finish().to_le_bytes()[..4]
270 .try_into()
271 .expect("hash is u64"),
272 ) as usize
273 % B
274 }
275
276 fn random_slot() -> usize {
277 let mut hasher = DefaultHasher::new();
278 SystemTime::now().hash(&mut hasher);
279 u32::from_le_bytes(
280 hasher.finish().to_le_bytes()[..4]
281 .try_into()
282 .expect("hash is u64"),
283 ) as usize
284 % S
285 }
286
287 fn random_from_bucket(bucket: &Bucket<S>) -> Option<Record> {
288 if bucket.is_empty() {
289 return None;
290 }
291 let slot_index = Self::random_slot();
292 let mut tmp = (slot_index + 1) % S;
293 while tmp.ne(&slot_index) {
294 let record = bucket.get(tmp);
295 if let Some(record) = record {
296 let Some(last_attempt) = record.last_attempt else {
297 return Some(record);
298 };
299 if last_attempt > ONE_MINUTE {
300 return Some(record);
301 }
302 }
303 tmp = (tmp + 1) % S;
304 }
305 bucket.get(slot_index)
306 }
307
308 pub fn new() -> Self {
310 let buckets: [Bucket<S>; B] = [const { Bucket::new() }; B];
311 Self { buckets }
312 }
313
314 pub fn add(&mut self, record: &Record) -> Option<Record> {
320 let bucket_index = Self::derive_bucket(record);
321 self.buckets[bucket_index].add(record.clone())
322 }
323
324 pub fn remove(&mut self, record: &Record) {
326 let bucket_index = Self::derive_bucket(record);
327 self.buckets[bucket_index].remove(record);
328 }
329
330 pub fn count(&self, record: &Record) -> usize {
332 self.buckets
333 .iter()
334 .filter(|bucket| bucket.has_record(record))
335 .count()
336 }
337
338 pub fn is_empty(&self) -> bool {
340 self.buckets.iter().all(|bucket| bucket.is_empty())
341 }
342
343 pub fn select(&self) -> Option<Record> {
350 if self.is_empty() {
351 return None;
352 };
353 let bucket_index = Self::random_bucket();
354 let bucket = &self.buckets[bucket_index];
355 if bucket.is_empty() {
356 let mut tmp = (bucket_index + 1) % B;
357 while tmp.ne(&bucket_index) {
358 let bucket = &self.buckets[tmp];
359 let random_record = Self::random_from_bucket(bucket);
360 if random_record.is_some() {
361 return random_record;
362 }
363 tmp = (tmp + 1) % B;
364 }
365 None
366 } else {
367 Self::random_from_bucket(bucket)
368 }
369 }
370
371 pub fn successful_connection(&mut self, record: &Record) {
373 let bucket_index = Self::derive_bucket(record);
374 let bucket = &mut self.buckets[bucket_index];
375 bucket.successful_connection(record);
376 }
377
378 pub fn failed_connection(&mut self, record: &Record) {
380 let bucket_index = Self::derive_bucket(record);
381 let bucket = &mut self.buckets[bucket_index];
382 bucket.failed_connection(record);
383 }
384}
385
386impl<const B: usize, const S: usize, const W: usize> Default for Table<B, S, W> {
387 fn default() -> Self {
388 Table::<B, S, W>::new()
389 }
390}
391
392#[derive(Debug)]
393struct Bucket<const S: usize> {
394 records: [Option<Record>; S],
395}
396
397impl<const S: usize> Bucket<S> {
398 fn derive_slot(record: &Record) -> usize {
399 let index = u32::from_le_bytes(record.destination_id().0) as usize;
400 index % S
401 }
402
403 const fn new() -> Self {
404 let records: [Option<Record>; S] = [const { None }; S];
405 Self { records }
406 }
407
408 fn add(&mut self, record: Record) -> Option<Record> {
409 let slot = Self::derive_slot(&record);
410 match &self.records[slot] {
411 Some(occupied) => Some(occupied.clone()),
412 None => {
413 self.records[slot] = Some(record);
414 None
415 }
416 }
417 }
418
419 fn remove(&mut self, record: &Record) {
420 let slot = Self::derive_slot(record);
421 self.records[slot] = None;
422 }
423
424 fn has_record(&self, record: &Record) -> bool {
425 let slot = Self::derive_slot(record);
426 match &self.records[slot] {
427 Some(cmp) => cmp.eq(record),
428 None => false,
429 }
430 }
431
432 fn is_empty(&self) -> bool {
433 self.records.iter().all(|record| record.is_none())
434 }
435
436 fn successful_connection(&mut self, record: &Record) {
437 let slot = Self::derive_slot(record);
438 let new_flags = record.services;
439 if let Some(record) = &mut self.records[slot] {
440 record.last_attempt = Some(
441 SystemTime::now()
442 .duration_since(UNIX_EPOCH)
443 .expect("time went backwards"),
444 );
445 record.last_connection = Some(
446 SystemTime::now()
447 .duration_since(UNIX_EPOCH)
448 .expect("time went backwards"),
449 );
450 record.failed_attempts = 0;
451 record.services = new_flags;
452 }
453 }
454
455 fn failed_connection(&mut self, record: &Record) {
456 let slot = Self::derive_slot(record);
457 if let Some(record) = &mut self.records[slot] {
458 record.last_attempt = Some(
459 SystemTime::now()
460 .duration_since(UNIX_EPOCH)
461 .expect("time went backwards"),
462 );
463 record.failed_attempts += 1;
464 }
465 }
466
467 fn get(&self, index: usize) -> Option<Record> {
468 let index = index % S;
469 self.records[index].clone()
470 }
471}
472
473#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash)]
474struct SourceId([u8; 4]);
475
476trait SourceIdExt {
477 fn source_id(&self) -> SourceId;
478}
479
480impl SourceIdExt for IpAddr {
481 fn source_id(&self) -> SourceId {
482 let mut hasher = DefaultHasher::new();
483 match self {
484 Self::V4(ipv4) => {
485 let octets = ipv4.octets();
486 let first_two_octets = [octets[0], octets[1]];
487 first_two_octets.hash(&mut hasher);
488 let hash = hasher.finish();
489 let bytes: [u8; 4] = hash.to_le_bytes()[..4].try_into().expect("hash is u64");
490 SourceId(bytes)
491 }
492 Self::V6(ipv6) => {
493 let octets = ipv6.octets();
494 let first_four_octets = [octets[0], octets[1], octets[2], octets[3]];
495 first_four_octets.hash(&mut hasher);
496 let hash = hasher.finish();
497 let bytes = hash.to_le_bytes()[..4].try_into().expect("hash is u64");
498 SourceId(bytes)
499 }
500 }
501 }
502}
503
504#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash)]
505struct DestinationId([u8; 4]);
506
507trait DestinationIdExt {
508 fn destination_id(&self) -> DestinationId;
509}
510
511impl DestinationIdExt for AddrV2 {
512 fn destination_id(&self) -> DestinationId {
513 let mut hasher = DefaultHasher::new();
514 match self {
515 Self::Ipv4(ipv4) => {
516 ipv4.octets().hash(&mut hasher);
517 DestinationId(
518 hasher.finish().to_le_bytes()[..4]
519 .try_into()
520 .expect("hash is u64"),
521 )
522 }
523 Self::Ipv6(ipv6) => {
524 ipv6.octets().hash(&mut hasher);
525 DestinationId(
526 hasher.finish().to_le_bytes()[..4]
527 .try_into()
528 .expect("hash is u64"),
529 )
530 }
531 Self::I2p(i2p) => {
532 i2p.hash(&mut hasher);
533 DestinationId(
534 hasher.finish().to_le_bytes()[..4]
535 .try_into()
536 .expect("hash is u64"),
537 )
538 }
539 Self::TorV3(tv3) => {
540 tv3.hash(&mut hasher);
541 DestinationId(
542 hasher.finish().to_le_bytes()[..4]
543 .try_into()
544 .expect("hash is u64"),
545 )
546 }
547 Self::Cjdns(ipv6) => {
548 ipv6.octets().hash(&mut hasher);
549 DestinationId(
550 hasher.finish().to_le_bytes()[..4]
551 .try_into()
552 .expect("hash is u64"),
553 )
554 }
555 _ => {
556 "unknown network address".hash(&mut hasher);
557 DestinationId(
558 hasher.finish().to_le_bytes()[..4]
559 .try_into()
560 .expect("hash is u64"),
561 )
562 }
563 }
564 }
565}
566
567#[cfg(test)]
568mod tests {
569 use std::{
570 hash::{DefaultHasher, Hash, Hasher},
571 net::{IpAddr, Ipv4Addr},
572 time::SystemTime,
573 };
574
575 use bitcoin::p2p::{address::AddrV2, ServiceFlags};
576
577 use crate::{Record, Table};
578
579 const LOCAL_HOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
580 const DUMB: AddrV2 = AddrV2::Ipv4(LOCAL_HOST);
581
582 const BUCKETS: usize = 256;
583 const SLOTS: usize = 16;
584 const RANGE: usize = 16;
585
586 pub fn random_record() -> Record {
587 let mut hasher = DefaultHasher::new();
588 let now = SystemTime::now();
589 now.hash(&mut hasher);
590 let bytes = hasher.finish();
591 let ip = bytes.to_le_bytes();
592 let dest = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]);
593 let source = Ipv4Addr::new(ip[4], ip[5], ip[6], ip[7]);
594 let now = SystemTime::now();
595 now.hash(&mut hasher);
596 let bytes = hasher.finish();
597 let addr_v2 = AddrV2::Ipv4(dest);
598 let mut record = Record::new(addr_v2, 8333, ServiceFlags::NETWORK, &IpAddr::V4(source));
599 record.failed_attempts += bytes.to_le_bytes()[0];
600 record
601 }
602
603 #[test]
604 fn test_simple_table_situations() {
605 let mut table = Table::<BUCKETS, SLOTS, RANGE>::new();
606 assert!(table.is_empty());
607 assert!(table.select().is_none());
608 let record = Record::new(DUMB, 8333, ServiceFlags::NONE, &IpAddr::V4(LOCAL_HOST));
609 table.add(&record);
610 assert!(!table.is_empty());
611 for _ in 0..BUCKETS * SLOTS {
613 assert!(table.select().is_some());
614 }
615 for _ in 0..BUCKETS * SLOTS {
617 assert!(table.add(&record).is_some());
618 }
619 assert_eq!(table.count(&record), 1);
620 }
621
622 #[test]
623 fn test_encoding_roundtrip() {
624 for _ in 0..BUCKETS * SLOTS {
625 let want = random_record();
626 let bytes = want.clone().serialize();
627 let got = Record::deserialize(&mut bytes.as_slice()).unwrap();
628 assert_eq!(want, got);
629 }
630 }
631}