//! Sapling prop tests. use proptest::prelude::*; use crate::{ block, sapling::{self, OutputInTransactionV4, PerSpendAnchor, SharedAnchor}, serialization::{ZcashDeserializeInto, ZcashSerialize}, transaction::{LockTime, Transaction}, }; use std::convert::TryInto; proptest! { /// Serialize and deserialize `Spend` #[test] fn spend_v4_roundtrip( spend in any::>(), ) { let _init_guard = zebra_test::init(); let data = spend.zcash_serialize_to_vec().expect("spend should serialize"); let spend_parsed = data.zcash_deserialize_into().expect("randomized spend should deserialize"); prop_assert_eq![&spend, &spend_parsed]; let data2 = spend_parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; } /// Serialize and deserialize `Spend` #[test] fn spend_v5_roundtrip( spend in any::>(), ) { let _init_guard = zebra_test::init(); let (prefix, zkproof, spend_auth_sig) = spend.into_v5_parts(); let data = prefix.zcash_serialize_to_vec().expect("spend prefix should serialize"); let parsed = data.zcash_deserialize_into().expect("randomized spend prefix should deserialize"); prop_assert_eq![&prefix, &parsed]; let data2 = parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; let data = zkproof.zcash_serialize_to_vec().expect("spend zkproof should serialize"); let parsed = data.zcash_deserialize_into().expect("randomized spend zkproof should deserialize"); prop_assert_eq![&zkproof, &parsed]; let data2 = parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; let data = spend_auth_sig.zcash_serialize_to_vec().expect("spend auth sig should serialize"); let parsed = data.zcash_deserialize_into().expect("randomized spend auth sig should deserialize"); prop_assert_eq![&spend_auth_sig, &parsed]; let data2 = parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; } /// Serialize and deserialize `Output` #[test] fn output_roundtrip( output in any::(), ) { let _init_guard = zebra_test::init(); // v4 format let data = output.clone().into_v4().zcash_serialize_to_vec().expect("output should serialize"); let output_parsed = data.zcash_deserialize_into::().expect("randomized output should deserialize").into_output(); prop_assert_eq![&output, &output_parsed]; let data2 = output_parsed .into_v4() .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; // v5 format let (prefix, zkproof) = output.into_v5_parts(); let data = prefix.zcash_serialize_to_vec().expect("output prefix should serialize"); let parsed = data.zcash_deserialize_into().expect("randomized output prefix should deserialize"); prop_assert_eq![&prefix, &parsed]; let data2 = parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; let data = zkproof.zcash_serialize_to_vec().expect("output zkproof should serialize"); let parsed = data.zcash_deserialize_into().expect("randomized output zkproof should deserialize"); prop_assert_eq![&zkproof, &parsed]; let data2 = parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; } } proptest! { /// Serialize and deserialize `PerSpendAnchor` shielded data by including it /// in a V4 transaction #[test] fn sapling_shielded_data_v4_roundtrip( shielded_v4 in any::>(), ) { let _init_guard = zebra_test::init(); // shielded data doesn't serialize by itself, so we have to stick it in // a transaction // stick `PerSpendAnchor` shielded data into a v4 transaction let tx = Transaction::V4 { inputs: Vec::new(), outputs: Vec::new(), lock_time: LockTime::min_lock_time_timestamp(), expiry_height: block::Height(0), joinsplit_data: None, sapling_shielded_data: Some(shielded_v4), }; let data = tx.zcash_serialize_to_vec().expect("tx should serialize"); let tx_parsed = data.zcash_deserialize_into().expect("randomized tx should deserialize"); prop_assert_eq![&tx, &tx_parsed]; let data2 = tx_parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; } /// Serialize and deserialize `SharedAnchor` shielded data #[test] fn sapling_shielded_data_v5_roundtrip( shielded_v5 in any::>(), ) { let _init_guard = zebra_test::init(); let data = shielded_v5.zcash_serialize_to_vec().expect("shielded_v5 should serialize"); let shielded_v5_parsed = data.zcash_deserialize_into().expect("randomized shielded_v5 should deserialize"); if let Some(shielded_v5_parsed) = shielded_v5_parsed { prop_assert_eq![&shielded_v5, &shielded_v5_parsed]; let data2 = shielded_v5_parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; } else { panic!("unexpected parsing error: ShieldedData should be Some(_)"); } } /// Test v4 with empty spends, but some outputs #[test] fn sapling_shielded_data_v4_outputs_only( shielded_v4 in any::>(), ) { let _init_guard = zebra_test::init(); // we need at least one output to delete all the spends prop_assume!(shielded_v4.outputs().count() > 0); // TODO: modify the strategy, rather than the shielded data let mut shielded_v4 = shielded_v4; let outputs: Vec<_> = shielded_v4.outputs().cloned().collect(); shielded_v4.transfers = sapling::TransferData::JustOutputs { outputs: outputs.try_into().unwrap(), }; // shielded data doesn't serialize by itself, so we have to stick it in // a transaction // stick `PerSpendAnchor` shielded data into a v4 transaction let tx = Transaction::V4 { inputs: Vec::new(), outputs: Vec::new(), lock_time: LockTime::min_lock_time_timestamp(), expiry_height: block::Height(0), joinsplit_data: None, sapling_shielded_data: Some(shielded_v4), }; let data = tx.zcash_serialize_to_vec().expect("tx should serialize"); let tx_parsed = data.zcash_deserialize_into().expect("randomized tx should deserialize"); prop_assert_eq![&tx, &tx_parsed]; let data2 = tx_parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; } /// Test the v5 shared anchor serialization condition: empty spends, but some outputs #[test] fn sapling_shielded_data_v5_outputs_only( shielded_v5 in any::>(), ) { let _init_guard = zebra_test::init(); // we need at least one output to delete all the spends prop_assume!(shielded_v5.outputs().count() > 0); // TODO: modify the strategy, rather than the shielded data let mut shielded_v5 = shielded_v5; let outputs: Vec<_> = shielded_v5.outputs().cloned().collect(); shielded_v5.transfers = sapling::TransferData::JustOutputs { outputs: outputs.try_into().unwrap(), }; let data = shielded_v5.zcash_serialize_to_vec().expect("shielded_v5 should serialize"); let shielded_v5_parsed = data.zcash_deserialize_into().expect("randomized shielded_v5 should deserialize"); if let Some(shielded_v5_parsed) = shielded_v5_parsed { prop_assert_eq![&shielded_v5, &shielded_v5_parsed]; let data2 = shielded_v5_parsed .zcash_serialize_to_vec() .expect("vec serialization is infallible"); prop_assert_eq![data, data2, "data must be equal if structs are equal"]; } else { panic!("unexpected parsing error: ShieldedData should be Some(_)"); } } }