use bytes::BytesMut;
use proptest::{collection::vec, prelude::*};
use tokio_util::codec::{Decoder, Encoder};
use zebra_chain::{
parameters::Network::*,
serialization::{
SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
MAX_PROTOCOL_MESSAGE_LEN,
},
};
use crate::{
meta_addr::{tests::check, MetaAddr},
protocol::external::{
addr::{AddrV1, AddrV2},
Codec, InventoryHash, Message,
},
};
const MAX_INVENTORY_HASH_BYTES: usize = 70;
proptest! {
#[test]
fn inventory_hash_roundtrip(inventory_hash in any::<InventoryHash>()) {
let mut bytes = Vec::new();
let serialization_result = inventory_hash.zcash_serialize(&mut bytes);
prop_assert!(serialization_result.is_ok());
prop_assert!(bytes.len() < MAX_INVENTORY_HASH_BYTES);
let deserialized: Result<InventoryHash, _> = bytes.zcash_deserialize_into();
prop_assert!(deserialized.is_ok());
prop_assert_eq!(deserialized.unwrap(), inventory_hash);
}
#[test]
fn inventory_hash_from_random_bytes(input in vec(any::<u8>(), 0..MAX_INVENTORY_HASH_BYTES)) {
let deserialized: Result<InventoryHash, _> = input.zcash_deserialize_into();
if input.len() >= 4 && (input[1..4] != [0u8; 3] || input[0] > 5 || input[0] == 4) {
prop_assert!(matches!(
deserialized,
Err(SerializationError::Parse("invalid inventory code"))
));
} else if input.len() < 36 {
prop_assert!(deserialized.is_err());
prop_assert_eq!(
deserialized.unwrap_err().to_string(),
"io error: failed to fill whole buffer"
);
} else if input[0] == 5 && input.len() < 68 {
prop_assert!(deserialized.is_err());
prop_assert_eq!(
deserialized.unwrap_err().to_string(),
"io error: failed to fill whole buffer"
);
} else {
prop_assert!(deserialized.is_ok());
let mut bytes = Vec::new();
let serialization_result = deserialized.unwrap().zcash_serialize(&mut bytes);
prop_assert!(serialization_result.is_ok());
prop_assert!(bytes.len() <= input.len());
prop_assert_eq!(&bytes, &input[..bytes.len()]);
}
}
#[test]
fn inv_and_getdata_message_roundtrip(
message in prop_oneof!(Message::inv_strategy(), Message::get_data_strategy()),
) {
let mut codec = Codec::builder().finish();
let mut bytes = BytesMut::with_capacity(MAX_PROTOCOL_MESSAGE_LEN);
let encoding_result = codec.encode(message.clone(), &mut bytes);
prop_assert!(encoding_result.is_ok());
let decoded: Result<Option<Message>, _> = codec.decode(&mut bytes);
prop_assert!(decoded.is_ok());
prop_assert_eq!(decoded.unwrap(), Some(message));
}
#[test]
fn addr_v1_sanitized_roundtrip(addr in any::<MetaAddr>()) {
let _init_guard = zebra_test::init();
let sanitized_addr = addr.sanitize(&Mainnet);
prop_assume!(sanitized_addr.is_some());
let sanitized_addr = sanitized_addr.unwrap();
check::sanitize_avoids_leaks(&addr, &sanitized_addr);
let addr_bytes = AddrV1::from(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();
let deserialized_addr = AddrV1::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: MetaAddr = deserialized_addr.unwrap().into();
prop_assert_eq!(
sanitized_addr,
deserialized_addr,
"unexpected round-trip mismatch with bytes: {:?}",
hex::encode(addr_bytes),
);
check::sanitize_avoids_leaks(&addr, &deserialized_addr);
let addr_bytes2 = AddrV1::from(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 double-serialization round-trip mismatch with original addr: {:?}, bytes: {:?}, deserialized addr: {:?}, bytes: {:?}",
sanitized_addr,
hex::encode(&addr_bytes),
deserialized_addr,
hex::encode(&addr_bytes2),
);
}
#[test]
fn addr_v2_sanitized_roundtrip(addr in any::<MetaAddr>()) {
let _init_guard = zebra_test::init();
let sanitized_addr = addr.sanitize(&Mainnet);
prop_assume!(sanitized_addr.is_some());
let sanitized_addr = sanitized_addr.unwrap();
check::sanitize_avoids_leaks(&addr, &sanitized_addr);
let addr_bytes = AddrV2::from(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();
let deserialized_addr = AddrV2::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: AddrV2 = deserialized_addr.unwrap();
let deserialized_addr: MetaAddr = deserialized_addr.try_into().expect("arbitrary MetaAddrs are IPv4 or IPv6");
prop_assert_eq!(
sanitized_addr,
deserialized_addr,
"unexpected round-trip mismatch with bytes: {:?}",
hex::encode(addr_bytes),
);
check::sanitize_avoids_leaks(&addr, &deserialized_addr);
let addr_bytes2 = AddrV2::from(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 double-serialization round-trip mismatch with original addr: {:?}, bytes: {:?}, deserialized addr: {:?}, bytes: {:?}",
sanitized_addr,
hex::encode(&addr_bytes),
deserialized_addr,
hex::encode(&addr_bytes2),
);
}
}