diff --git a/zebra-network/src/meta_addr.rs b/zebra-network/src/meta_addr.rs index 74439a0302e..dfb5167c315 100644 --- a/zebra-network/src/meta_addr.rs +++ b/zebra-network/src/meta_addr.rs @@ -574,7 +574,8 @@ impl MetaAddrChange { .. } => Some(*untrusted_last_seen), NewAlternate { .. } => None, - NewLocal { .. } => None, + // We know that our local listener is available + NewLocal { .. } => Some(DateTime32::now()), UpdateAttempt { .. } => None, UpdateResponded { .. } => None, UpdateFailed { .. } => None, diff --git a/zebra-network/src/meta_addr/arbitrary.rs b/zebra-network/src/meta_addr/arbitrary.rs index 1604e4332b4..82a10de863e 100644 --- a/zebra-network/src/meta_addr/arbitrary.rs +++ b/zebra-network/src/meta_addr/arbitrary.rs @@ -6,12 +6,18 @@ use super::{MetaAddr, MetaAddrChange, PeerServices}; use zebra_chain::serialization::{arbitrary::canonical_socket_addr_strategy, DateTime32}; -/// The largest number of random changes we want to apply to a MetaAddr +/// The largest number of random changes we want to apply to a [`MetaAddr`]. /// -/// This should be at least twice the number of [`PeerAddrState`]s, so -/// the tests can cover multiple transitions through every state. +/// This should be at least twice the number of [`PeerAddrState`]s, so the tests +/// can cover multiple transitions through every state. pub const MAX_ADDR_CHANGE: usize = 15; +/// The largest number of random addresses we want to add to an [`AddressBook`]. +/// +/// This should be at least the number of [`PeerAddrState`]s, so the tests can +/// cover interactions between addresses in different states. +pub const MAX_META_ADDR: usize = 8; + impl MetaAddr { /// Create a strategy that generates [`MetaAddr`]s in the /// [`PeerAddrState::NeverAttemptedGossiped`] state. diff --git a/zebra-network/src/meta_addr/tests/prop.rs b/zebra-network/src/meta_addr/tests/prop.rs index b36eec47260..b07d9625869 100644 --- a/zebra-network/src/meta_addr/tests/prop.rs +++ b/zebra-network/src/meta_addr/tests/prop.rs @@ -14,12 +14,16 @@ use tokio::{runtime::Runtime, time::Instant}; use tower::service_fn; use tracing::Span; -use zebra_chain::serialization::{ZcashDeserialize, ZcashSerialize}; +use zebra_chain::serialization::{canonical_socket_addr, ZcashDeserialize, ZcashSerialize}; use super::check; use crate::{ constants::LIVE_PEER_DURATION, - meta_addr::{arbitrary::MAX_ADDR_CHANGE, MetaAddr, MetaAddrChange, PeerAddrState::*}, + meta_addr::{ + arbitrary::{MAX_ADDR_CHANGE, MAX_META_ADDR}, + MetaAddr, MetaAddrChange, + PeerAddrState::*, + }, peer_set::candidate_set::CandidateSet, protocol::types::PeerServices, AddressBook, Config, @@ -256,6 +260,40 @@ proptest! { } } } + + /// Make sure that a sanitized [`AddressBook`] contains the local listener + /// [`MetaAddr`], regardless of the previous contents of the address book. + /// + /// If Zebra gossips its own listener address to peers, and gets it back, + /// its address book will contain its local listener address. This address + /// will likely be in [`PeerAddrState::Failed`], due to failed + /// self-connection attempts. + #[test] + fn sanitized_address_book_contains_local_listener( + local_listener in any::(), + address_book_addrs in vec(any::(), 0..MAX_META_ADDR), + ) { + zebra_test::init(); + + let config = Config { listen_addr: local_listener, ..Config::default() }; + let address_book = AddressBook::new_with_addrs(&config, Span::none(), address_book_addrs); + let sanitized_addrs = address_book.sanitized(); + + let expected_local_listener = address_book.get_local_listener(); + let canonical_local_listener = canonical_socket_addr(local_listener); + let book_sanitized_local_listener = sanitized_addrs.iter().find(|meta_addr| meta_addr.addr == canonical_local_listener ); + + // invalid addresses should be removed by sanitization, + // regardless of where they have come from + prop_assert_eq!( + book_sanitized_local_listener.cloned(), + expected_local_listener.sanitize(), + "address book: {:?}, sanitized_addrs: {:?}, canonical_local_listener: {:?}", + address_book, + sanitized_addrs, + canonical_local_listener, + ); + } } proptest! {