diff --git a/components/zcash_address/src/kind/unified.rs b/components/zcash_address/src/kind/unified.rs index c715bc1c7..4173eff31 100644 --- a/components/zcash_address/src/kind/unified.rs +++ b/components/zcash_address/src/kind/unified.rs @@ -209,7 +209,8 @@ pub(crate) mod private { padding[0..hrp.len()].copy_from_slice(hrp.as_bytes()); writer.write_all(&padding).unwrap(); - f4jumble::f4jumble(&writer.into_inner()).unwrap() + let padded = writer.into_inner(); + f4jumble::f4jumble(&padded).unwrap_or_else(|| panic!("f4jumble failed on {:?}", padded)) } /// Parse the items of the unified container. diff --git a/components/zcash_address/src/kind/unified/address.rs b/components/zcash_address/src/kind/unified/address.rs index 80e9d131a..c0d5f3c63 100644 --- a/components/zcash_address/src/kind/unified/address.rs +++ b/components/zcash_address/src/kind/unified/address.rs @@ -113,6 +113,7 @@ pub(crate) mod test_vectors; #[cfg(test)] mod tests { use assert_matches::assert_matches; + use zcash_encoding::MAX_COMPACT_SIZE; use crate::{ kind::unified::{private::SealedContainer, Container, Encoding}, @@ -121,6 +122,7 @@ mod tests { use proptest::{ array::{uniform11, uniform20, uniform32}, + collection::vec, prelude::*, sample::select, }; @@ -136,35 +138,52 @@ mod tests { } } - fn arb_shielded_receiver() -> BoxedStrategy> { - prop_oneof![ - vec![uniform43().prop_map(Receiver::Sapling)], - vec![uniform43().prop_map(Receiver::Orchard)], - vec![ - uniform43().prop_map(Receiver::Orchard as fn([u8; 43]) -> Receiver), - uniform43().prop_map(Receiver::Sapling) - ], - ] - .boxed() + fn arb_transparent_typecode() -> impl Strategy { + select(vec![Typecode::P2pkh, Typecode::P2sh]) } - fn arb_transparent_receiver() -> BoxedStrategy { + fn arb_shielded_typecode() -> impl Strategy { prop_oneof![ - uniform20(0u8..).prop_map(Receiver::P2pkh), - uniform20(0u8..).prop_map(Receiver::P2sh), + Just(Typecode::Sapling), + Just(Typecode::Orchard), + (0u32..MAX_COMPACT_SIZE).prop_map(Typecode::Unknown) ] - .boxed() } - prop_compose! { - fn arb_unified_address()( - shielded in arb_shielded_receiver(), - transparent in prop::option::of(arb_transparent_receiver()), - )( - shuffled in Just(shielded.into_iter().chain(transparent).collect()).prop_shuffle() - ) -> Address { - Address(shuffled) - } + /// A strategy to generate an arbitrary valid set of typecodes without + /// duplication and containing only one of P2sh and P2pkh transparent + /// typecodes. + fn arb_typecodes() -> impl Strategy> { + prop::option::of(arb_transparent_typecode()) + .prop_flat_map(|transparent| { + prop::collection::hash_set(arb_shielded_typecode(), 1..4) + .prop_map(move |xs| xs.into_iter().chain(transparent).collect()) + .boxed() + }) + .prop_shuffle() + } + + fn arb_unified_address_for_typecodes( + typecodes: Vec, + ) -> impl Strategy> { + typecodes + .into_iter() + .map(|tc| match tc { + Typecode::P2pkh => uniform20(0u8..).prop_map(Receiver::P2pkh).boxed(), + Typecode::P2sh => uniform20(0u8..).prop_map(Receiver::P2sh).boxed(), + Typecode::Sapling => uniform43().prop_map(Receiver::Sapling).boxed(), + Typecode::Orchard => uniform43().prop_map(Receiver::Orchard).boxed(), + Typecode::Unknown(typecode) => vec(any::(), 32..256) + .prop_map(move |data| Receiver::Unknown { typecode, data }) + .boxed(), + }) + .collect::>() + } + + fn arb_unified_address() -> impl Strategy { + arb_typecodes() + .prop_flat_map(arb_unified_address_for_typecodes) + .prop_map(Address) } proptest! { diff --git a/components/zcash_encoding/src/lib.rs b/components/zcash_encoding/src/lib.rs index 99b8ede53..2aac48900 100644 --- a/components/zcash_encoding/src/lib.rs +++ b/components/zcash_encoding/src/lib.rs @@ -13,7 +13,8 @@ use nonempty::NonEmpty; use std::convert::TryFrom; use std::io::{self, Read, Write}; -const MAX_SIZE: u64 = 0x02000000; +/// The maximum allowed value representable as a `[CompactSize]` +pub const MAX_COMPACT_SIZE: u32 = 0x02000000; /// Namespace for functions for compact encoding of integers. /// @@ -54,7 +55,7 @@ impl CompactSize { }?; match result { - s if s > MAX_SIZE => Err(io::Error::new( + s if s > (MAX_COMPACT_SIZE as u64) => Err(io::Error::new( io::ErrorKind::InvalidInput, "CompactSize too large", )),