use std::time::Instant;
use chrono::Utc;
use tracing::Span;
use zakura_chain::{
parameters::Network::*,
serialization::{DateTime32, Duration32},
};
use crate::{
constants::{DEFAULT_MAX_CONNS_PER_IP, MAX_ADDRS_IN_ADDRESS_BOOK, MAX_PEER_MISBEHAVIOR_SCORE},
meta_addr::{MetaAddr, MetaAddrChange},
protocol::external::types::PeerServices,
AddressBook,
};
#[test]
fn address_book_empty() {
let address_book = AddressBook::new(
"0.0.0.0:0".parse().unwrap(),
&Mainnet,
DEFAULT_MAX_CONNS_PER_IP,
Span::current(),
);
assert_eq!(
address_book
.reconnection_peers(Instant::now(), Utc::now())
.next(),
None
);
assert_eq!(address_book.len(), 0);
}
#[test]
fn peer_address_exposure_requires_explicit_opt_in() {
let address_book = AddressBook::new(
"0.0.0.0:0".parse().unwrap(),
&Mainnet,
DEFAULT_MAX_CONNS_PER_IP,
Span::current(),
);
assert!(!address_book.expose_peer_addresses);
let address_book = address_book.with_expose_peer_addresses(true);
assert!(address_book.expose_peer_addresses);
assert!(address_book.clone().expose_peer_addresses);
}
fn gossiped_change(
addr: crate::PeerSocketAddr,
services: PeerServices,
untrusted_last_seen: DateTime32,
) -> MetaAddrChange {
MetaAddr::new_gossiped_meta_addr(addr, services, untrusted_last_seen)
.new_gossiped_change()
.expect("gossiped MetaAddr should produce a NewGossiped change")
}
#[test]
fn misbehavior_ban_removes_all_addresses_for_ip() {
let banned_addr: crate::PeerSocketAddr = "127.0.0.1:8233".parse().unwrap();
let other_port_same_ip: crate::PeerSocketAddr = "127.0.0.1:8234".parse().unwrap();
let unrelated_addr: crate::PeerSocketAddr = "127.0.0.2:8233".parse().unwrap();
let mut address_book =
AddressBook::new("0.0.0.0:0".parse().unwrap(), &Mainnet, 2, Span::current());
let mut address_metrics = address_book.address_metrics_watcher();
address_book.update(gossiped_change(
banned_addr,
PeerServices::NODE_NETWORK,
DateTime32::MIN,
));
address_book.update(gossiped_change(
other_port_same_ip,
PeerServices::NODE_NETWORK,
DateTime32::MIN.saturating_add(Duration32::from_seconds(1)),
));
address_book.update(gossiped_change(
unrelated_addr,
PeerServices::NODE_NETWORK,
DateTime32::MIN.saturating_add(Duration32::from_seconds(2)),
));
address_book.peers.assert_consistent();
address_book.update(MetaAddr::new_reconnect(banned_addr));
address_book.update(MetaAddr::new_responded(banned_addr, None));
address_book.update(MetaAddr::new_reconnect(unrelated_addr));
address_book.update(MetaAddr::new_responded(unrelated_addr, None));
assert!(address_book.get(banned_addr).is_some());
assert!(address_book.get(other_port_same_ip).is_some());
let ordered_addrs: Vec<_> = address_book.peers().map(|peer| peer.addr()).collect();
let same_ip_positions: Vec<_> = ordered_addrs
.iter()
.enumerate()
.filter_map(|(index, addr)| (addr.ip() == banned_addr.ip()).then_some(index))
.collect();
assert_eq!(same_ip_positions.len(), 2);
assert!(same_ip_positions[1] > same_ip_positions[0] + 1);
let bans = address_book.bans();
assert_eq!(address_metrics.borrow_and_update().num_addresses, 3);
address_book.update(MetaAddrChange::UpdateMisbehavior {
addr: banned_addr,
score_increment: MAX_PEER_MISBEHAVIOR_SCORE,
});
address_book.peers.assert_consistent();
assert!(
bans.contains(banned_addr.ip()),
"ban-threshold misbehavior should ban the peer IP"
);
assert!(
address_book.get(banned_addr).is_none(),
"primary banned address should be removed from the address book"
);
assert!(
address_book.get(other_port_same_ip).is_none(),
"all addresses for the banned IP should be removed from the address book"
);
assert!(
address_book.get(unrelated_addr).is_some(),
"unrelated IP entries should remain after banning a different IP"
);
assert!(
address_metrics.has_changed().unwrap(),
"the ban should publish updated address metrics"
);
assert_eq!(
address_metrics.borrow_and_update().num_addresses,
1,
"published metrics should exclude all addresses on the banned IP"
);
}
#[test]
fn address_book_peer_order() {
let addr1 = "127.0.0.1:1".parse().unwrap();
let addr2 = "127.0.0.2:2".parse().unwrap();
let mut meta_addr1 =
MetaAddr::new_gossiped_meta_addr(addr1, PeerServices::NODE_NETWORK, DateTime32::MIN);
let mut meta_addr2 = MetaAddr::new_gossiped_meta_addr(
addr2,
PeerServices::NODE_NETWORK,
DateTime32::MIN.saturating_add(Duration32::from_seconds(1)),
);
let addrs = vec![meta_addr1, meta_addr2];
let address_book = AddressBook::new_with_addrs(
"0.0.0.0:0".parse().unwrap(),
&Mainnet,
DEFAULT_MAX_CONNS_PER_IP,
MAX_ADDRS_IN_ADDRESS_BOOK,
Span::current(),
addrs,
);
assert_eq!(
address_book
.reconnection_peers(Instant::now(), Utc::now())
.next(),
Some(meta_addr2),
);
let addrs = vec![meta_addr2, meta_addr1];
let address_book = AddressBook::new_with_addrs(
"0.0.0.0:0".parse().unwrap(),
&Mainnet,
DEFAULT_MAX_CONNS_PER_IP,
MAX_ADDRS_IN_ADDRESS_BOOK,
Span::current(),
addrs,
);
assert_eq!(
address_book
.reconnection_peers(Instant::now(), Utc::now())
.next(),
Some(meta_addr2),
);
meta_addr1.addr = addr2;
meta_addr2.addr = addr1;
let addrs = vec![meta_addr1, meta_addr2];
let address_book = AddressBook::new_with_addrs(
"0.0.0.0:0".parse().unwrap(),
&Mainnet,
DEFAULT_MAX_CONNS_PER_IP,
MAX_ADDRS_IN_ADDRESS_BOOK,
Span::current(),
addrs,
);
assert_eq!(
address_book
.reconnection_peers(Instant::now(), Utc::now())
.next(),
Some(meta_addr2),
);
let addrs = vec![meta_addr2, meta_addr1];
let address_book = AddressBook::new_with_addrs(
"0.0.0.0:0".parse().unwrap(),
&Mainnet,
DEFAULT_MAX_CONNS_PER_IP,
MAX_ADDRS_IN_ADDRESS_BOOK,
Span::current(),
addrs,
);
assert_eq!(
address_book
.reconnection_peers(Instant::now(), Utc::now())
.next(),
Some(meta_addr2),
);
}
#[test]
fn reconnection_peers_skips_recently_updated_ip() {
test_reconnection_peers_skips_recently_updated_ip(true, |addr| {
MetaAddr::new_responded(addr, None)
});
test_reconnection_peers_skips_recently_updated_ip(false, MetaAddr::new_reconnect);
test_reconnection_peers_skips_recently_updated_ip(false, |addr| {
MetaAddr::new_errored(addr, PeerServices::NODE_NETWORK)
});
}
fn test_reconnection_peers_skips_recently_updated_ip<
M: Fn(crate::PeerSocketAddr) -> crate::meta_addr::MetaAddrChange,
>(
should_skip_ip: bool,
make_meta_addr_change: M,
) {
let addr1 = "127.0.0.1:1".parse().unwrap();
let addr2 = "127.0.0.1:2".parse().unwrap();
let meta_addr1 = make_meta_addr_change(addr1).into_new_meta_addr(
Instant::now(),
Utc::now().try_into().expect("will succeed until 2038"),
);
let meta_addr2 = MetaAddr::new_gossiped_meta_addr(
addr2,
PeerServices::NODE_NETWORK,
DateTime32::MIN.saturating_add(Duration32::from_seconds(1)),
);
let addrs = vec![meta_addr1, meta_addr2];
let address_book = AddressBook::new_with_addrs(
"0.0.0.0:0".parse().unwrap(),
&Mainnet,
DEFAULT_MAX_CONNS_PER_IP,
MAX_ADDRS_IN_ADDRESS_BOOK,
Span::current(),
addrs,
);
let next_reconnection_peer = address_book
.reconnection_peers(Instant::now(), Utc::now())
.next();
if should_skip_ip {
assert_eq!(next_reconnection_peer, None,);
} else {
assert_ne!(next_reconnection_peer, None,);
}
}