scan(test): Implement scanner format round-trip tests (#8071)

* Implement scanner format round-trip tests

* Limit random heights to database range

* Increase coverage of state height serialization proptests #7443
This commit is contained in:
teor 2023-12-08 05:43:49 +10:00 committed by GitHub
parent 6306a755de
commit 1ccf5fba46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 14 deletions

View File

@ -23,6 +23,7 @@ pub mod json_conversion;
/// There are multiple formats for serializing a height, so we don't implement
/// `ZcashSerialize` or `ZcashDeserialize` for `Height`.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Default))]
pub struct Height(pub u32);
#[derive(Error, Debug)]

View File

@ -65,7 +65,7 @@ pub const TRANSACTION_LOCATION_DISK_BYTES: usize = HEIGHT_DISK_BYTES + TX_INDEX_
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(Arbitrary, Serialize, Deserialize)
derive(Arbitrary, Default, Serialize, Deserialize)
)]
pub struct TransactionIndex(pub(super) u16);
@ -126,7 +126,7 @@ impl TransactionIndex {
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(Arbitrary, Serialize, Deserialize)
derive(Arbitrary, Default, Serialize, Deserialize)
)]
pub struct TransactionLocation {
/// The block height of the transaction.

View File

@ -13,6 +13,12 @@ use crate::{FromDisk, IntoDisk, TransactionLocation};
use super::block::TRANSACTION_LOCATION_DISK_BYTES;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[cfg(test)]
mod tests;
/// The type used in Zebra to store Sapling scanning keys.
/// It can represent a full viewing key or an individual viewing key.
pub type SaplingScanningKey = String;
@ -22,6 +28,7 @@ pub type SaplingScanningKey = String;
/// Currently contains a TXID in "display order", which is big-endian byte order following the u256
/// convention set by Bitcoin and zcashd.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Default))]
pub struct SaplingScannedResult([u8; 32]);
impl From<SaplingScannedResult> for transaction::Hash {
@ -38,6 +45,7 @@ impl From<&[u8; 32]> for SaplingScannedResult {
/// A database column family entry for a block scanned with a Sapling vieweing key.
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Default))]
pub struct SaplingScannedDatabaseEntry {
/// The database column family key. Must be unique for each scanning key and scanned block.
pub index: SaplingScannedDatabaseIndex,
@ -48,6 +56,7 @@ pub struct SaplingScannedDatabaseEntry {
/// A database column family key for a block scanned with a Sapling vieweing key.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Default))]
pub struct SaplingScannedDatabaseIndex {
/// The Sapling viewing key used to scan the block.
pub sapling_key: SaplingScanningKey,

View File

@ -0,0 +1,3 @@
//! Tests for scanner database serialization.
mod prop;

View File

@ -0,0 +1,44 @@
//! Randomised proptests for scanner database formats.
use proptest::{arbitrary::any, prelude::*};
use crate::{
service::finalized_state::arbitrary::assert_value_properties, SaplingScannedDatabaseIndex,
SaplingScannedResult, SaplingScanningKey, MAX_ON_DISK_HEIGHT,
};
#[test]
fn roundtrip_sapling_scanning_key() {
let _init_guard = zebra_test::init();
proptest!(|(val in any::<SaplingScanningKey>())| assert_value_properties(val));
}
#[test]
fn roundtrip_sapling_db_index() {
let _init_guard = zebra_test::init();
proptest!(
|(mut val in any::<SaplingScannedDatabaseIndex>())| {
// Limit the random height to the valid on-disk range.
// Blocks outside this range are rejected before they reach the state.
// (It would take decades to generate a valid chain this high.)
val.tx_loc.height.0 %= MAX_ON_DISK_HEIGHT.0 + 1;
assert_value_properties(val)
}
);
}
#[test]
fn roundtrip_sapling_result() {
let _init_guard = zebra_test::init();
proptest!(|(val in any::<SaplingScannedResult>())| assert_value_properties(val));
}
#[test]
fn roundtrip_option_sapling_result() {
let _init_guard = zebra_test::init();
proptest!(|(val in any::<Option<SaplingScannedResult>>())| assert_value_properties(val));
}

View File

@ -26,12 +26,15 @@ use crate::service::finalized_state::{
// Common
// TODO: turn this into a unit test, it has a fixed value
/// This test has a fixed value, so testing it once is sufficient.
#[test]
fn roundtrip_unit_type() {
let _init_guard = zebra_test::init();
proptest!(|(val in any::<()>())| assert_value_properties(val));
// The unit type `()` is serialized to the empty (zero-length) array `[]`.
#[allow(clippy::let_unit_value)]
let value = ();
assert_value_properties(value);
}
// Block
@ -46,7 +49,7 @@ fn roundtrip_block_height() {
// Limit the random height to the valid on-disk range.
// Blocks outside this range are rejected before they reach the state.
// (It would take decades to generate a valid chain this high.)
val = val.clamp(Height(0), MAX_ON_DISK_HEIGHT);
val.0 %= MAX_ON_DISK_HEIGHT.0 + 1;
assert_value_properties(val)
}
);
@ -74,7 +77,7 @@ fn roundtrip_transaction_location() {
proptest!(
|(mut val in any::<TransactionLocation>())| {
val.height = val.height.clamp(Height(0), MAX_ON_DISK_HEIGHT);
val.height.0 %= MAX_ON_DISK_HEIGHT.0 + 1;
assert_value_properties(val)
}
);
@ -142,7 +145,7 @@ fn roundtrip_output_location() {
proptest!(
|(mut val in any::<OutputLocation>())| {
*val.height_mut() = val.height().clamp(Height(0), MAX_ON_DISK_HEIGHT);
val.height_mut().0 %= MAX_ON_DISK_HEIGHT.0 + 1;
assert_value_properties(val)
}
);
@ -154,7 +157,7 @@ fn roundtrip_address_location() {
proptest!(
|(mut val in any::<AddressLocation>())| {
*val.height_mut() = val.height().clamp(Height(0), MAX_ON_DISK_HEIGHT);
val.height_mut().0 %= MAX_ON_DISK_HEIGHT.0 + 1;
assert_value_properties(val)
}
);
@ -166,7 +169,7 @@ fn roundtrip_address_balance_location() {
proptest!(
|(mut val in any::<AddressBalanceLocation>())| {
*val.height_mut() = val.address_location().height().clamp(Height(0), MAX_ON_DISK_HEIGHT);
val.height_mut().0 %= MAX_ON_DISK_HEIGHT.0 + 1;
assert_value_properties(val)
}
);
@ -185,8 +188,8 @@ fn roundtrip_address_unspent_output() {
proptest!(
|(mut val in any::<AddressUnspentOutput>())| {
*val.address_location_mut().height_mut() = val.address_location().height().clamp(Height(0), MAX_ON_DISK_HEIGHT);
*val.unspent_output_location_mut().height_mut() = val.unspent_output_location().height().clamp(Height(0), MAX_ON_DISK_HEIGHT);
val.address_location_mut().height_mut().0 %= MAX_ON_DISK_HEIGHT.0 + 1;
val.unspent_output_location_mut().height_mut().0 %= MAX_ON_DISK_HEIGHT.0 + 1;
assert_value_properties(val)
}
@ -199,8 +202,8 @@ fn roundtrip_address_transaction() {
proptest!(
|(mut val in any::<AddressTransaction>())| {
*val.address_location_mut().height_mut() = val.address_location().height().clamp(Height(0), MAX_ON_DISK_HEIGHT);
val.transaction_location_mut().height = val.transaction_location().height.clamp(Height(0), MAX_ON_DISK_HEIGHT);
val.address_location_mut().height_mut().0 %= MAX_ON_DISK_HEIGHT.0 + 1;
val.transaction_location_mut().height.0 %= MAX_ON_DISK_HEIGHT.0 + 1;
assert_value_properties(val)
}
@ -461,7 +464,6 @@ fn roundtrip_orchard_subtree_data() {
let _init_guard = zebra_test::init();
proptest!(|(mut val in any::<NoteCommitmentSubtreeData<orchard::tree::Node>>())| {
val.end_height = val.end_height.clamp(Height(0), MAX_ON_DISK_HEIGHT);
val.end_height.0 %= MAX_ON_DISK_HEIGHT.0 + 1;
assert_value_properties(val)
});