Simplify preallocate tests (#3032)
* Simplify preallocation tests using a test function * Use prop_assert in proptests
This commit is contained in:
parent
02526c3339
commit
c0c00b3f0d
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
mod preallocate;
|
||||
mod prop;
|
||||
|
||||
pub use preallocate::max_allocation_is_big_enough;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue