Move the preallocate tests into their own files (#1977)
* Move the preallocate tests into their own files And move the MetaAddr proptest into its own file. Also do some minor formatting and cleanups. Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com>
This commit is contained in:
parent
05b60db993
commit
64662a758d
|
@ -914,20 +914,6 @@ dependencies = [
|
||||||
"syn 1.0.60",
|
"syn 1.0.60",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ed25519-zebra"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0a128b76af6dd4b427e34a6fd43dc78dbfe73672ec41ff615a2414c1a0ad0409"
|
|
||||||
dependencies = [
|
|
||||||
"curve25519-dalek",
|
|
||||||
"hex",
|
|
||||||
"rand_core 0.5.1",
|
|
||||||
"serde",
|
|
||||||
"sha2",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ed25519-zebra"
|
name = "ed25519-zebra"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -942,6 +928,20 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519-zebra"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a128b76af6dd4b427e34a6fd43dc78dbfe73672ec41ff615a2414c1a0ad0409"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek",
|
||||||
|
"hex",
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
"serde",
|
||||||
|
"sha2",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
|
|
|
@ -91,8 +91,10 @@ impl<'a> From<&'a Block> for Hash {
|
||||||
(&block.header).into()
|
(&block.header).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A serialized Block hash takes 32 bytes
|
/// A serialized Block hash takes 32 bytes
|
||||||
const BLOCK_HASH_SIZE: u64 = 32;
|
const BLOCK_HASH_SIZE: u64 = 32;
|
||||||
|
|
||||||
/// The maximum number of hashes in a valid Zcash protocol message.
|
/// The maximum number of hashes in a valid Zcash protocol message.
|
||||||
impl TrustedPreallocate for Hash {
|
impl TrustedPreallocate for Hash {
|
||||||
fn max_allocation() -> u64 {
|
fn max_allocation() -> u64 {
|
||||||
|
@ -101,53 +103,3 @@ impl TrustedPreallocate for Hash {
|
||||||
((MAX_PROTOCOL_MESSAGE_LEN - 1) as u64) / BLOCK_HASH_SIZE
|
((MAX_PROTOCOL_MESSAGE_LEN - 1) as u64) / BLOCK_HASH_SIZE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_trusted_preallocate {
|
|
||||||
use super::{Hash, BLOCK_HASH_SIZE, MAX_PROTOCOL_MESSAGE_LEN};
|
|
||||||
use crate::serialization::{TrustedPreallocate, ZcashSerialize};
|
|
||||||
use proptest::prelude::*;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(10_000))]
|
|
||||||
/// Verify that the serialized size of a block hash used to calculate the allocation limit is correct
|
|
||||||
#[test]
|
|
||||||
fn block_hash_size_is_correct(hash in Hash::arbitrary()) {
|
|
||||||
let serialized = hash.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
||||||
prop_assert!(serialized.len() as u64 == BLOCK_HASH_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
proptest! {
|
|
||||||
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(200))]
|
|
||||||
|
|
||||||
/// Verify that...
|
|
||||||
/// 1. The smallest disallowed vector of `Hash`s is too large to send via the Zcash Wire Protocol
|
|
||||||
/// 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_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());
|
|
||||||
// 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");
|
|
||||||
|
|
||||||
// Check that our largest_allowed_vec contains the maximum number of hashes
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -135,7 +135,8 @@ const BLOCK_HEADER_LENGTH: usize =
|
||||||
/// The minimum size for a serialized CountedHeader.
|
/// The minimum size for a serialized CountedHeader.
|
||||||
///
|
///
|
||||||
/// A CountedHeader has BLOCK_HEADER_LENGTH bytes + 1 or more bytes for the transaction count
|
/// A CountedHeader has BLOCK_HEADER_LENGTH bytes + 1 or more bytes for the transaction count
|
||||||
const MIN_COUNTED_HEADER_LEN: usize = BLOCK_HEADER_LENGTH + 1;
|
pub(crate) const MIN_COUNTED_HEADER_LEN: usize = BLOCK_HEADER_LENGTH + 1;
|
||||||
|
|
||||||
impl TrustedPreallocate for CountedHeader {
|
impl TrustedPreallocate for CountedHeader {
|
||||||
fn max_allocation() -> u64 {
|
fn max_allocation() -> u64 {
|
||||||
// Every vector type requires a length field of at least one byte for de/serialization.
|
// Every vector type requires a length field of at least one byte for de/serialization.
|
||||||
|
@ -143,61 +144,3 @@ impl TrustedPreallocate for CountedHeader {
|
||||||
((MAX_PROTOCOL_MESSAGE_LEN - 1) / MIN_COUNTED_HEADER_LEN) as u64
|
((MAX_PROTOCOL_MESSAGE_LEN - 1) / MIN_COUNTED_HEADER_LEN) as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_trusted_preallocate {
|
|
||||||
use super::{CountedHeader, Header, MAX_PROTOCOL_MESSAGE_LEN, MIN_COUNTED_HEADER_LEN};
|
|
||||||
use crate::serialization::{TrustedPreallocate, ZcashSerialize};
|
|
||||||
use proptest::prelude::*;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
proptest! {
|
|
||||||
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(10_000))]
|
|
||||||
|
|
||||||
/// Confirm that each counted header takes at least COUNTED_HEADER_LEN bytes when serialized.
|
|
||||||
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
|
||||||
#[test]
|
|
||||||
fn counted_header_min_length(header in Header::arbitrary_with(()), transaction_count in (0..std::u32::MAX)) {
|
|
||||||
let header = CountedHeader {
|
|
||||||
header,
|
|
||||||
transaction_count: transaction_count.try_into().expect("Must run test on platform with at least 32 bit address space"),
|
|
||||||
};
|
|
||||||
let serialized_header = header.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
||||||
prop_assert!(serialized_header.len() >= MIN_COUNTED_HEADER_LEN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(100))]
|
|
||||||
/// Verify that...
|
|
||||||
/// 1. The smallest disallowed vector of `CountedHeaders`s is too large to send via the Zcash Wire Protocol
|
|
||||||
/// 2. The largest allowed vector is small enough to fit in a legal Zcash Wire Protocol message
|
|
||||||
#[test]
|
|
||||||
fn counted_header_max_allocation(header in Header::arbitrary_with(())) {
|
|
||||||
let header = CountedHeader {
|
|
||||||
header,
|
|
||||||
transaction_count: 0,
|
|
||||||
};
|
|
||||||
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.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) == 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");
|
|
||||||
|
|
||||||
// Check that our largest_allowed_vec contains the maximum number of CountedHeaders
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// XXX this should be rewritten as strategies
|
// XXX generate should be rewritten as strategies
|
||||||
mod generate;
|
mod generate;
|
||||||
|
mod preallocate;
|
||||||
mod prop;
|
mod prop;
|
||||||
mod vectors;
|
mod vectors;
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
//! Tests for trusted preallocation during deserialization.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
block::{
|
||||||
|
header::MIN_COUNTED_HEADER_LEN, CountedHeader, Hash, Header, BLOCK_HASH_SIZE,
|
||||||
|
MAX_PROTOCOL_MESSAGE_LEN,
|
||||||
|
},
|
||||||
|
serialization::{TrustedPreallocate, ZcashSerialize},
|
||||||
|
};
|
||||||
|
|
||||||
|
use proptest::prelude::*;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
/// Verify that the serialized size of a block hash used to calculate the allocation limit is correct
|
||||||
|
#[test]
|
||||||
|
fn block_hash_size_is_correct(hash in Hash::arbitrary()) {
|
||||||
|
let serialized = hash.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
||||||
|
prop_assert!(serialized.len() as u64 == BLOCK_HASH_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify that...
|
||||||
|
/// 1. The smallest disallowed vector of `Hash`s is too large to send via the Zcash Wire Protocol
|
||||||
|
/// 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_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());
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
// Check that our largest_allowed_vec contains the maximum number of hashes
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Confirm that each counted header takes at least COUNTED_HEADER_LEN bytes when serialized.
|
||||||
|
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
||||||
|
#[test]
|
||||||
|
fn counted_header_min_length(header in Header::arbitrary_with(()), transaction_count in (0..std::u32::MAX)) {
|
||||||
|
let header = CountedHeader {
|
||||||
|
header,
|
||||||
|
transaction_count: transaction_count.try_into().expect("Must run test on platform with at least 32 bit address space"),
|
||||||
|
};
|
||||||
|
let serialized_header = header.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
||||||
|
prop_assert!(serialized_header.len() >= MIN_COUNTED_HEADER_LEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#![proptest_config(ProptestConfig::with_cases(128))]
|
||||||
|
|
||||||
|
/// Verify that...
|
||||||
|
/// 1. The smallest disallowed vector of `CountedHeaders`s is too large to send via the Zcash Wire Protocol
|
||||||
|
/// 2. The largest allowed vector is small enough to fit in a legal Zcash Wire Protocol message
|
||||||
|
#[test]
|
||||||
|
fn counted_header_max_allocation(header in Header::arbitrary_with(())) {
|
||||||
|
let header = CountedHeader {
|
||||||
|
header,
|
||||||
|
transaction_count: 0,
|
||||||
|
};
|
||||||
|
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.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) == 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");
|
||||||
|
|
||||||
|
// Check that our largest_allowed_vec contains the maximum number of CountedHeaders
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,10 +78,11 @@ impl ZcashDeserialize for Output {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An output contains: a 32 byte cv, a 32 byte cmu, a 32 byte ephemeral key
|
/// An output contains: a 32 byte cv, a 32 byte cmu, a 32 byte ephemeral key
|
||||||
/// a 580 byte encCiphertext, an 80 byte outCiphertext, and a 192 byte zkproof
|
/// a 580 byte encCiphertext, an 80 byte outCiphertext, and a 192 byte zkproof
|
||||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#outputencoding
|
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#outputencoding
|
||||||
const OUTPUT_SIZE: u64 = 32 + 32 + 32 + 580 + 80 + 192;
|
pub(crate) const OUTPUT_SIZE: u64 = 32 + 32 + 32 + 580 + 80 + 192;
|
||||||
|
|
||||||
/// The maximum number of outputs in a valid Zcash on-chain transaction.
|
/// The maximum number of outputs in a valid Zcash on-chain transaction.
|
||||||
///
|
///
|
||||||
|
@ -95,56 +96,3 @@ impl TrustedPreallocate for Output {
|
||||||
(MAX_BLOCK_BYTES - 1) / OUTPUT_SIZE
|
(MAX_BLOCK_BYTES - 1) / OUTPUT_SIZE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_trusted_preallocate {
|
|
||||||
use super::{Output, MAX_BLOCK_BYTES, OUTPUT_SIZE};
|
|
||||||
use crate::serialization::{TrustedPreallocate, ZcashSerialize};
|
|
||||||
use proptest::prelude::*;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(10_000))]
|
|
||||||
|
|
||||||
/// Confirm that each output takes exactly OUTPUT_SIZE bytes when serialized.
|
|
||||||
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
|
||||||
#[test]
|
|
||||||
fn output_size_is_small_enough(output in Output::arbitrary_with(())) {
|
|
||||||
let serialized = output.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
||||||
prop_assert!(serialized.len() as u64 == OUTPUT_SIZE)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(100))]
|
|
||||||
/// Verify that...
|
|
||||||
/// 1. The smallest disallowed vector of `Outputs`s is too large to fit in a Zcash block
|
|
||||||
/// 2. The largest allowed vector is small enough to fit in a legal Zcash block
|
|
||||||
#[test]
|
|
||||||
fn output_max_allocation_is_big_enough(output in Output::arbitrary_with(())) {
|
|
||||||
|
|
||||||
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 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) == 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!((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 Outputs
|
|
||||||
prop_assert!((largest_allowed_vec.len() as u64) == Output::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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -145,15 +145,15 @@ impl ZcashSerialize for Spend<SharedAnchor> {
|
||||||
/// in V5), and a 64 byte spendAuthSig (serialized separately in V5).
|
/// in V5), and a 64 byte spendAuthSig (serialized separately in V5).
|
||||||
///
|
///
|
||||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#spendencoding
|
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#spendencoding
|
||||||
const SHARED_ANCHOR_SPEND_FULL_SIZE: u64 = SHARED_ANCHOR_SPEND_INITIAL_SIZE + 192 + 64;
|
pub(crate) const SHARED_ANCHOR_SPEND_FULL_SIZE: u64 = SHARED_ANCHOR_SPEND_INITIAL_SIZE + 192 + 64;
|
||||||
/// The size of a spend with a shared anchor, without associated fields.
|
/// The size of a spend with a shared anchor, without associated fields.
|
||||||
///
|
///
|
||||||
/// This is the size of spends in the initial array, there are another
|
/// This is the size of spends in the initial array, there are another
|
||||||
/// 2 arrays of zkproofs and spend_auth_sigs required in the transaction format.
|
/// 2 arrays of zkproofs and spend_auth_sigs required in the transaction format.
|
||||||
const SHARED_ANCHOR_SPEND_INITIAL_SIZE: u64 = 32 + 32 + 32;
|
pub(crate) const SHARED_ANCHOR_SPEND_INITIAL_SIZE: u64 = 32 + 32 + 32;
|
||||||
|
|
||||||
/// The size of a spend with a per-spend anchor.
|
/// The size of a spend with a per-spend anchor.
|
||||||
const ANCHOR_PER_SPEND_SIZE: u64 = SHARED_ANCHOR_SPEND_FULL_SIZE + 32;
|
pub(crate) const ANCHOR_PER_SPEND_SIZE: u64 = SHARED_ANCHOR_SPEND_FULL_SIZE + 32;
|
||||||
|
|
||||||
/// The maximum number of spends in a valid Zcash on-chain transaction V5.
|
/// The maximum number of spends in a valid Zcash on-chain transaction V5.
|
||||||
///
|
///
|
||||||
|
@ -175,121 +175,3 @@ impl TrustedPreallocate for Spend<PerSpendAnchor> {
|
||||||
(MAX_BLOCK_BYTES - 1) / ANCHOR_PER_SPEND_SIZE
|
(MAX_BLOCK_BYTES - 1) / ANCHOR_PER_SPEND_SIZE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_trusted_preallocate {
|
|
||||||
use super::{
|
|
||||||
Spend, ANCHOR_PER_SPEND_SIZE, MAX_BLOCK_BYTES, SHARED_ANCHOR_SPEND_FULL_SIZE,
|
|
||||||
SHARED_ANCHOR_SPEND_INITIAL_SIZE,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
sapling::{AnchorVariant, PerSpendAnchor, SharedAnchor},
|
|
||||||
serialization::{TrustedPreallocate, ZcashSerialize},
|
|
||||||
};
|
|
||||||
use proptest::prelude::*;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(10_000))]
|
|
||||||
|
|
||||||
/// Confirm that each spend takes exactly ANCHOR_PER_SPEND_SIZE bytes when serialized.
|
|
||||||
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
|
||||||
#[test]
|
|
||||||
fn anchor_per_spend_size_is_small_enough(spend in Spend::<PerSpendAnchor>::arbitrary_with(())) {
|
|
||||||
let serialized = spend.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
||||||
prop_assert!(serialized.len() as u64 == ANCHOR_PER_SPEND_SIZE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Confirm that each spend takes exactly SHARED_SPEND_SIZE bytes when serialized.
|
|
||||||
#[test]
|
|
||||||
fn shared_anchor_spend_size_is_small_enough(spend in Spend::<SharedAnchor>::arbitrary_with(())) {
|
|
||||||
let mut serialized_len = spend.zcash_serialize_to_vec().expect("Serialization to vec must succeed").len();
|
|
||||||
serialized_len += spend.zkproof.zcash_serialize_to_vec().expect("Serialization to vec must succeed").len();
|
|
||||||
serialized_len += &<[u8; 64]>::from(spend.spend_auth_sig).len();
|
|
||||||
prop_assert!(serialized_len as u64 == SHARED_ANCHOR_SPEND_FULL_SIZE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(100))]
|
|
||||||
|
|
||||||
/// Verify that...
|
|
||||||
/// 1. The smallest disallowed vector of `Spend`s is too large to fit in a Zcash block
|
|
||||||
/// 2. The largest allowed vector is small enough to fit in a legal Zcash block
|
|
||||||
#[test]
|
|
||||||
fn anchor_per_spend_max_allocation_is_big_enough(spend in Spend::<PerSpendAnchor>::arbitrary_with(())) {
|
|
||||||
let (
|
|
||||||
smallest_disallowed_vec_len,
|
|
||||||
smallest_disallowed_serialized_len,
|
|
||||||
largest_allowed_vec_len,
|
|
||||||
largest_allowed_serialized_len,
|
|
||||||
) = spend_max_allocation_is_big_enough(spend);
|
|
||||||
|
|
||||||
// Check that our smallest_disallowed_vec is only one item larger than the limit
|
|
||||||
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Spend::<PerSpendAnchor>::max_allocation());
|
|
||||||
// Check that our smallest_disallowed_vec is too big to send as a protocol message
|
|
||||||
// Note that a serialized block always includes at least one byte for the number of transactions,
|
|
||||||
// so any serialized Vec<Spend> 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);
|
|
||||||
|
|
||||||
// Check that our largest_allowed_vec contains the maximum number of spends
|
|
||||||
prop_assert!((largest_allowed_vec_len as u64) == Spend::<PerSpendAnchor>::max_allocation());
|
|
||||||
// Check that our largest_allowed_vec is small enough to send as a protocol message
|
|
||||||
prop_assert!((largest_allowed_serialized_len as u64) <= MAX_BLOCK_BYTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify trusted preallocation for `Spend<SharedAnchor>`
|
|
||||||
#[test]
|
|
||||||
fn shared_spend_max_allocation_is_big_enough(spend in Spend::<SharedAnchor>::arbitrary_with(())) {
|
|
||||||
let (
|
|
||||||
smallest_disallowed_vec_len,
|
|
||||||
smallest_disallowed_serialized_len,
|
|
||||||
largest_allowed_vec_len,
|
|
||||||
largest_allowed_serialized_len,
|
|
||||||
) = spend_max_allocation_is_big_enough(spend);
|
|
||||||
|
|
||||||
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Spend::<SharedAnchor>::max_allocation());
|
|
||||||
// Calculate the actual size of all required Spend fields
|
|
||||||
//
|
|
||||||
// TODO: modify the test to serialize the associated zkproof and
|
|
||||||
// spend_auth_sig fields
|
|
||||||
prop_assert!((smallest_disallowed_serialized_len as u64)/SHARED_ANCHOR_SPEND_INITIAL_SIZE*SHARED_ANCHOR_SPEND_FULL_SIZE >= MAX_BLOCK_BYTES);
|
|
||||||
|
|
||||||
prop_assert!((largest_allowed_vec_len as u64) == Spend::<SharedAnchor>::max_allocation());
|
|
||||||
prop_assert!((largest_allowed_serialized_len as u64) <= MAX_BLOCK_BYTES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the
|
|
||||||
fn spend_max_allocation_is_big_enough<AnchorV>(
|
|
||||||
spend: Spend<AnchorV>,
|
|
||||||
) -> (usize, usize, usize, usize)
|
|
||||||
where
|
|
||||||
AnchorV: AnchorVariant,
|
|
||||||
Spend<AnchorV>: TrustedPreallocate + ZcashSerialize + Clone,
|
|
||||||
{
|
|
||||||
let max_allocation: usize = Spend::max_allocation().try_into().unwrap();
|
|
||||||
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
|
|
||||||
for _ in 0..(Spend::max_allocation() + 1) {
|
|
||||||
smallest_disallowed_vec.push(spend.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_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 +1,2 @@
|
||||||
|
mod preallocate;
|
||||||
mod prop;
|
mod prop;
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
//! Tests for trusted preallocation during deserialization.
|
||||||
|
|
||||||
|
use super::super::{
|
||||||
|
output::{Output, OUTPUT_SIZE},
|
||||||
|
spend::{
|
||||||
|
Spend, ANCHOR_PER_SPEND_SIZE, SHARED_ANCHOR_SPEND_FULL_SIZE,
|
||||||
|
SHARED_ANCHOR_SPEND_INITIAL_SIZE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
block::MAX_BLOCK_BYTES,
|
||||||
|
sapling::{AnchorVariant, PerSpendAnchor, SharedAnchor},
|
||||||
|
serialization::{TrustedPreallocate, ZcashSerialize},
|
||||||
|
};
|
||||||
|
|
||||||
|
use proptest::prelude::*;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
/// Confirm that each spend takes exactly ANCHOR_PER_SPEND_SIZE bytes when serialized.
|
||||||
|
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
||||||
|
#[test]
|
||||||
|
fn anchor_per_spend_size_is_small_enough(spend in Spend::<PerSpendAnchor>::arbitrary_with(())) {
|
||||||
|
let serialized = spend.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
||||||
|
prop_assert!(serialized.len() as u64 == ANCHOR_PER_SPEND_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Confirm that each spend takes exactly SHARED_SPEND_SIZE bytes when serialized.
|
||||||
|
#[test]
|
||||||
|
fn shared_anchor_spend_size_is_small_enough(spend in Spend::<SharedAnchor>::arbitrary_with(())) {
|
||||||
|
let mut serialized_len = spend.zcash_serialize_to_vec().expect("Serialization to vec must succeed").len();
|
||||||
|
serialized_len += spend.zkproof.zcash_serialize_to_vec().expect("Serialization to vec must succeed").len();
|
||||||
|
serialized_len += &<[u8; 64]>::from(spend.spend_auth_sig).len();
|
||||||
|
prop_assert!(serialized_len as u64 == SHARED_ANCHOR_SPEND_FULL_SIZE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#![proptest_config(ProptestConfig::with_cases(128))]
|
||||||
|
|
||||||
|
/// Verify that...
|
||||||
|
/// 1. The smallest disallowed vector of `Spend`s is too large to fit in a Zcash block
|
||||||
|
/// 2. The largest allowed vector is small enough to fit in a legal Zcash block
|
||||||
|
#[test]
|
||||||
|
fn anchor_per_spend_max_allocation_is_big_enough(spend in Spend::<PerSpendAnchor>::arbitrary_with(())) {
|
||||||
|
let (
|
||||||
|
smallest_disallowed_vec_len,
|
||||||
|
smallest_disallowed_serialized_len,
|
||||||
|
largest_allowed_vec_len,
|
||||||
|
largest_allowed_serialized_len,
|
||||||
|
) = spend_max_allocation_is_big_enough(spend);
|
||||||
|
|
||||||
|
// Check that our smallest_disallowed_vec is only one item larger than the limit
|
||||||
|
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Spend::<PerSpendAnchor>::max_allocation());
|
||||||
|
// Check that our smallest_disallowed_vec is too big to send as a protocol message
|
||||||
|
// Note that a serialized block always includes at least one byte for the number of transactions,
|
||||||
|
// so any serialized Vec<Spend> 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);
|
||||||
|
|
||||||
|
// Check that our largest_allowed_vec contains the maximum number of spends
|
||||||
|
prop_assert!((largest_allowed_vec_len as u64) == Spend::<PerSpendAnchor>::max_allocation());
|
||||||
|
// Check that our largest_allowed_vec is small enough to send as a protocol message
|
||||||
|
prop_assert!((largest_allowed_serialized_len as u64) <= MAX_BLOCK_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify trusted preallocation for `Spend<SharedAnchor>`
|
||||||
|
#[test]
|
||||||
|
fn shared_spend_max_allocation_is_big_enough(spend in Spend::<SharedAnchor>::arbitrary_with(())) {
|
||||||
|
let (
|
||||||
|
smallest_disallowed_vec_len,
|
||||||
|
smallest_disallowed_serialized_len,
|
||||||
|
largest_allowed_vec_len,
|
||||||
|
largest_allowed_serialized_len,
|
||||||
|
) = spend_max_allocation_is_big_enough(spend);
|
||||||
|
|
||||||
|
prop_assert!(((smallest_disallowed_vec_len - 1) as u64) == Spend::<SharedAnchor>::max_allocation());
|
||||||
|
// Calculate the actual size of all required Spend fields
|
||||||
|
//
|
||||||
|
// TODO: modify the test to serialize the associated zkproof and
|
||||||
|
// spend_auth_sig fields
|
||||||
|
prop_assert!((smallest_disallowed_serialized_len as u64)/SHARED_ANCHOR_SPEND_INITIAL_SIZE*SHARED_ANCHOR_SPEND_FULL_SIZE >= MAX_BLOCK_BYTES);
|
||||||
|
|
||||||
|
prop_assert!((largest_allowed_vec_len as u64) == Spend::<SharedAnchor>::max_allocation());
|
||||||
|
prop_assert!((largest_allowed_serialized_len as u64) <= MAX_BLOCK_BYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the
|
||||||
|
fn spend_max_allocation_is_big_enough<AnchorV>(
|
||||||
|
spend: Spend<AnchorV>,
|
||||||
|
) -> (usize, usize, usize, usize)
|
||||||
|
where
|
||||||
|
AnchorV: AnchorVariant,
|
||||||
|
Spend<AnchorV>: TrustedPreallocate + ZcashSerialize + Clone,
|
||||||
|
{
|
||||||
|
let max_allocation: usize = Spend::max_allocation().try_into().unwrap();
|
||||||
|
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
|
||||||
|
for _ in 0..(Spend::max_allocation() + 1) {
|
||||||
|
smallest_disallowed_vec.push(spend.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_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! {
|
||||||
|
/// Confirm that each output takes exactly OUTPUT_SIZE bytes when serialized.
|
||||||
|
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
||||||
|
#[test]
|
||||||
|
fn output_size_is_small_enough(output in Output::arbitrary_with(())) {
|
||||||
|
let serialized = output.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
||||||
|
prop_assert!(serialized.len() as u64 == OUTPUT_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#![proptest_config(ProptestConfig::with_cases(128))]
|
||||||
|
|
||||||
|
/// Verify that...
|
||||||
|
/// 1. The smallest disallowed vector of `Outputs`s is too large to fit in a Zcash block
|
||||||
|
/// 2. The largest allowed vector is small enough to fit in a legal Zcash block
|
||||||
|
#[test]
|
||||||
|
fn output_max_allocation_is_big_enough(output in Output::arbitrary_with(())) {
|
||||||
|
|
||||||
|
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 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) == 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!((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 Outputs
|
||||||
|
prop_assert!((largest_allowed_vec.len() as u64) == Output::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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@
|
||||||
#[cfg(any(test, feature = "proptest-impl"))]
|
#[cfg(any(test, feature = "proptest-impl"))]
|
||||||
mod arbitrary;
|
mod arbitrary;
|
||||||
mod joinsplit;
|
mod joinsplit;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
// XXX clean up these modules
|
// XXX clean up these modules
|
||||||
|
|
||||||
|
|
|
@ -111,13 +111,13 @@ const JOINSPLIT_SIZE_WITHOUT_ZKPROOF: u64 =
|
||||||
/// A BTCV14 proof takes 296 bytes, per the Zcash [protocol specification §7.2][ps]
|
/// A BTCV14 proof takes 296 bytes, per the Zcash [protocol specification §7.2][ps]
|
||||||
///
|
///
|
||||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding
|
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding
|
||||||
const BCTV14_JOINSPLIT_SIZE: u64 = JOINSPLIT_SIZE_WITHOUT_ZKPROOF + 296;
|
pub(crate) const BCTV14_JOINSPLIT_SIZE: u64 = JOINSPLIT_SIZE_WITHOUT_ZKPROOF + 296;
|
||||||
/// The size of a version 4+ joinsplit transaction, which uses a Groth16 proof
|
/// The size of a version 4+ joinsplit transaction, which uses a Groth16 proof
|
||||||
///
|
///
|
||||||
/// A Groth16 proof takes 192 bytes, per the Zcash [protocol specification §7.2][ps]
|
/// A Groth16 proof takes 192 bytes, per the Zcash [protocol specification §7.2][ps]
|
||||||
///
|
///
|
||||||
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding
|
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding
|
||||||
const GROTH16_JOINSPLIT_SIZE: u64 = JOINSPLIT_SIZE_WITHOUT_ZKPROOF + 192;
|
pub(crate) const GROTH16_JOINSPLIT_SIZE: u64 = JOINSPLIT_SIZE_WITHOUT_ZKPROOF + 192;
|
||||||
|
|
||||||
impl TrustedPreallocate for JoinSplit<Bctv14Proof> {
|
impl TrustedPreallocate for JoinSplit<Bctv14Proof> {
|
||||||
fn max_allocation() -> u64 {
|
fn max_allocation() -> u64 {
|
||||||
|
@ -136,95 +136,3 @@ impl TrustedPreallocate for JoinSplit<Groth16Proof> {
|
||||||
(MAX_BLOCK_BYTES - 1) / GROTH16_JOINSPLIT_SIZE
|
(MAX_BLOCK_BYTES - 1) / GROTH16_JOINSPLIT_SIZE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_trusted_preallocate {
|
|
||||||
use super::{
|
|
||||||
Bctv14Proof, Groth16Proof, JoinSplit, BCTV14_JOINSPLIT_SIZE, GROTH16_JOINSPLIT_SIZE,
|
|
||||||
MAX_BLOCK_BYTES,
|
|
||||||
};
|
|
||||||
use crate::serialization::{TrustedPreallocate, ZcashSerialize};
|
|
||||||
use proptest::{prelude::*, proptest};
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(1_000))]
|
|
||||||
#[test]
|
|
||||||
/// 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.
|
|
||||||
fn joinsplit_btcv14_size_is_correct(joinsplit in <JoinSplit<Bctv14Proof>>::arbitrary_with(())) {
|
|
||||||
let serialized = joinsplit.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
||||||
prop_assert!(serialized.len() as u64 == BCTV14_JOINSPLIT_SIZE)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
/// Confirm that each JoinSplit<Btcv14Proof> takes exactly GROTH16_JOINSPLIT_SIZE bytes when serialized.
|
|
||||||
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
|
||||||
fn joinsplit_groth16_size_is_correct(joinsplit in <JoinSplit<Groth16Proof>>::arbitrary_with(())) {
|
|
||||||
let serialized = joinsplit.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
||||||
prop_assert!(serialized.len() as u64 == GROTH16_JOINSPLIT_SIZE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(100))]
|
|
||||||
/// Verify that...
|
|
||||||
/// 1. The smallest disallowed vector of `JoinSplit<Bctv14Proof>`s is too large to fit in a Zcash block
|
|
||||||
/// 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 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);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify that...
|
|
||||||
/// 1. The smallest disallowed vector of `JoinSplit<Groth16Proof>`s is too large to fit in a Zcash block
|
|
||||||
/// 2. The largest allowed vector is small enough to fit in a legal Zcash block
|
|
||||||
#[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");
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
mod arbitrary;
|
mod preallocate;
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
//! Tests for trusted preallocation during deserialization.
|
||||||
|
|
||||||
|
use super::super::joinsplit::{JoinSplit, BCTV14_JOINSPLIT_SIZE, GROTH16_JOINSPLIT_SIZE};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
block::MAX_BLOCK_BYTES,
|
||||||
|
primitives::{Bctv14Proof, Groth16Proof},
|
||||||
|
serialization::{TrustedPreallocate, ZcashSerialize},
|
||||||
|
};
|
||||||
|
|
||||||
|
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.
|
||||||
|
#[test]
|
||||||
|
fn joinsplit_btcv14_size_is_correct(joinsplit in <JoinSplit<Bctv14Proof>>::arbitrary_with(())) {
|
||||||
|
let serialized = joinsplit.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
||||||
|
prop_assert!(serialized.len() as u64 == BCTV14_JOINSPLIT_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Confirm that each JoinSplit<Btcv14Proof> takes exactly GROTH16_JOINSPLIT_SIZE bytes when serialized.
|
||||||
|
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
||||||
|
#[test]
|
||||||
|
fn joinsplit_groth16_size_is_correct(joinsplit in <JoinSplit<Groth16Proof>>::arbitrary_with(())) {
|
||||||
|
let serialized = joinsplit.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
||||||
|
prop_assert!(serialized.len() as u64 == GROTH16_JOINSPLIT_SIZE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#![proptest_config(ProptestConfig::with_cases(128))]
|
||||||
|
|
||||||
|
/// Verify that...
|
||||||
|
/// 1. The smallest disallowed vector of `JoinSplit<Bctv14Proof>`s is too large to fit in a Zcash block
|
||||||
|
/// 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 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify that...
|
||||||
|
/// 1. The smallest disallowed vector of `JoinSplit<Groth16Proof>`s is too large to fit in a Zcash block
|
||||||
|
/// 2. The largest allowed vector is small enough to fit in a legal Zcash block
|
||||||
|
#[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");
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -357,11 +357,12 @@ where
|
||||||
|
|
||||||
/// A Tx Input must have an Outpoint (32 byte hash + 4 byte index), a 4 byte sequence number,
|
/// 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)
|
||||||
const MIN_TRANSPARENT_INPUT_SIZE: u64 = 32 + 4 + 4 + 1;
|
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
|
||||||
const MIN_TRANSPARENT_OUTPUT_SIZE: u64 = 8 + 1;
|
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
|
||||||
const MIN_TRANSPARENT_TX_SIZE: u64 = MIN_TRANSPARENT_INPUT_SIZE + 4 + MIN_TRANSPARENT_OUTPUT_SIZE;
|
pub(crate) const MIN_TRANSPARENT_TX_SIZE: u64 =
|
||||||
|
MIN_TRANSPARENT_INPUT_SIZE + 4 + MIN_TRANSPARENT_OUTPUT_SIZE;
|
||||||
|
|
||||||
/// No valid Zcash message contains more transactions than can fit in a single block
|
/// No valid Zcash message contains more transactions than can fit in a single block
|
||||||
///
|
///
|
||||||
|
@ -373,6 +374,7 @@ impl TrustedPreallocate for Arc<Transaction> {
|
||||||
MAX_BLOCK_BYTES / MIN_TRANSPARENT_TX_SIZE
|
MAX_BLOCK_BYTES / MIN_TRANSPARENT_TX_SIZE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum number of inputs in a valid Zcash on-chain transaction.
|
/// The maximum number of inputs in a valid Zcash on-chain transaction.
|
||||||
///
|
///
|
||||||
/// If a transaction contains more inputs than can fit in maximally large block, it might be
|
/// If a transaction contains more inputs than can fit in maximally large block, it might be
|
||||||
|
@ -383,6 +385,7 @@ impl TrustedPreallocate for transparent::Input {
|
||||||
MAX_BLOCK_BYTES / MIN_TRANSPARENT_INPUT_SIZE
|
MAX_BLOCK_BYTES / MIN_TRANSPARENT_INPUT_SIZE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum number of outputs in a valid Zcash on-chain transaction.
|
/// The maximum number of outputs in a valid Zcash on-chain transaction.
|
||||||
///
|
///
|
||||||
/// If a transaction contains more outputs than can fit in maximally large block, it might be
|
/// If a transaction contains more outputs than can fit in maximally large block, it might be
|
||||||
|
@ -393,99 +396,3 @@ impl TrustedPreallocate for transparent::Output {
|
||||||
MAX_BLOCK_BYTES / MIN_TRANSPARENT_OUTPUT_SIZE
|
MAX_BLOCK_BYTES / MIN_TRANSPARENT_OUTPUT_SIZE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_trusted_preallocate {
|
|
||||||
use super::{
|
|
||||||
transparent::Input, transparent::Output, Transaction, MAX_BLOCK_BYTES,
|
|
||||||
MIN_TRANSPARENT_INPUT_SIZE, MIN_TRANSPARENT_OUTPUT_SIZE, MIN_TRANSPARENT_TX_SIZE,
|
|
||||||
};
|
|
||||||
use crate::serialization::{TrustedPreallocate, ZcashSerialize};
|
|
||||||
use proptest::prelude::*;
|
|
||||||
use std::{convert::TryInto, sync::Arc};
|
|
||||||
proptest! {
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(300))]
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
#[test]
|
|
||||||
fn tx_size_is_small_enough(tx in Transaction::arbitrary()) {
|
|
||||||
let serialized = tx.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
||||||
prop_assert!(serialized.len() as u64 >= MIN_TRANSPARENT_TX_SIZE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
#[test]
|
|
||||||
fn transparent_input_size_is_small_enough(input in Input::arbitrary()) {
|
|
||||||
let serialized = input.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
||||||
prop_assert!(serialized.len() as u64 >= MIN_TRANSPARENT_INPUT_SIZE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
#[test]
|
|
||||||
fn transparent_output_size_is_small_enough(output in Output::arbitrary()) {
|
|
||||||
let serialized = output.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
|
||||||
prop_assert!(serialized.len() as u64 >= MIN_TRANSPARENT_OUTPUT_SIZE)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
proptest! {
|
|
||||||
// This test is pretty slow, so only run a few
|
|
||||||
#![proptest_config(ProptestConfig::with_cases(7))]
|
|
||||||
#[test]
|
|
||||||
/// Verify the smallest disallowed vector of `Transaction`s is too large to fit in a Zcash block
|
|
||||||
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");
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
/// Verify the smallest disallowed vector of `Input`s is too large to fit in a Zcash block
|
|
||||||
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");
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
/// Verify the smallest disallowed vector of `Output`s is too large to fit in a Zcash block
|
|
||||||
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");
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
|
mod preallocate;
|
||||||
mod prop;
|
mod prop;
|
||||||
mod vectors;
|
mod vectors;
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
//! 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 crate::{
|
||||||
|
block::MAX_BLOCK_BYTES,
|
||||||
|
serialization::{TrustedPreallocate, ZcashSerialize},
|
||||||
|
};
|
||||||
|
|
||||||
|
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.
|
||||||
|
#[test]
|
||||||
|
fn tx_size_is_small_enough(tx in Transaction::arbitrary()) {
|
||||||
|
let serialized = tx.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
||||||
|
prop_assert!(serialized.len() as u64 >= MIN_TRANSPARENT_TX_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[test]
|
||||||
|
fn transparent_input_size_is_small_enough(input in Input::arbitrary()) {
|
||||||
|
let serialized = input.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
||||||
|
prop_assert!(serialized.len() as u64 >= MIN_TRANSPARENT_INPUT_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[test]
|
||||||
|
fn transparent_output_size_is_small_enough(output in Output::arbitrary()) {
|
||||||
|
let serialized = output.zcash_serialize_to_vec().expect("Serialization to vec must succeed");
|
||||||
|
prop_assert!(serialized.len() as u64 >= MIN_TRANSPARENT_OUTPUT_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
// This test is pretty slow, so only run a few cases
|
||||||
|
#![proptest_config(ProptestConfig::with_cases(8))]
|
||||||
|
|
||||||
|
/// 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");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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");
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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");
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,9 @@ use crate::protocol::{external::MAX_PROTOCOL_MESSAGE_LEN, types::PeerServices};
|
||||||
|
|
||||||
use PeerAddrState::*;
|
use PeerAddrState::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
/// Peer connection state, based on our interactions with the peer.
|
/// Peer connection state, based on our interactions with the peer.
|
||||||
///
|
///
|
||||||
/// Zebra also tracks how recently a peer has sent us messages, and derives peer
|
/// Zebra also tracks how recently a peer has sent us messages, and derives peer
|
||||||
|
@ -264,8 +267,10 @@ impl ZcashDeserialize for MetaAddr {
|
||||||
Ok(MetaAddr::new_gossiped(&addr, &services, &last_seen))
|
Ok(MetaAddr::new_gossiped(&addr, &services, &last_seen))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A serialized meta addr has a 4 byte time, 8 byte services, 16 byte IP addr, and 2 byte port
|
/// A serialized meta addr has a 4 byte time, 8 byte services, 16 byte IP addr, and 2 byte port
|
||||||
const META_ADDR_SIZE: usize = 4 + 8 + 16 + 2;
|
const META_ADDR_SIZE: usize = 4 + 8 + 16 + 2;
|
||||||
|
|
||||||
impl TrustedPreallocate for MetaAddr {
|
impl TrustedPreallocate for MetaAddr {
|
||||||
fn max_allocation() -> u64 {
|
fn max_allocation() -> u64 {
|
||||||
// Since a maximal serialized Vec<MetAddr> uses at least three bytes for its length (2MB messages / 30B MetaAddr implies the maximal length is much greater than 253)
|
// Since a maximal serialized Vec<MetAddr> uses at least three bytes for its length (2MB messages / 30B MetaAddr implies the maximal length is much greater than 253)
|
||||||
|
@ -273,96 +278,3 @@ impl TrustedPreallocate for MetaAddr {
|
||||||
((MAX_PROTOCOL_MESSAGE_LEN - 3) / META_ADDR_SIZE) as u64
|
((MAX_PROTOCOL_MESSAGE_LEN - 3) / META_ADDR_SIZE) as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_trusted_preallocate {
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
use super::{MetaAddr, MAX_PROTOCOL_MESSAGE_LEN, META_ADDR_SIZE};
|
|
||||||
use super::{PeerAddrState, PeerServices};
|
|
||||||
use chrono::{TimeZone, Utc};
|
|
||||||
use zebra_chain::serialization::{TrustedPreallocate, ZcashSerialize};
|
|
||||||
#[test]
|
|
||||||
/// Confirm that each MetaAddr takes exactly META_ADDR_SIZE bytes when serialized.
|
|
||||||
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
|
||||||
fn meta_addr_size_is_correct() {
|
|
||||||
let addr = MetaAddr {
|
|
||||||
addr: ([192, 168, 0, 0], 8333).into(),
|
|
||||||
services: PeerServices::default(),
|
|
||||||
last_seen: Utc.timestamp(1_573_680_222, 0),
|
|
||||||
last_connection_state: PeerAddrState::Responded,
|
|
||||||
};
|
|
||||||
let serialized = addr
|
|
||||||
.zcash_serialize_to_vec()
|
|
||||||
.expect("Serialization to vec must succeed");
|
|
||||||
assert!(serialized.len() == META_ADDR_SIZE)
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
/// Verifies that...
|
|
||||||
/// 1. The smallest disallowed vector of `MetaAddrs`s is too large to fit in a legal Zcash message
|
|
||||||
/// 2. The largest allowed vector is small enough to fit in a legal Zcash message
|
|
||||||
fn meta_addr_max_allocation_is_correct() {
|
|
||||||
let addr = MetaAddr {
|
|
||||||
addr: ([192, 168, 0, 0], 8333).into(),
|
|
||||||
services: PeerServices::default(),
|
|
||||||
last_seen: Utc.timestamp(1_573_680_222, 0),
|
|
||||||
last_connection_state: PeerAddrState::Responded,
|
|
||||||
};
|
|
||||||
let max_allocation: usize = MetaAddr::max_allocation().try_into().unwrap();
|
|
||||||
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
|
|
||||||
for _ in 0..(MetaAddr::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) == MetaAddr::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);
|
|
||||||
|
|
||||||
// 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 MetaAddrs
|
|
||||||
assert!((largest_allowed_vec.len() as u64) == MetaAddr::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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
// XXX remove this test and replace it with a proptest instance.
|
|
||||||
#[test]
|
|
||||||
fn sanitize_truncates_timestamps() {
|
|
||||||
zebra_test::init();
|
|
||||||
|
|
||||||
let services = PeerServices::default();
|
|
||||||
let addr = "127.0.0.1:8233".parse().unwrap();
|
|
||||||
|
|
||||||
let entry = MetaAddr {
|
|
||||||
services,
|
|
||||||
addr,
|
|
||||||
last_seen: Utc.timestamp(1_573_680_222, 0),
|
|
||||||
last_connection_state: Responded,
|
|
||||||
}
|
|
||||||
.sanitize();
|
|
||||||
|
|
||||||
// We want the sanitized timestamp to be a multiple of the truncation interval.
|
|
||||||
assert_eq!(
|
|
||||||
entry.get_last_seen().timestamp() % crate::constants::TIMESTAMP_TRUNCATION_SECONDS,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
// We want the state to be the default
|
|
||||||
assert_eq!(entry.last_connection_state, Default::default());
|
|
||||||
// We want the other fields to be unmodified
|
|
||||||
assert_eq!(entry.addr, addr);
|
|
||||||
assert_eq!(entry.services, services);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
mod preallocate;
|
||||||
|
mod prop;
|
|
@ -0,0 +1,61 @@
|
||||||
|
//! Tests for trusted preallocation during deserialization.
|
||||||
|
|
||||||
|
use super::super::{MetaAddr, PeerAddrState, PeerServices, META_ADDR_SIZE};
|
||||||
|
|
||||||
|
use zebra_chain::serialization::{TrustedPreallocate, ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN};
|
||||||
|
|
||||||
|
use chrono::{TimeZone, Utc};
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
/// Confirm that each MetaAddr takes exactly META_ADDR_SIZE bytes when serialized.
|
||||||
|
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
||||||
|
#[test]
|
||||||
|
fn meta_addr_size_is_correct() {
|
||||||
|
let addr = MetaAddr {
|
||||||
|
addr: ([192, 168, 0, 0], 8333).into(),
|
||||||
|
services: PeerServices::default(),
|
||||||
|
last_seen: Utc.timestamp(1_573_680_222, 0),
|
||||||
|
last_connection_state: PeerAddrState::Responded,
|
||||||
|
};
|
||||||
|
let serialized = addr
|
||||||
|
.zcash_serialize_to_vec()
|
||||||
|
.expect("Serialization to vec must succeed");
|
||||||
|
assert!(serialized.len() == META_ADDR_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies that...
|
||||||
|
/// 1. The smallest disallowed vector of `MetaAddrs`s is too large to fit in a legal Zcash message
|
||||||
|
/// 2. The largest allowed vector is small enough to fit in a legal Zcash message
|
||||||
|
#[test]
|
||||||
|
fn meta_addr_max_allocation_is_correct() {
|
||||||
|
let addr = MetaAddr {
|
||||||
|
addr: ([192, 168, 0, 0], 8333).into(),
|
||||||
|
services: PeerServices::default(),
|
||||||
|
last_seen: Utc.timestamp(1_573_680_222, 0),
|
||||||
|
last_connection_state: PeerAddrState::Responded,
|
||||||
|
};
|
||||||
|
let max_allocation: usize = MetaAddr::max_allocation().try_into().unwrap();
|
||||||
|
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
|
||||||
|
for _ in 0..(MetaAddr::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) == MetaAddr::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);
|
||||||
|
|
||||||
|
// 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 MetaAddrs
|
||||||
|
assert!((largest_allowed_vec.len() as u64) == MetaAddr::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);
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
use super::super::*;
|
||||||
|
|
||||||
|
// XXX remove this test and replace it with a proptest instance.
|
||||||
|
#[test]
|
||||||
|
fn sanitize_truncates_timestamps() {
|
||||||
|
zebra_test::init();
|
||||||
|
|
||||||
|
let services = PeerServices::default();
|
||||||
|
let addr = "127.0.0.1:8233".parse().unwrap();
|
||||||
|
|
||||||
|
let entry = MetaAddr {
|
||||||
|
services,
|
||||||
|
addr,
|
||||||
|
last_seen: Utc.timestamp(1_573_680_222, 0),
|
||||||
|
last_connection_state: Responded,
|
||||||
|
}
|
||||||
|
.sanitize();
|
||||||
|
|
||||||
|
// We want the sanitized timestamp to be a multiple of the truncation interval.
|
||||||
|
assert_eq!(
|
||||||
|
entry.get_last_seen().timestamp() % crate::constants::TIMESTAMP_TRUNCATION_SECONDS,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
// We want the state to be the default
|
||||||
|
assert_eq!(entry.last_connection_state, Default::default());
|
||||||
|
// We want the other fields to be unmodified
|
||||||
|
assert_eq!(entry.addr, addr);
|
||||||
|
assert_eq!(entry.services, services);
|
||||||
|
}
|
|
@ -7,6 +7,9 @@ mod message;
|
||||||
/// Newtype wrappers for primitive types.
|
/// Newtype wrappers for primitive types.
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
pub use codec::{Codec, MAX_PROTOCOL_MESSAGE_LEN};
|
pub use codec::{Codec, MAX_PROTOCOL_MESSAGE_LEN};
|
||||||
pub use inv::InventoryHash;
|
pub use inv::InventoryHash;
|
||||||
pub use message::Message;
|
pub use message::Message;
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
//! Inventory items for the Bitcoin protocol.
|
//! Inventory items for the Bitcoin protocol.
|
||||||
|
|
||||||
// XXX the exact optimal arrangement of all of these parts is a little unclear
|
|
||||||
// until we have more pieces in place the optimal global arrangement of items is
|
|
||||||
// a little unclear.
|
|
||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
@ -86,7 +82,9 @@ impl ZcashDeserialize for InventoryHash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const INV_HASH_SIZE: usize = 36;
|
/// The serialized size of an [`InventoryHash`].
|
||||||
|
pub(crate) const INV_HASH_SIZE: usize = 36;
|
||||||
|
|
||||||
impl TrustedPreallocate for InventoryHash {
|
impl TrustedPreallocate for InventoryHash {
|
||||||
fn max_allocation() -> u64 {
|
fn max_allocation() -> u64 {
|
||||||
// An Inventory hash takes 36 bytes, and we reserve at least one byte for the Vector length
|
// An Inventory hash takes 36 bytes, and we reserve at least one byte for the Vector length
|
||||||
|
@ -94,76 +92,3 @@ impl TrustedPreallocate for InventoryHash {
|
||||||
((MAX_PROTOCOL_MESSAGE_LEN - 1) / INV_HASH_SIZE) as u64
|
((MAX_PROTOCOL_MESSAGE_LEN - 1) / INV_HASH_SIZE) as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_trusted_preallocate {
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
use super::{InventoryHash, INV_HASH_SIZE, MAX_PROTOCOL_MESSAGE_LEN};
|
|
||||||
use zebra_chain::{
|
|
||||||
block,
|
|
||||||
serialization::{TrustedPreallocate, ZcashSerialize},
|
|
||||||
transaction,
|
|
||||||
};
|
|
||||||
#[test]
|
|
||||||
/// Confirm that each InventoryHash takes exactly INV_HASH_SIZE bytes when serialized.
|
|
||||||
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
|
||||||
fn inv_hash_size_is_correct() {
|
|
||||||
let block_hash = block::Hash([1u8; 32]);
|
|
||||||
let tx_hash = transaction::Hash([1u8; 32]);
|
|
||||||
let inv_block = InventoryHash::Block(block_hash);
|
|
||||||
let serialized_inv_block = inv_block
|
|
||||||
.zcash_serialize_to_vec()
|
|
||||||
.expect("Serialization to vec must succeed");
|
|
||||||
assert!(serialized_inv_block.len() == INV_HASH_SIZE);
|
|
||||||
|
|
||||||
let inv_filtered_block = InventoryHash::FilteredBlock(block_hash);
|
|
||||||
let serialized_inv_filtered = inv_filtered_block
|
|
||||||
.zcash_serialize_to_vec()
|
|
||||||
.expect("Serialization to vec must succeed");
|
|
||||||
assert!(serialized_inv_filtered.len() == INV_HASH_SIZE);
|
|
||||||
|
|
||||||
let inv_tx = InventoryHash::Tx(tx_hash);
|
|
||||||
let serialized_inv_tx = inv_tx
|
|
||||||
.zcash_serialize_to_vec()
|
|
||||||
.expect("Serialization to vec must succeed");
|
|
||||||
assert!(serialized_inv_tx.len() == INV_HASH_SIZE);
|
|
||||||
|
|
||||||
let inv_err = InventoryHash::Error;
|
|
||||||
let serializd_inv_err = inv_err
|
|
||||||
.zcash_serialize_to_vec()
|
|
||||||
.expect("Serialization to vec must succeed");
|
|
||||||
assert!(serializd_inv_err.len() == INV_HASH_SIZE)
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
/// Verifies that...
|
|
||||||
/// 1. The smallest disallowed vector of `InventoryHash`s is too large to fit in a legal Zcash message
|
|
||||||
/// 2. The largest allowed vector is small enough to fit in a legal Zcash message
|
|
||||||
fn meta_addr_max_allocation_is_correct() {
|
|
||||||
let inv = InventoryHash::Error;
|
|
||||||
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 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());
|
|
||||||
// Check that our smallest_disallowed_vec is too big to fit in a Zcash message.
|
|
||||||
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");
|
|
||||||
|
|
||||||
// Check that our largest_allowed_vec contains the maximum number of InventoryHashes
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
mod preallocate;
|
|
@ -0,0 +1,74 @@
|
||||||
|
//! Tests for trusted preallocation during deserialization.
|
||||||
|
|
||||||
|
use super::super::inv::{InventoryHash, INV_HASH_SIZE};
|
||||||
|
|
||||||
|
use zebra_chain::{
|
||||||
|
block,
|
||||||
|
serialization::{TrustedPreallocate, ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN},
|
||||||
|
transaction,
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
/// Confirm that each InventoryHash takes exactly INV_HASH_SIZE bytes when serialized.
|
||||||
|
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
||||||
|
#[test]
|
||||||
|
fn inv_hash_size_is_correct() {
|
||||||
|
let block_hash = block::Hash([1u8; 32]);
|
||||||
|
let tx_hash = transaction::Hash([1u8; 32]);
|
||||||
|
let inv_block = InventoryHash::Block(block_hash);
|
||||||
|
let serialized_inv_block = inv_block
|
||||||
|
.zcash_serialize_to_vec()
|
||||||
|
.expect("Serialization to vec must succeed");
|
||||||
|
assert!(serialized_inv_block.len() == INV_HASH_SIZE);
|
||||||
|
|
||||||
|
let inv_filtered_block = InventoryHash::FilteredBlock(block_hash);
|
||||||
|
let serialized_inv_filtered = inv_filtered_block
|
||||||
|
.zcash_serialize_to_vec()
|
||||||
|
.expect("Serialization to vec must succeed");
|
||||||
|
assert!(serialized_inv_filtered.len() == INV_HASH_SIZE);
|
||||||
|
|
||||||
|
let inv_tx = InventoryHash::Tx(tx_hash);
|
||||||
|
let serialized_inv_tx = inv_tx
|
||||||
|
.zcash_serialize_to_vec()
|
||||||
|
.expect("Serialization to vec must succeed");
|
||||||
|
assert!(serialized_inv_tx.len() == INV_HASH_SIZE);
|
||||||
|
|
||||||
|
let inv_err = InventoryHash::Error;
|
||||||
|
let serializd_inv_err = inv_err
|
||||||
|
.zcash_serialize_to_vec()
|
||||||
|
.expect("Serialization to vec must succeed");
|
||||||
|
assert!(serializd_inv_err.len() == INV_HASH_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies that...
|
||||||
|
/// 1. The smallest disallowed vector of `InventoryHash`s is too large to fit in a legal Zcash message
|
||||||
|
/// 2. The largest allowed vector is small enough to fit in a legal Zcash message
|
||||||
|
#[test]
|
||||||
|
fn inv_hash_max_allocation_is_correct() {
|
||||||
|
let inv = InventoryHash::Error;
|
||||||
|
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 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());
|
||||||
|
// Check that our smallest_disallowed_vec is too big to fit in a Zcash message.
|
||||||
|
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");
|
||||||
|
|
||||||
|
// Check that our largest_allowed_vec contains the maximum number of InventoryHashes
|
||||||
|
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);
|
||||||
|
}
|
Loading…
Reference in New Issue