2019-10-17 15:42:19 -07:00
|
|
|
//! The addressbook manages information about what peers exist, when they were
|
|
|
|
//! seen, and what services they provide.
|
|
|
|
|
|
|
|
use std::{
|
2019-10-17 21:19:23 -07:00
|
|
|
collections::{BTreeSet, HashMap},
|
2019-10-17 17:54:08 -07:00
|
|
|
iter::Extend,
|
2019-10-17 15:42:19 -07:00
|
|
|
net::SocketAddr,
|
|
|
|
};
|
|
|
|
|
|
|
|
use chrono::{DateTime, Utc};
|
2019-10-21 15:56:16 -07:00
|
|
|
use tracing::Span;
|
2019-10-17 15:42:19 -07:00
|
|
|
|
|
|
|
use crate::{
|
|
|
|
constants,
|
|
|
|
types::{MetaAddr, PeerServices},
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A database of peers, their advertised services, and information on when they
|
|
|
|
/// were last seen.
|
2019-10-21 15:56:16 -07:00
|
|
|
#[derive(Debug)]
|
2019-10-17 15:42:19 -07:00
|
|
|
pub struct AddressBook {
|
|
|
|
by_addr: HashMap<SocketAddr, (DateTime<Utc>, PeerServices)>,
|
2019-10-17 21:19:23 -07:00
|
|
|
by_time: BTreeSet<MetaAddr>,
|
2019-10-21 15:56:16 -07:00
|
|
|
span: Span,
|
2019-10-17 15:42:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AddressBook {
|
2019-10-21 15:56:16 -07:00
|
|
|
/// Construct an `AddressBook` with the given [`tracing::Span`].
|
|
|
|
pub fn new(span: Span) -> AddressBook {
|
|
|
|
AddressBook {
|
|
|
|
by_addr: HashMap::default(),
|
|
|
|
by_time: BTreeSet::default(),
|
|
|
|
span,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-18 11:04:38 -07:00
|
|
|
/// Check consistency of the address book invariants or panic, doing work
|
|
|
|
/// quadratic in the address book size.
|
|
|
|
#[cfg(test)]
|
2019-10-17 21:19:23 -07:00
|
|
|
fn assert_consistency(&self) {
|
|
|
|
for (a, (t, s)) in self.by_addr.iter() {
|
|
|
|
for meta in self.by_time.iter().filter(|meta| meta.addr == *a) {
|
|
|
|
if meta.last_seen != *t || meta.services != *s {
|
|
|
|
panic!("meta {:?} is not {:?}, {:?}, {:?}", meta, a, t, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-18 11:04:38 -07:00
|
|
|
/// Returns true if the address book has an entry for `addr`.
|
|
|
|
pub fn contains_addr(&self, addr: &SocketAddr) -> bool {
|
2019-10-21 15:56:16 -07:00
|
|
|
let _guard = self.span.enter();
|
2019-10-18 11:04:38 -07:00
|
|
|
self.by_addr.contains_key(addr)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the entry corresponding to `addr`, or `None` if it does not exist.
|
|
|
|
pub fn get_by_addr(&self, addr: SocketAddr) -> Option<MetaAddr> {
|
2019-10-21 15:56:16 -07:00
|
|
|
let _guard = self.span.enter();
|
2019-10-18 11:04:38 -07:00
|
|
|
let (last_seen, services) = self.by_addr.get(&addr).cloned()?;
|
|
|
|
Some(MetaAddr {
|
|
|
|
addr,
|
|
|
|
last_seen,
|
|
|
|
services,
|
|
|
|
})
|
|
|
|
}
|
2019-10-17 15:42:19 -07:00
|
|
|
|
2019-10-18 11:04:38 -07:00
|
|
|
/// Add `new` to the address book, updating the previous entry if `new` is
|
|
|
|
/// more recent or discarding `new` if it is stale.
|
|
|
|
pub fn update(&mut self, new: MetaAddr) {
|
2019-10-21 15:56:16 -07:00
|
|
|
let _guard = self.span.enter();
|
2019-10-17 21:19:23 -07:00
|
|
|
trace!(
|
2019-10-18 11:04:38 -07:00
|
|
|
?new,
|
2019-10-17 15:42:19 -07:00
|
|
|
data.total = self.by_time.len(),
|
2019-10-17 16:25:24 -07:00
|
|
|
data.recent = (self.by_time.len() - self.disconnected_peers().count()),
|
2019-10-17 15:42:19 -07:00
|
|
|
);
|
2019-10-18 11:04:38 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
self.assert_consistency();
|
2019-10-17 15:42:19 -07:00
|
|
|
|
2019-10-18 11:04:38 -07:00
|
|
|
match self.get_by_addr(new.addr) {
|
|
|
|
Some(prev) => {
|
|
|
|
if prev.last_seen > new.last_seen {
|
2019-10-17 15:42:19 -07:00
|
|
|
return;
|
2019-10-18 11:04:38 -07:00
|
|
|
} else {
|
|
|
|
self.by_time
|
|
|
|
.take(&prev)
|
|
|
|
.expect("cannot have by_addr entry without by_time entry");
|
2019-10-17 15:42:19 -07:00
|
|
|
}
|
|
|
|
}
|
2019-10-18 11:04:38 -07:00
|
|
|
None => {}
|
2019-10-17 15:42:19 -07:00
|
|
|
}
|
2019-10-18 11:04:38 -07:00
|
|
|
self.by_time.insert(new);
|
|
|
|
self.by_addr.insert(new.addr, (new.last_seen, new.services));
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
self.assert_consistency();
|
2019-10-17 15:42:19 -07:00
|
|
|
}
|
2019-10-17 16:25:24 -07:00
|
|
|
|
2019-10-18 09:54:34 -07:00
|
|
|
/// Compute a cutoff time that can determine whether an entry
|
|
|
|
/// in an address book being updated with peer message timestamps
|
|
|
|
/// represents a known-disconnected peer or a potentially-connected peer.
|
|
|
|
///
|
|
|
|
/// [`constants::LIVE_PEER_DURATION`] represents the time interval in which
|
|
|
|
/// we are guaranteed to receive at least one message from a peer or close
|
|
|
|
/// the connection. Therefore, if the last-seen timestamp is older than
|
|
|
|
/// [`constants::LIVE_PEER_DURATION`] ago, we know we must have disconnected
|
|
|
|
/// from it. Otherwise, we could potentially be connected to it.
|
|
|
|
fn cutoff_time() -> DateTime<Utc> {
|
|
|
|
// chrono uses signed durations while stdlib uses unsigned durations
|
|
|
|
use chrono::Duration as CD;
|
|
|
|
Utc::now() - CD::from_std(constants::LIVE_PEER_DURATION).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if the given [`SocketAddr`] could potentially be connected
|
|
|
|
/// to a node feeding timestamps into this address book.
|
|
|
|
pub fn is_potentially_connected(&self, addr: &SocketAddr) -> bool {
|
2019-10-21 15:56:16 -07:00
|
|
|
let _guard = self.span.enter();
|
2019-10-18 09:54:34 -07:00
|
|
|
match self.by_addr.get(addr) {
|
|
|
|
None => return false,
|
|
|
|
Some((ref last_seen, _)) => last_seen > &AddressBook::cutoff_time(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-17 16:25:24 -07:00
|
|
|
/// Return an iterator over all peers, ordered from most recently seen to
|
|
|
|
/// least recently seen.
|
|
|
|
pub fn peers<'a>(&'a self) -> impl Iterator<Item = MetaAddr> + 'a {
|
2019-10-21 15:56:16 -07:00
|
|
|
let _guard = self.span.enter();
|
2019-10-17 21:19:23 -07:00
|
|
|
self.by_time.iter().rev().cloned()
|
2019-10-17 16:25:24 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return an iterator over peers known to be disconnected, ordered from most
|
|
|
|
/// recently seen to least recently seen.
|
|
|
|
pub fn disconnected_peers<'a>(&'a self) -> impl Iterator<Item = MetaAddr> + 'a {
|
2019-10-21 15:56:16 -07:00
|
|
|
let _guard = self.span.enter();
|
2019-10-17 21:19:23 -07:00
|
|
|
use std::net::{IpAddr, Ipv4Addr};
|
2019-10-17 16:25:24 -07:00
|
|
|
use std::ops::Bound::{Excluded, Unbounded};
|
2019-10-17 21:19:23 -07:00
|
|
|
let cutoff_meta = MetaAddr {
|
2019-10-18 09:54:34 -07:00
|
|
|
last_seen: AddressBook::cutoff_time(),
|
2019-10-17 21:19:23 -07:00
|
|
|
// The ordering on MetaAddrs is newest-first, then arbitrary,
|
|
|
|
// so any fields will do here.
|
|
|
|
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0),
|
|
|
|
services: PeerServices::default(),
|
|
|
|
};
|
2019-10-17 16:25:24 -07:00
|
|
|
|
|
|
|
self.by_time
|
2019-10-17 21:19:23 -07:00
|
|
|
.range((Excluded(cutoff_meta), Unbounded))
|
2019-10-17 16:25:24 -07:00
|
|
|
.rev()
|
2019-10-17 21:19:23 -07:00
|
|
|
.cloned()
|
2019-10-17 16:25:24 -07:00
|
|
|
}
|
2019-10-17 17:54:08 -07:00
|
|
|
|
|
|
|
/// Returns an iterator that drains entries from the address book, removing
|
|
|
|
/// them in order from most recent to least recent.
|
2019-10-18 09:27:28 -07:00
|
|
|
pub fn drain_newest<'a>(&'a mut self) -> impl Iterator<Item = MetaAddr> + 'a {
|
|
|
|
Drain {
|
|
|
|
book: self,
|
|
|
|
newest_first: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns an iterator that drains entries from the address book, removing
|
2019-10-18 18:03:35 -07:00
|
|
|
/// them in order from least recent to most recent.
|
2019-10-18 09:27:28 -07:00
|
|
|
pub fn drain_oldest<'a>(&'a mut self) -> impl Iterator<Item = MetaAddr> + 'a {
|
|
|
|
Drain {
|
|
|
|
book: self,
|
|
|
|
newest_first: false,
|
|
|
|
}
|
2019-10-17 17:54:08 -07:00
|
|
|
}
|
2019-10-21 15:24:17 -07:00
|
|
|
|
|
|
|
/// Returns the number of entries in this address book.
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.by_time.len()
|
|
|
|
}
|
2019-10-17 16:25:24 -07:00
|
|
|
}
|
|
|
|
|
2019-10-17 17:54:08 -07:00
|
|
|
impl Extend<MetaAddr> for AddressBook {
|
|
|
|
fn extend<T>(&mut self, iter: T)
|
|
|
|
where
|
|
|
|
T: IntoIterator<Item = MetaAddr>,
|
|
|
|
{
|
|
|
|
for meta in iter.into_iter() {
|
|
|
|
self.update(meta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Drain<'a> {
|
|
|
|
book: &'a mut AddressBook,
|
2019-10-18 09:27:28 -07:00
|
|
|
newest_first: bool,
|
2019-10-17 17:54:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for Drain<'a> {
|
|
|
|
type Item = MetaAddr;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
2019-10-18 09:27:28 -07:00
|
|
|
let next_item = if self.newest_first {
|
|
|
|
self.book.by_time.iter().next()?.clone()
|
|
|
|
} else {
|
|
|
|
self.book.by_time.iter().rev().next()?.clone()
|
|
|
|
};
|
|
|
|
self.book.by_time.remove(&next_item);
|
2019-10-17 21:19:23 -07:00
|
|
|
self.book
|
|
|
|
.by_addr
|
2019-10-18 09:27:28 -07:00
|
|
|
.remove(&next_item.addr)
|
2019-10-17 21:19:23 -07:00
|
|
|
.expect("cannot have by_time entry without by_addr entry");
|
2019-10-18 09:27:28 -07:00
|
|
|
Some(next_item)
|
2019-10-17 17:54:08 -07:00
|
|
|
}
|
2019-10-17 21:19:23 -07:00
|
|
|
}
|