Simplify preallocate tests (#3032)

* Simplify preallocation tests using a test function

* Use prop_assert in proptests
This commit is contained in:
teor 2021-11-11 07:53:21 +10:00 committed by GitHub
parent 02526c3339
commit c0c00b3f0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 217 additions and 208 deletions

View File

@ -9,7 +9,10 @@ use crate::{
header::MIN_COUNTED_HEADER_LEN, CountedHeader, Hash, Header, BLOCK_HASH_SIZE,
MAX_PROTOCOL_MESSAGE_LEN,
},
serialization::{CompactSizeMessage, TrustedPreallocate, ZcashSerialize},
serialization::{
arbitrary::max_allocation_is_big_enough, CompactSizeMessage, TrustedPreallocate,
ZcashSerialize,
},
};
proptest! {
@ -25,28 +28,22 @@ proptest! {
/// 2. The largest allowed vector is small enough to fit in a legal Zcash Wire Protocol message
#[test]
fn block_hash_max_allocation(hash in Hash::arbitrary_with(())) {
let max_allocation: usize = Hash::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(Hash::max_allocation()+1) {
smallest_disallowed_vec.push(hash);
}
let (
smallest_disallowed_vec_len,
smallest_disallowed_serialized_len,
largest_allowed_vec_len,
largest_allowed_serialized_len,
) = max_allocation_is_big_enough(hash);
let smallest_disallowed_serialized = smallest_disallowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
// Check that our smallest_disallowed_vec is only one item larger than the limit
prop_assert!(((smallest_disallowed_vec.len() - 1) as u64) == Hash::max_allocation());
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Hash::max_allocation());
// Check that our smallest_disallowed_vec is too big to send as a protocol message
prop_assert!(smallest_disallowed_serialized.len() > MAX_PROTOCOL_MESSAGE_LEN);
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
smallest_disallowed_vec.pop();
let largest_allowed_vec = smallest_disallowed_vec;
let largest_allowed_serialized = largest_allowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
prop_assert!(smallest_disallowed_serialized_len > MAX_PROTOCOL_MESSAGE_LEN);
// Check that our largest_allowed_vec contains the maximum number of hashes
prop_assert!((largest_allowed_vec.len() as u64) == Hash::max_allocation());
prop_assert!((largest_allowed_vec_len as u64) == Hash::max_allocation());
// Check that our largest_allowed_vec is small enough to send as a protocol message
prop_assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
prop_assert!(largest_allowed_serialized_len <= MAX_PROTOCOL_MESSAGE_LEN);
}
/// Confirm that each counted header takes at least COUNTED_HEADER_LEN bytes when serialized.
@ -72,28 +69,24 @@ proptest! {
fn counted_header_max_allocation(header in any::<Header>()) {
let header = CountedHeader {
header,
transaction_count: 0.try_into().expect("zero fits in MAX_PROTOCOL_MESSAGE_LEN"),
transaction_count: 0.try_into().expect("zero is less than MAX_PROTOCOL_MESSAGE_LEN"),
};
let max_allocation: usize = CountedHeader::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(CountedHeader::max_allocation()+1) {
smallest_disallowed_vec.push(header);
}
let smallest_disallowed_serialized = smallest_disallowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
let (
smallest_disallowed_vec_len,
smallest_disallowed_serialized_len,
largest_allowed_vec_len,
largest_allowed_serialized_len,
) = max_allocation_is_big_enough(header);
// Check that our smallest_disallowed_vec is only one item larger than the limit
prop_assert!(((smallest_disallowed_vec.len() - 1) as u64) == CountedHeader::max_allocation());
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == CountedHeader::max_allocation());
// Check that our smallest_disallowed_vec is too big to send as a protocol message
prop_assert!(smallest_disallowed_serialized.len() > MAX_PROTOCOL_MESSAGE_LEN);
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
smallest_disallowed_vec.pop();
let largest_allowed_vec = smallest_disallowed_vec;
let largest_allowed_serialized = largest_allowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
prop_assert!(smallest_disallowed_serialized_len > MAX_PROTOCOL_MESSAGE_LEN);
// Check that our largest_allowed_vec contains the maximum number of CountedHeaders
prop_assert!((largest_allowed_vec.len() as u64) == CountedHeader::max_allocation());
prop_assert!((largest_allowed_vec_len as u64) == CountedHeader::max_allocation());
// Check that our largest_allowed_vec is small enough to send as a protocol message
prop_assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
prop_assert!(largest_allowed_serialized_len <= MAX_PROTOCOL_MESSAGE_LEN);
}
}

View File

@ -7,7 +7,7 @@ use crate::{
Action, AuthorizedAction,
},
primitives::redpallas::{Signature, SpendAuth},
serialization::{tests::max_allocation_is_big_enough, TrustedPreallocate, ZcashSerialize},
serialization::{arbitrary::max_allocation_is_big_enough, TrustedPreallocate, ZcashSerialize},
};
use proptest::{prelude::*, proptest};

View File

@ -14,7 +14,7 @@ use crate::{
},
PerSpendAnchor, SharedAnchor,
},
serialization::{tests::max_allocation_is_big_enough, TrustedPreallocate, ZcashSerialize},
serialization::{arbitrary::max_allocation_is_big_enough, TrustedPreallocate, ZcashSerialize},
};
use proptest::prelude::*;

View File

@ -5,7 +5,9 @@ use std::convert::TryInto;
use chrono::{TimeZone, Utc, MAX_DATETIME, MIN_DATETIME};
use proptest::{arbitrary::any, prelude::*};
use super::{CompactSizeMessage, DateTime32, MAX_PROTOCOL_MESSAGE_LEN};
use super::{
CompactSizeMessage, DateTime32, TrustedPreallocate, ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN,
};
impl Arbitrary for DateTime32 {
type Parameters = ();
@ -72,3 +74,40 @@ impl Arbitrary for CompactSizeMessage {
type Strategy = BoxedStrategy<Self>;
}
/// Allocate a maximum-sized vector of cloned `item`s, and serialize that array.
///
/// Returns the following calculations on `item`:
/// smallest_disallowed_vec_len
/// smallest_disallowed_serialized_len
/// largest_allowed_vec_len
/// largest_allowed_serialized_len
///
/// For varible-size types, `largest_allowed_serialized_len` might not fit within
/// `MAX_PROTOCOL_MESSAGE_LEN` or `MAX_BLOCK_SIZE`.
pub fn max_allocation_is_big_enough<T>(item: T) -> (usize, usize, usize, usize)
where
T: TrustedPreallocate + ZcashSerialize + Clone,
{
let max_allocation: usize = T::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = vec![item; max_allocation + 1];
let smallest_disallowed_serialized = smallest_disallowed_vec
.zcash_serialize_to_vec()
.expect("Serialization to vec must succeed");
let smallest_disallowed_vec_len = smallest_disallowed_vec.len();
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
smallest_disallowed_vec.pop();
let largest_allowed_vec = smallest_disallowed_vec;
let largest_allowed_serialized = largest_allowed_vec
.zcash_serialize_to_vec()
.expect("Serialization to vec must succeed");
(
smallest_disallowed_vec_len,
smallest_disallowed_serialized.len(),
largest_allowed_vec.len(),
largest_allowed_serialized.len(),
)
}

View File

@ -1,4 +1,2 @@
mod preallocate;
mod prop;
pub use preallocate::max_allocation_is_big_enough;

View File

@ -5,52 +5,31 @@ use proptest::{collection::size_range, prelude::*};
use std::{convert::TryInto, matches};
use crate::serialization::{
zcash_deserialize::MAX_U8_ALLOCATION, SerializationError, TrustedPreallocate, ZcashDeserialize,
ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN,
arbitrary::max_allocation_is_big_enough, zcash_deserialize::MAX_U8_ALLOCATION,
SerializationError, TrustedPreallocate, ZcashDeserialize, ZcashSerialize,
MAX_PROTOCOL_MESSAGE_LEN,
};
// Allow direct serialization of Vec<u8> for these tests. We don't usually
// allow this because some types have specific rules for about serialization
// of their inner Vec<u8>. This method could be easily misused if it applied
// more generally.
//
// Due to Rust's trait rules, these trait impls apply to all zebra-chain tests,
// not just the tests in this module. But other crates' tests can't access them.
impl ZcashSerialize for u8 {
fn zcash_serialize<W: std::io::Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
writer.write_all(&[*self])
}
}
/// Return the following calculations on `item`:
/// smallest_disallowed_vec_len
/// smallest_disallowed_serialized_len
/// largest_allowed_vec_len
/// largest_allowed_serialized_len
pub fn max_allocation_is_big_enough<T>(item: T) -> (usize, usize, usize, usize)
where
T: TrustedPreallocate + ZcashSerialize + Clone,
{
let max_allocation: usize = T::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(max_allocation + 1) {
smallest_disallowed_vec.push(item.clone());
impl TrustedPreallocate for u8 {
fn max_allocation() -> u64 {
// MAX_PROTOCOL_MESSAGE_LEN takes up 5 bytes when encoded as a CompactSize.
(MAX_PROTOCOL_MESSAGE_LEN - 5)
.try_into()
.expect("MAX_PROTOCOL_MESSAGE_LEN fits in u64")
}
let smallest_disallowed_serialized = smallest_disallowed_vec
.zcash_serialize_to_vec()
.expect("Serialization to vec must succeed");
let smallest_disallowed_vec_len = smallest_disallowed_vec.len();
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
smallest_disallowed_vec.pop();
let largest_allowed_vec = smallest_disallowed_vec;
let largest_allowed_serialized = largest_allowed_vec
.zcash_serialize_to_vec()
.expect("Serialization to vec must succeed");
(
smallest_disallowed_vec_len,
smallest_disallowed_serialized.len(),
largest_allowed_vec.len(),
largest_allowed_serialized.len(),
)
}
proptest! {
@ -111,25 +90,20 @@ fn u8_size_is_correct() {
/// 1. The smallest disallowed `Vec<u8>` is too big to include in a Zcash Wire Protocol message
/// 2. The largest allowed `Vec<u8>`is exactly the size of a maximal Zcash Wire Protocol message
fn u8_max_allocation_is_correct() {
let mut shortest_disallowed_vec = vec![0u8; MAX_U8_ALLOCATION + 1];
let shortest_disallowed_serialized = shortest_disallowed_vec
.zcash_serialize_to_vec()
.expect("Serialization to vec must succeed");
let (
smallest_disallowed_vec_len,
smallest_disallowed_serialized_len,
largest_allowed_vec_len,
largest_allowed_serialized_len,
) = max_allocation_is_big_enough(0u8);
// Confirm that shortest_disallowed_vec is only one item larger than the limit
assert_eq!((shortest_disallowed_vec.len() - 1), MAX_U8_ALLOCATION);
assert_eq!((smallest_disallowed_vec_len - 1), MAX_U8_ALLOCATION);
// Confirm that shortest_disallowed_vec is too large to be included in a valid zcash message
assert!(shortest_disallowed_serialized.len() > MAX_PROTOCOL_MESSAGE_LEN);
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
shortest_disallowed_vec.pop();
let longest_allowed_vec = shortest_disallowed_vec;
let longest_allowed_serialized = longest_allowed_vec
.zcash_serialize_to_vec()
.expect("serialization to vec must succed");
assert!(smallest_disallowed_serialized_len > MAX_PROTOCOL_MESSAGE_LEN);
// Check that our largest_allowed_vec contains the maximum number of items
assert_eq!(longest_allowed_vec.len(), MAX_U8_ALLOCATION);
assert_eq!(largest_allowed_vec_len, MAX_U8_ALLOCATION);
// Check that our largest_allowed_vec is the size of a maximal protocol message
assert_eq!(longest_allowed_serialized.len(), MAX_PROTOCOL_MESSAGE_LEN);
assert_eq!(largest_allowed_serialized_len, MAX_PROTOCOL_MESSAGE_LEN);
}

View File

@ -2,6 +2,7 @@ use std::{
convert::{TryFrom, TryInto},
io,
net::Ipv6Addr,
sync::Arc,
};
use super::{AtLeastOne, CompactSizeMessage, SerializationError, MAX_PROTOCOL_MESSAGE_LEN};
@ -171,6 +172,15 @@ pub trait TrustedPreallocate {
fn max_allocation() -> u64;
}
impl<T> TrustedPreallocate for Arc<T>
where
T: TrustedPreallocate,
{
fn max_allocation() -> u64 {
T::max_allocation()
}
}
/// The length of the longest valid `Vec<u8>` that can be received over the network
///
/// It takes 5 bytes to encode a CompactSize representing any number netween 2^16 and (2^32 - 1)

View File

@ -1,16 +1,14 @@
//! Tests for trusted preallocation during deserialization.
use super::super::joinsplit::{JoinSplit, BCTV14_JOINSPLIT_SIZE, GROTH16_JOINSPLIT_SIZE};
use proptest::{prelude::*, proptest};
use crate::{
block::MAX_BLOCK_BYTES,
primitives::{Bctv14Proof, Groth16Proof},
serialization::{TrustedPreallocate, ZcashSerialize},
serialization::{arbitrary::max_allocation_is_big_enough, TrustedPreallocate, ZcashSerialize},
sprout::joinsplit::{JoinSplit, BCTV14_JOINSPLIT_SIZE, GROTH16_JOINSPLIT_SIZE},
};
use proptest::{prelude::*, proptest};
use std::convert::TryInto;
proptest! {
/// Confirm that each JoinSplit<Btcv14Proof> takes exactly BCTV14_JOINSPLIT_SIZE bytes when serialized.
/// This verifies that our calculated [`TrustedPreallocate::max_allocation`] is indeed an upper bound.
@ -37,29 +35,22 @@ proptest! {
/// 2. The largest allowed vector is small enough to fit in a legal Zcash block
#[test]
fn joinsplit_btcv14_max_allocation_is_correct(joinsplit in <JoinSplit<Bctv14Proof>>::arbitrary_with(())) {
let (
smallest_disallowed_vec_len,
smallest_disallowed_serialized_len,
largest_allowed_vec_len,
largest_allowed_serialized_len,
) = max_allocation_is_big_enough(joinsplit);
let max_allocation: usize = <JoinSplit<Bctv14Proof>>::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(<JoinSplit<Bctv14Proof>>::max_allocation()+1) {
smallest_disallowed_vec.push(joinsplit.clone());
}
let smallest_disallowed_serialized = smallest_disallowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
// Check that our smallest_disallowed_vec is only one item larger than the limit
prop_assert!(((smallest_disallowed_vec.len() - 1) as u64) == <JoinSplit<Bctv14Proof>>::max_allocation());
// Check that our smallest_disallowed_vec is too big to be included in a valid block
// Note that a serialized block always includes at least one byte for the number of transactions,
// so any serialized Vec<JoinSplit<Bctv14Proof>> at least MAX_BLOCK_BYTES long is too large to fit in a block.
prop_assert!((smallest_disallowed_serialized.len() as u64) >= MAX_BLOCK_BYTES);
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == <JoinSplit<Bctv14Proof>>::max_allocation());
// Check that our smallest_disallowed_vec is too big to fit in a valid Zcash Block.
prop_assert!(smallest_disallowed_serialized_len as u64 > MAX_BLOCK_BYTES);
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
smallest_disallowed_vec.pop();
let largest_allowed_vec = smallest_disallowed_vec;
let largest_allowed_serialized = largest_allowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
// Check that our largest_allowed_vec contains the maximum number of <JoinSplit<Bctv14Proof>>
prop_assert!((largest_allowed_vec.len() as u64) == <JoinSplit<Bctv14Proof>>::max_allocation());
// Check that our largest_allowed_vec is small enough to fit in a Zcash block.
prop_assert!((largest_allowed_serialized.len() as u64) < MAX_BLOCK_BYTES);
// Check that our largest_allowed_vec contains the maximum number of JoinSplits
prop_assert!((largest_allowed_vec_len as u64) == <JoinSplit<Bctv14Proof>>::max_allocation());
// Check that our largest_allowed_vec is small enough to fit in a Zcash Block.
prop_assert!(largest_allowed_serialized_len as u64 <= MAX_BLOCK_BYTES);
}
/// Verify that...
@ -68,27 +59,21 @@ proptest! {
#[test]
fn joinsplit_groth16_max_allocation_is_correct(joinsplit in <JoinSplit<Groth16Proof>>::arbitrary_with(())) {
let max_allocation: usize = <JoinSplit<Groth16Proof>>::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(<JoinSplit<Groth16Proof>>::max_allocation()+1) {
smallest_disallowed_vec.push(joinsplit.clone());
}
let smallest_disallowed_serialized = smallest_disallowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
let (
smallest_disallowed_vec_len,
smallest_disallowed_serialized_len,
largest_allowed_vec_len,
largest_allowed_serialized_len,
) = max_allocation_is_big_enough(joinsplit);
// Check that our smallest_disallowed_vec is only one item larger than the limit
prop_assert!(((smallest_disallowed_vec.len() - 1) as u64) == <JoinSplit<Groth16Proof>>::max_allocation());
// Check that our smallest_disallowed_vec is too big to be included in a valid block
// Note that a serialized block always includes at least one byte for the number of transactions,
// so any serialized Vec<JoinSplit<Groth16Proof>> at least MAX_BLOCK_BYTES long is too large to fit in a block.
prop_assert!((smallest_disallowed_serialized.len() as u64) >= MAX_BLOCK_BYTES);
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == <JoinSplit<Groth16Proof>>::max_allocation());
// Check that our smallest_disallowed_vec is too big to fit in a valid Zcash Block.
prop_assert!(smallest_disallowed_serialized_len as u64 > MAX_BLOCK_BYTES);
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
smallest_disallowed_vec.pop();
let largest_allowed_vec = smallest_disallowed_vec;
let largest_allowed_serialized = largest_allowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
// Check that our largest_allowed_vec contains the maximum number of <JoinSplit<Groth16Proof>>
prop_assert!((largest_allowed_vec.len() as u64) == <JoinSplit<Groth16Proof>>::max_allocation());
// Check that our largest_allowed_vec is small enough to fit in a Zcash block.
prop_assert!((largest_allowed_serialized.len() as u64) < MAX_BLOCK_BYTES);
// Check that our largest_allowed_vec contains the maximum number of JoinSplits
prop_assert!((largest_allowed_vec_len as u64) == <JoinSplit<Groth16Proof>>::max_allocation());
// Check that our largest_allowed_vec is small enough to fit in a Zcash Block.
prop_assert!(largest_allowed_serialized_len as u64 <= MAX_BLOCK_BYTES);
}
}

View File

@ -717,11 +717,16 @@ where
}
/// A Tx Input must have an Outpoint (32 byte hash + 4 byte index), a 4 byte sequence number,
/// and a signature script, which always takes a min of 1 byte (for a length 0 script)
/// and a signature script, which always takes a min of 1 byte (for a length 0 script).
pub(crate) const MIN_TRANSPARENT_INPUT_SIZE: u64 = 32 + 4 + 4 + 1;
/// A Transparent output has an 8 byte value and script which takes a min of 1 byte
/// A Transparent output has an 8 byte value and script which takes a min of 1 byte.
pub(crate) const MIN_TRANSPARENT_OUTPUT_SIZE: u64 = 8 + 1;
/// All txs must have at least one input, a 4 byte locktime, and at least one output
/// All txs must have at least one input, a 4 byte locktime, and at least one output.
///
/// Shielded transfers are much larger than transparent transfers,
/// so this is the minimum transaction size.
pub(crate) const MIN_TRANSPARENT_TX_SIZE: u64 =
MIN_TRANSPARENT_INPUT_SIZE + 4 + MIN_TRANSPARENT_OUTPUT_SIZE;
@ -729,7 +734,7 @@ pub(crate) const MIN_TRANSPARENT_TX_SIZE: u64 =
///
/// `tx` messages contain a single transaction, and `block` messages are limited to the maximum
/// block size.
impl TrustedPreallocate for Arc<Transaction> {
impl TrustedPreallocate for Transaction {
fn max_allocation() -> u64 {
// A transparent transaction is the smallest transaction variant
MAX_BLOCK_BYTES / MIN_TRANSPARENT_TX_SIZE

View File

@ -1,20 +1,20 @@
//! Tests for trusted preallocation during deserialization.
use super::super::{
serialize::{MIN_TRANSPARENT_INPUT_SIZE, MIN_TRANSPARENT_OUTPUT_SIZE, MIN_TRANSPARENT_TX_SIZE},
transparent::Input,
transparent::Output,
Transaction,
};
use proptest::prelude::*;
use crate::{
block::MAX_BLOCK_BYTES,
serialization::{TrustedPreallocate, ZcashSerialize},
serialization::{arbitrary::max_allocation_is_big_enough, TrustedPreallocate, ZcashSerialize},
transaction::{
serialize::{
MIN_TRANSPARENT_INPUT_SIZE, MIN_TRANSPARENT_OUTPUT_SIZE, MIN_TRANSPARENT_TX_SIZE,
},
transparent::Input,
transparent::Output,
Transaction,
},
};
use proptest::prelude::*;
use std::{convert::TryInto, sync::Arc};
proptest! {
/// Confirm that each spend takes at least MIN_TRANSPARENT_TX_SIZE bytes when serialized.
/// This verifies that our calculated [`TrustedPreallocate::max_allocation`] is indeed an upper bound.
@ -49,55 +49,66 @@ proptest! {
/// Verify the smallest disallowed vector of `Transaction`s is too large to fit in a Zcash block
#[test]
fn tx_max_allocation_is_big_enough(tx in Transaction::arbitrary()) {
let max_allocation: usize = <Arc<Transaction>>::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(<Arc<Transaction>>::max_allocation()+1) {
smallest_disallowed_vec.push(Arc::new(tx.clone()));
}
let serialized = smallest_disallowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
let (
smallest_disallowed_vec_len,
smallest_disallowed_serialized_len,
largest_allowed_vec_len,
_largest_allowed_serialized_len,
) = max_allocation_is_big_enough(tx);
// Check that our smallest_disallowed_vec is only one item larger than the limit
prop_assert!(((smallest_disallowed_vec.len() - 1) as u64) == <Arc<Transaction>>::max_allocation());
// Check that our smallest_disallowed_vec is too big to be included in a valid block
prop_assert!(serialized.len() as u64 > MAX_BLOCK_BYTES);
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Transaction::max_allocation());
// Check that our smallest_disallowed_vec is too big to send in a valid Zcash Block
prop_assert!(smallest_disallowed_serialized_len as u64 > MAX_BLOCK_BYTES);
// Check that our largest_allowed_vec contains the maximum number of Transactions
prop_assert!((largest_allowed_vec_len as u64) == Transaction::max_allocation());
// largest_allowed_serialized_len exceeds the limit for variable-sized types
}
/// Verify the smallest disallowed vector of `Input`s is too large to fit in a Zcash block
#[test]
fn input_max_allocation_is_big_enough(input in Input::arbitrary()) {
let max_allocation: usize = Input::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(Input::max_allocation()+1) {
smallest_disallowed_vec.push(input.clone());
}
let serialized = smallest_disallowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
let (
smallest_disallowed_vec_len,
smallest_disallowed_serialized_len,
largest_allowed_vec_len,
_largest_allowed_serialized_len,
) = max_allocation_is_big_enough(input);
// Check that our smallest_disallowed_vec is only one item larger than the limit
prop_assert!(((smallest_disallowed_vec.len() - 1) as u64) == Input::max_allocation());
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Input::max_allocation());
// Check that our smallest_disallowed_vec is too big to be included in a valid block
// Note that a serialized block always includes at least one byte for the number of transactions,
// so any serialized Vec<Input> at least MAX_BLOCK_BYTES long is too large to fit in a block.
prop_assert!(serialized.len() as u64 >= MAX_BLOCK_BYTES);
prop_assert!(smallest_disallowed_serialized_len as u64 >= MAX_BLOCK_BYTES);
// Check that our largest_allowed_vec contains the maximum number of Inputs
prop_assert!((largest_allowed_vec_len as u64) == Input::max_allocation());
// largest_allowed_serialized_len exceeds the limit for variable-sized types
}
/// Verify the smallest disallowed vector of `Output`s is too large to fit in a Zcash block
#[test]
fn output_max_allocation_is_big_enough(output in Output::arbitrary()) {
let max_allocation: usize = Output::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(Output::max_allocation()+1) {
smallest_disallowed_vec.push(output.clone());
}
let serialized = smallest_disallowed_vec.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
let (
smallest_disallowed_vec_len,
smallest_disallowed_serialized_len,
largest_allowed_vec_len,
_largest_allowed_serialized_len,
) = max_allocation_is_big_enough(output);
// Check that our smallest_disallowed_vec is only one item larger than the limit
prop_assert!(((smallest_disallowed_vec.len() - 1) as u64) == Output::max_allocation());
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Output::max_allocation());
// Check that our smallest_disallowed_vec is too big to be included in a valid block
// Note that a serialized block always includes at least one byte for the number of transactions,
// so any serialized Vec<Output> at least MAX_BLOCK_BYTES long is too large to fit in a block.
prop_assert!(serialized.len() as u64 >= MAX_BLOCK_BYTES);
prop_assert!(smallest_disallowed_serialized_len as u64 >= MAX_BLOCK_BYTES);
// Check that our largest_allowed_vec contains the maximum number of Outputs
prop_assert!((largest_allowed_vec_len as u64) == Output::max_allocation());
// largest_allowed_serialized_len exceeds the limit for variable-sized types
}
}

View File

@ -4,13 +4,17 @@ use std::convert::TryInto;
use proptest::prelude::*;
use zebra_chain::serialization::{TrustedPreallocate, ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN};
use zebra_chain::serialization::{
arbitrary::max_allocation_is_big_enough, TrustedPreallocate, ZcashSerialize,
MAX_PROTOCOL_MESSAGE_LEN,
};
use crate::meta_addr::MetaAddr;
use super::super::{
addr::{AddrV1, ADDR_V1_SIZE},
inv::InventoryHash,
use crate::{
meta_addr::MetaAddr,
protocol::external::{
addr::{AddrV1, ADDR_V1_SIZE},
inv::InventoryHash,
},
};
proptest! {
@ -30,7 +34,7 @@ proptest! {
InventoryHash::Wtx(_) => 32 + 32 + 4,
};
assert_eq!(serialized_inv.len(), expected_size);
prop_assert_eq!(serialized_inv.len(), expected_size);
}
/// Verifies that...
@ -39,17 +43,15 @@ proptest! {
#[test]
fn inv_hash_max_allocation_is_correct(inv in InventoryHash::smallest_types_strategy()) {
let max_allocation: usize = InventoryHash::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(InventoryHash::max_allocation() + 1) {
smallest_disallowed_vec.push(inv);
}
let mut smallest_disallowed_vec = vec![inv; max_allocation + 1];
let smallest_disallowed_serialized = smallest_disallowed_vec
.zcash_serialize_to_vec()
.expect("Serialization to vec must succeed");
// Check that our smallest_disallowed_vec is only one item larger than the limit
assert!(((smallest_disallowed_vec.len() - 1) as u64) == InventoryHash::max_allocation());
prop_assert!(((smallest_disallowed_vec.len() - 1) as u64) == InventoryHash::max_allocation());
// Check that our smallest_disallowed_vec is too big to fit in a Zcash message.
assert!(smallest_disallowed_serialized.len() > MAX_PROTOCOL_MESSAGE_LEN);
prop_assert!(smallest_disallowed_serialized.len() > MAX_PROTOCOL_MESSAGE_LEN);
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
smallest_disallowed_vec.pop();
@ -59,9 +61,9 @@ proptest! {
.expect("Serialization to vec must succeed");
// Check that our largest_allowed_vec contains the maximum number of InventoryHashes
assert!((largest_allowed_vec.len() as u64) == InventoryHash::max_allocation());
prop_assert!((largest_allowed_vec.len() as u64) == InventoryHash::max_allocation());
// Check that our largest_allowed_vec is small enough to fit in a Zcash message.
assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
prop_assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
}
}
@ -81,7 +83,7 @@ proptest! {
let serialized = addr
.zcash_serialize_to_vec()
.expect("Serialization to vec must succeed");
assert!(serialized.len() == ADDR_V1_SIZE)
prop_assert!(serialized.len() == ADDR_V1_SIZE)
}
/// Verifies that...
@ -97,29 +99,21 @@ proptest! {
let addr: AddrV1 = addr.unwrap().into();
let max_allocation: usize = AddrV1::max_allocation().try_into().unwrap();
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
for _ in 0..(AddrV1::max_allocation() + 1) {
smallest_disallowed_vec.push(addr);
}
let smallest_disallowed_serialized = smallest_disallowed_vec
.zcash_serialize_to_vec()
.expect("Serialization to vec must succeed");
// Check that our smallest_disallowed_vec is only one item larger than the limit
assert!(((smallest_disallowed_vec.len() - 1) as u64) == AddrV1::max_allocation());
// Check that our smallest_disallowed_vec is too big to send in a valid Zcash message
assert!(smallest_disallowed_serialized.len() > MAX_PROTOCOL_MESSAGE_LEN);
let (
smallest_disallowed_vec_len,
smallest_disallowed_serialized_len,
largest_allowed_vec_len,
largest_allowed_serialized_len,
) = max_allocation_is_big_enough(addr);
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
smallest_disallowed_vec.pop();
let largest_allowed_vec = smallest_disallowed_vec;
let largest_allowed_serialized = largest_allowed_vec
.zcash_serialize_to_vec()
.expect("Serialization to vec must succeed");
// Check that our smallest_disallowed_vec is only one item larger than the limit
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == AddrV1::max_allocation());
// Check that our smallest_disallowed_vec is too big to send in a valid Zcash message
prop_assert!(smallest_disallowed_serialized_len > MAX_PROTOCOL_MESSAGE_LEN);
// Check that our largest_allowed_vec contains the maximum number of AddrV1s
assert!((largest_allowed_vec.len() as u64) == AddrV1::max_allocation());
prop_assert!((largest_allowed_vec_len as u64) == AddrV1::max_allocation());
// Check that our largest_allowed_vec is small enough to fit in a Zcash message.
assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
prop_assert!(largest_allowed_serialized_len <= MAX_PROTOCOL_MESSAGE_LEN);
}
}