use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
time::{Duration, Instant},
};
use chrono::Utc;
use zebra_chain::{
parameters::Network::*,
serialization::{DateTime32, Duration32},
};
use crate::{
constants::{CONCURRENT_ADDRESS_CHANGE_PERIOD, MAX_PEER_ACTIVE_FOR_GOSSIP},
meta_addr::MetaAddrChange,
protocol::{external::canonical_peer_addr, types::PeerServices},
PeerSocketAddr,
};
use super::{super::MetaAddr, check};
const TEST_TIME_ERROR_MARGIN: Duration32 = Duration32::from_seconds(1);
#[test]
fn sanitize_extremes() {
let _init_guard = zebra_test::init();
let min_time_entry = MetaAddr {
addr: "127.0.0.1:8233".parse().unwrap(),
services: Default::default(),
untrusted_last_seen: Some(u32::MIN.into()),
last_response: Some(u32::MIN.into()),
rtt: Some(Duration::ZERO),
ping_sent_at: None,
last_attempt: None,
last_failure: None,
last_connection_state: Default::default(),
misbehavior_score: Default::default(),
is_inbound: false,
};
let max_time_entry = MetaAddr {
addr: "127.0.0.1:8233".parse().unwrap(),
services: Default::default(),
untrusted_last_seen: Some(u32::MAX.into()),
last_response: Some(u32::MAX.into()),
rtt: Some(Duration::ZERO),
ping_sent_at: None,
last_attempt: None,
last_failure: None,
last_connection_state: Default::default(),
misbehavior_score: Default::default(),
is_inbound: false,
};
if let Some(min_sanitized) = min_time_entry.sanitize(&Mainnet) {
check::sanitize_avoids_leaks(&min_time_entry, &min_sanitized);
}
if let Some(max_sanitized) = max_time_entry.sanitize(&Mainnet) {
check::sanitize_avoids_leaks(&max_time_entry, &max_sanitized);
}
}
#[test]
fn new_local_listener_is_gossipable() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let local_now: DateTime32 = chrono_now.try_into().expect("will succeed until 2038");
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer =
MetaAddr::new_local_listener_change(address).into_new_meta_addr(instant_now, local_now);
assert!(peer.is_active_for_gossip(chrono_now));
}
#[test]
fn gossiped_peer_reportedly_to_be_seen_recently_is_gossipable() {
let _init_guard = zebra_test::init();
let chrono_now = Utc::now();
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let offset = MAX_PEER_ACTIVE_FOR_GOSSIP
.checked_sub(TEST_TIME_ERROR_MARGIN)
.expect("Test margin is too large");
let last_seen = DateTime32::now()
.checked_sub(offset)
.expect("Offset is too large");
let peer = MetaAddr::new_gossiped_meta_addr(address, PeerServices::NODE_NETWORK, last_seen);
assert!(peer.is_active_for_gossip(chrono_now));
}
#[test]
fn gossiped_peer_reportedly_seen_in_the_future_is_gossipable() {
let _init_guard = zebra_test::init();
let chrono_now = Utc::now();
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let last_seen = DateTime32::now()
.checked_add(MAX_PEER_ACTIVE_FOR_GOSSIP)
.expect("Reachable peer duration is too large");
let peer = MetaAddr::new_gossiped_meta_addr(address, PeerServices::NODE_NETWORK, last_seen);
assert!(peer.is_active_for_gossip(chrono_now));
}
#[test]
fn gossiped_peer_reportedly_seen_long_ago_is_not_gossipable() {
let _init_guard = zebra_test::init();
let chrono_now = Utc::now();
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let offset = MAX_PEER_ACTIVE_FOR_GOSSIP
.checked_add(TEST_TIME_ERROR_MARGIN)
.expect("Test margin is too large");
let last_seen = DateTime32::now()
.checked_sub(offset)
.expect("Offset is too large");
let peer = MetaAddr::new_gossiped_meta_addr(address, PeerServices::NODE_NETWORK, last_seen);
assert!(!peer.is_active_for_gossip(chrono_now));
}
#[test]
fn recently_responded_peer_is_gossipable() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let local_now: DateTime32 = chrono_now.try_into().expect("will succeed until 2038");
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_initial_peer(address).into_new_meta_addr(instant_now, local_now);
let peer = MetaAddr::new_responded(address, None)
.apply_to_meta_addr(peer_seed, instant_now, chrono_now)
.expect("Failed to create MetaAddr for responded peer");
assert!(peer.is_active_for_gossip(chrono_now));
}
#[test]
fn not_so_recently_responded_peer_is_still_gossipable() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let local_now: DateTime32 = chrono_now.try_into().expect("will succeed until 2038");
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_initial_peer(address).into_new_meta_addr(instant_now, local_now);
let mut peer = MetaAddr::new_responded(address, None)
.apply_to_meta_addr(peer_seed, instant_now, chrono_now)
.expect("Failed to create MetaAddr for responded peer");
let offset = MAX_PEER_ACTIVE_FOR_GOSSIP
.checked_sub(TEST_TIME_ERROR_MARGIN)
.expect("Test margin is too large");
let last_response = DateTime32::now()
.checked_sub(offset)
.expect("Offset is too large");
peer.set_last_response(last_response);
assert!(peer.is_active_for_gossip(chrono_now));
}
#[test]
fn responded_long_ago_peer_is_not_gossipable() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let local_now: DateTime32 = chrono_now.try_into().expect("will succeed until 2038");
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_initial_peer(address).into_new_meta_addr(instant_now, local_now);
let mut peer = MetaAddr::new_responded(address, None)
.apply_to_meta_addr(peer_seed, instant_now, chrono_now)
.expect("Failed to create MetaAddr for responded peer");
let offset = MAX_PEER_ACTIVE_FOR_GOSSIP
.checked_add(TEST_TIME_ERROR_MARGIN)
.expect("Test margin is too large");
let last_response = DateTime32::now()
.checked_sub(offset)
.expect("Offset is too large");
peer.set_last_response(last_response);
assert!(!peer.is_active_for_gossip(chrono_now));
}
#[test]
fn long_delayed_change_is_not_applied() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let local_now: DateTime32 = chrono_now.try_into().expect("will succeed until 2038");
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_initial_peer(address).into_new_meta_addr(instant_now, local_now);
let peer = MetaAddr::new_responded(address, None)
.apply_to_meta_addr(peer_seed, instant_now, chrono_now)
.expect("Failed to create MetaAddr for responded peer");
let instant_early = instant_now - (CONCURRENT_ADDRESS_CHANGE_PERIOD * 3);
let chrono_early = chrono_now
- chrono::Duration::from_std(CONCURRENT_ADDRESS_CHANGE_PERIOD * 3)
.expect("constant is valid");
let change = MetaAddr::new_errored(address, PeerServices::NODE_NETWORK);
let outcome = change.apply_to_meta_addr(peer, instant_early, chrono_early);
assert_eq!(
outcome, None,
"\n\
unexpected application of a much earlier change to a peer:\n\
change: {change:?}\n\
times: {instant_early:?} {chrono_early}\n\
peer: {peer:?}"
);
}
#[test]
fn later_revert_change_is_applied() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let local_now: DateTime32 = chrono_now.try_into().expect("will succeed until 2038");
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_initial_peer(address).into_new_meta_addr(instant_now, local_now);
let peer = MetaAddr::new_responded(address, None)
.apply_to_meta_addr(peer_seed, instant_now, chrono_now)
.expect("Failed to create MetaAddr for responded peer");
let instant_late = instant_now + (CONCURRENT_ADDRESS_CHANGE_PERIOD * 3);
let chrono_late = chrono_now
+ chrono::Duration::from_std(CONCURRENT_ADDRESS_CHANGE_PERIOD * 3)
.expect("constant is valid");
let change = MetaAddr::new_reconnect(address);
let outcome = change.apply_to_meta_addr(peer, instant_late, chrono_late);
assert!(
outcome.is_some(),
"\n\
unexpected skipped much later change to a peer:\n\
change: {change:?}\n\
times: {instant_late:?} {chrono_late}\n\
peer: {peer:?}"
);
}
#[test]
fn concurrent_state_revert_change_is_not_applied() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let local_now: DateTime32 = chrono_now.try_into().expect("will succeed until 2038");
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_initial_peer(address).into_new_meta_addr(instant_now, local_now);
let peer = MetaAddr::new_responded(address, None)
.apply_to_meta_addr(peer_seed, instant_now, chrono_now)
.expect("Failed to create MetaAddr for responded peer");
let instant_early = instant_now - (CONCURRENT_ADDRESS_CHANGE_PERIOD / 2);
let chrono_early = chrono_now
- chrono::Duration::from_std(CONCURRENT_ADDRESS_CHANGE_PERIOD / 2)
.expect("constant is valid");
let change = MetaAddr::new_reconnect(address);
let outcome = change.apply_to_meta_addr(peer, instant_early, chrono_early);
assert_eq!(
outcome, None,
"\n\
unexpected application of an early concurrent change to a peer:\n\
change: {change:?}\n\
times: {instant_early:?} {chrono_early}\n\
peer: {peer:?}"
);
let instant_late = instant_now + (CONCURRENT_ADDRESS_CHANGE_PERIOD / 2);
let chrono_late = chrono_now
+ chrono::Duration::from_std(CONCURRENT_ADDRESS_CHANGE_PERIOD / 2)
.expect("constant is valid");
let change = MetaAddr::new_reconnect(address);
let outcome = change.apply_to_meta_addr(peer, instant_late, chrono_late);
assert_eq!(
outcome, None,
"\n\
unexpected application of a late concurrent change to a peer:\n\
change: {change:?}\n\
times: {instant_late:?} {chrono_late}\n\
peer: {peer:?}"
);
}
#[test]
fn concurrent_state_progress_change_is_applied() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let local_now: DateTime32 = chrono_now.try_into().expect("will succeed until 2038");
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_initial_peer(address).into_new_meta_addr(instant_now, local_now);
let peer = MetaAddr::new_responded(address, None)
.apply_to_meta_addr(peer_seed, instant_now, chrono_now)
.expect("Failed to create MetaAddr for responded peer");
let instant_early = instant_now - (CONCURRENT_ADDRESS_CHANGE_PERIOD / 2);
let chrono_early = chrono_now
- chrono::Duration::from_std(CONCURRENT_ADDRESS_CHANGE_PERIOD / 2)
.expect("constant is valid");
let change = MetaAddr::new_errored(address, None);
let outcome = change.apply_to_meta_addr(peer, instant_early, chrono_early);
assert!(
outcome.is_some(),
"\n\
unexpected skipped early concurrent change to a peer:\n\
change: {change:?}\n\
times: {instant_early:?} {chrono_early}\n\
peer: {peer:?}"
);
let instant_late = instant_now + (CONCURRENT_ADDRESS_CHANGE_PERIOD / 2);
let chrono_late = chrono_now
+ chrono::Duration::from_std(CONCURRENT_ADDRESS_CHANGE_PERIOD / 2)
.expect("constant is valid");
let change = MetaAddr::new_errored(address, None);
let outcome = change.apply_to_meta_addr(peer, instant_late, chrono_late);
assert!(
outcome.is_some(),
"\n\
unexpected skipped late concurrent change to a peer:\n\
change: {change:?}\n\
times: {instant_late:?} {chrono_late}\n\
peer: {peer:?}"
);
}
#[test]
fn rtt_is_stored_correctly_in_meta_addr() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let local_now: DateTime32 = chrono_now.try_into().expect("will succeed until 2038");
let address = PeerSocketAddr::from(([192, 168, 180, 9], 10_000));
let peer_seed = MetaAddr::new_initial_peer(address).into_new_meta_addr(instant_now, local_now);
let rtt = Duration::from_millis(128);
let peer = MetaAddr::new_responded(address, Some(rtt))
.apply_to_meta_addr(peer_seed, instant_now, chrono_now)
.expect("Failed to create MetaAddr for responded peer");
assert_eq!(peer.rtt, Some(rtt));
}
#[test]
#[should_panic(expected = "unexpected addr mismatch")]
fn ipv4_mapped_misbehavior_panics_without_fix() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let raw_addr = PeerSocketAddr::from(SocketAddr::new(
IpAddr::V6(Ipv4Addr::LOCALHOST.to_ipv6_mapped()),
8233,
));
let canonical_addr = canonical_peer_addr(*raw_addr);
assert_ne!(
raw_addr, canonical_addr,
"test setup: need an IPv4-mapped addr"
);
let previous = MetaAddr::new_connected(raw_addr, &PeerServices::NODE_NETWORK, true)
.into_new_meta_addr(
instant_now,
chrono_now.try_into().expect("will succeed until 2038"),
);
assert_eq!(
previous.addr(),
canonical_addr,
"handshake must canonicalize"
);
let misbehavior_change = MetaAddrChange::UpdateMisbehavior {
addr: raw_addr, score_increment: 100,
};
let _ = misbehavior_change.apply_to_meta_addr(previous, instant_now, chrono_now);
}
#[test]
fn new_misbehavior_canonicalizes_ipv4_mapped_addr() {
let _init_guard = zebra_test::init();
let instant_now = Instant::now();
let chrono_now = Utc::now();
let raw_addr = PeerSocketAddr::from(SocketAddr::new(
IpAddr::V6(Ipv4Addr::LOCALHOST.to_ipv6_mapped()),
8233,
));
let canonical_addr = canonical_peer_addr(*raw_addr);
assert_ne!(raw_addr, canonical_addr);
let previous = MetaAddr::new_connected(raw_addr, &PeerServices::NODE_NETWORK, true)
.into_new_meta_addr(
instant_now,
chrono_now.try_into().expect("will succeed until 2038"),
);
assert_eq!(previous.addr(), canonical_addr);
let change = MetaAddr::new_misbehavior(raw_addr, 100);
assert_eq!(change.addr(), canonical_addr);
let updated = change
.apply_to_meta_addr(previous, instant_now, chrono_now)
.expect("canonical misbehavior update should apply to existing peer");
assert_eq!(updated.addr(), canonical_addr);
assert_eq!(updated.misbehavior(), 100);
}