diff --git a/zebra-network/src/meta_addr/tests/check.rs b/zebra-network/src/meta_addr/tests/check.rs index 061854d5f..dbe5d3fd4 100644 --- a/zebra-network/src/meta_addr/tests/check.rs +++ b/zebra-network/src/meta_addr/tests/check.rs @@ -2,13 +2,13 @@ use super::super::MetaAddr; -use crate::constants::TIMESTAMP_TRUNCATION_SECONDS; - -/// Make sure that the sanitize function reduces time and state metadata -/// leaks. -pub(crate) fn sanitize_avoids_leaks(entry: &MetaAddr) { - let sanitized = entry.sanitize(); +use crate::{constants::TIMESTAMP_TRUNCATION_SECONDS, types::PeerServices}; +/// Check that `sanitized_addr` has less time and state metadata than +/// `original_addr`. +/// +/// Also check that the time hasn't changed too much. +pub(crate) fn sanitize_avoids_leaks(original: &MetaAddr, sanitized: &MetaAddr) { // We want the sanitized timestamp to: // - be a multiple of the truncation interval, // - have a zero nanoseconds component, and @@ -20,11 +20,11 @@ pub(crate) fn sanitize_avoids_leaks(entry: &MetaAddr) { assert_eq!(sanitized.get_last_seen().timestamp_subsec_nanos(), 0); // handle underflow and overflow by skipping the check // the other check will ensure correctness - let lowest_time = entry + let lowest_time = original .get_last_seen() .timestamp() .checked_sub(TIMESTAMP_TRUNCATION_SECONDS); - let highest_time = entry + let highest_time = original .get_last_seen() .timestamp() .checked_add(TIMESTAMP_TRUNCATION_SECONDS); @@ -37,9 +37,9 @@ pub(crate) fn sanitize_avoids_leaks(entry: &MetaAddr) { // Sanitize to the the default state, even though it's not serialized assert_eq!(sanitized.last_connection_state, Default::default()); + // Sanitize to known flags + assert_eq!(sanitized.services, original.services & PeerServices::all()); + // We want the other fields to be unmodified - assert_eq!(sanitized.addr, entry.addr); - // Services are sanitized during parsing, so we don't need to make - // any changes in sanitize() - assert_eq!(sanitized.services, entry.services); + assert_eq!(sanitized.addr, original.addr); } diff --git a/zebra-network/src/meta_addr/tests/prop.rs b/zebra-network/src/meta_addr/tests/prop.rs index f72d3b076..7f4094e17 100644 --- a/zebra-network/src/meta_addr/tests/prop.rs +++ b/zebra-network/src/meta_addr/tests/prop.rs @@ -1,6 +1,7 @@ //! Randomised property tests for MetaAddr. -use super::{super::MetaAddr, check}; +use super::check; +use crate::{meta_addr::MetaAddr, types::PeerServices}; use proptest::prelude::*; @@ -10,10 +11,10 @@ proptest! { /// Make sure that the sanitize function reduces time and state metadata /// leaks. #[test] - fn sanitized_fields(entry in MetaAddr::arbitrary()) { + fn sanitize_avoids_leaks(addr in MetaAddr::arbitrary()) { zebra_test::init(); - check::sanitize_avoids_leaks(&entry); + check::sanitize_avoids_leaks(&addr, &addr.sanitize()); } /// Test round-trip serialization for gossiped MetaAddrs @@ -79,4 +80,73 @@ proptest! { ); } + + /// Test round-trip serialization for all MetaAddr variants after sanitization + #[test] + fn sanitized_roundtrip( + addr in any::() + ) { + zebra_test::init(); + + let sanitized_addr = addr.sanitize(); + // Make sure sanitization avoids leaks on this address, to avoid spurious errors + check::sanitize_avoids_leaks(&addr, &sanitized_addr); + + // Check that sanitization doesn't make Zebra's serialization fail + let addr_bytes = sanitized_addr.zcash_serialize_to_vec(); + prop_assert!( + addr_bytes.is_ok(), + "unexpected serialization error: {:?}, addr: {:?}", + addr_bytes, + sanitized_addr + ); + let addr_bytes = addr_bytes.unwrap(); + + // Assume other implementations deserialize like Zebra + let deserialized_addr = MetaAddr::zcash_deserialize(addr_bytes.as_slice()); + prop_assert!( + deserialized_addr.is_ok(), + "unexpected deserialization error: {:?}, addr: {:?}, bytes: {:?}", + deserialized_addr, + sanitized_addr, + hex::encode(addr_bytes), + ); + let deserialized_addr = deserialized_addr.unwrap(); + + // Check that the addrs are equal + prop_assert_eq!( + sanitized_addr, + deserialized_addr, + "unexpected round-trip mismatch with bytes: {:?}", + hex::encode(addr_bytes), + ); + + // Check that serialization hasn't de-sanitized anything + check::sanitize_avoids_leaks(&addr, &deserialized_addr); + + // Now check that the re-serialized bytes are equal + // (`impl PartialEq for MetaAddr` might not match serialization equality) + let addr_bytes2 = deserialized_addr.zcash_serialize_to_vec(); + prop_assert!( + addr_bytes2.is_ok(), + "unexpected serialization error after round-trip: {:?}, original addr: {:?}, bytes: {:?}, deserialized addr: {:?}", + addr_bytes2, + sanitized_addr, + hex::encode(addr_bytes), + deserialized_addr, + ); + let addr_bytes2 = addr_bytes2.unwrap(); + + prop_assert_eq!( + &addr_bytes, + &addr_bytes2, + "unexpected round-trip bytes mismatch: original addr: {:?}, bytes: {:?}, deserialized addr: {:?}, bytes: {:?}", + sanitized_addr, + hex::encode(&addr_bytes), + deserialized_addr, + hex::encode(&addr_bytes2), + ); + + } + } diff --git a/zebra-network/src/meta_addr/tests/vectors.rs b/zebra-network/src/meta_addr/tests/vectors.rs index 538ada7b8..828dd8ff6 100644 --- a/zebra-network/src/meta_addr/tests/vectors.rs +++ b/zebra-network/src/meta_addr/tests/vectors.rs @@ -23,6 +23,6 @@ fn sanitize_extremes() { last_connection_state: Default::default(), }; - check::sanitize_avoids_leaks(&min_time_entry); - check::sanitize_avoids_leaks(&max_time_entry); + check::sanitize_avoids_leaks(&min_time_entry, &min_time_entry.sanitize()); + check::sanitize_avoids_leaks(&max_time_entry, &max_time_entry.sanitize()); }