Proptest `MetaAddr` sanitization and serialization together
This commit is contained in:
parent
9f8b4f836e
commit
5cdcc5255f
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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::<MetaAddr>()
|
||||
) {
|
||||
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),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue