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:
parent
6306a755de
commit
1ccf5fba46
|
@ -23,6 +23,7 @@ pub mod json_conversion;
|
||||||
/// There are multiple formats for serializing a height, so we don't implement
|
/// There are multiple formats for serializing a height, so we don't implement
|
||||||
/// `ZcashSerialize` or `ZcashDeserialize` for `Height`.
|
/// `ZcashSerialize` or `ZcashDeserialize` for `Height`.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[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);
|
pub struct Height(pub u32);
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
|
@ -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)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
any(test, feature = "proptest-impl"),
|
any(test, feature = "proptest-impl"),
|
||||||
derive(Arbitrary, Serialize, Deserialize)
|
derive(Arbitrary, Default, Serialize, Deserialize)
|
||||||
)]
|
)]
|
||||||
pub struct TransactionIndex(pub(super) u16);
|
pub struct TransactionIndex(pub(super) u16);
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ impl TransactionIndex {
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
any(test, feature = "proptest-impl"),
|
any(test, feature = "proptest-impl"),
|
||||||
derive(Arbitrary, Serialize, Deserialize)
|
derive(Arbitrary, Default, Serialize, Deserialize)
|
||||||
)]
|
)]
|
||||||
pub struct TransactionLocation {
|
pub struct TransactionLocation {
|
||||||
/// The block height of the transaction.
|
/// The block height of the transaction.
|
||||||
|
|
|
@ -13,6 +13,12 @@ use crate::{FromDisk, IntoDisk, TransactionLocation};
|
||||||
|
|
||||||
use super::block::TRANSACTION_LOCATION_DISK_BYTES;
|
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.
|
/// The type used in Zebra to store Sapling scanning keys.
|
||||||
/// It can represent a full viewing key or an individual viewing key.
|
/// It can represent a full viewing key or an individual viewing key.
|
||||||
pub type SaplingScanningKey = String;
|
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
|
/// Currently contains a TXID in "display order", which is big-endian byte order following the u256
|
||||||
/// convention set by Bitcoin and zcashd.
|
/// convention set by Bitcoin and zcashd.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Default))]
|
||||||
pub struct SaplingScannedResult([u8; 32]);
|
pub struct SaplingScannedResult([u8; 32]);
|
||||||
|
|
||||||
impl From<SaplingScannedResult> for transaction::Hash {
|
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.
|
/// A database column family entry for a block scanned with a Sapling vieweing key.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Default))]
|
||||||
pub struct SaplingScannedDatabaseEntry {
|
pub struct SaplingScannedDatabaseEntry {
|
||||||
/// The database column family key. Must be unique for each scanning key and scanned block.
|
/// The database column family key. Must be unique for each scanning key and scanned block.
|
||||||
pub index: SaplingScannedDatabaseIndex,
|
pub index: SaplingScannedDatabaseIndex,
|
||||||
|
@ -48,6 +56,7 @@ pub struct SaplingScannedDatabaseEntry {
|
||||||
|
|
||||||
/// A database column family key for a block scanned with a Sapling vieweing key.
|
/// A database column family key for a block scanned with a Sapling vieweing key.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Default))]
|
||||||
pub struct SaplingScannedDatabaseIndex {
|
pub struct SaplingScannedDatabaseIndex {
|
||||||
/// The Sapling viewing key used to scan the block.
|
/// The Sapling viewing key used to scan the block.
|
||||||
pub sapling_key: SaplingScanningKey,
|
pub sapling_key: SaplingScanningKey,
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
//! Tests for scanner database serialization.
|
||||||
|
|
||||||
|
mod prop;
|
|
@ -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));
|
||||||
|
}
|
|
@ -26,12 +26,15 @@ use crate::service::finalized_state::{
|
||||||
|
|
||||||
// Common
|
// 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]
|
#[test]
|
||||||
fn roundtrip_unit_type() {
|
fn roundtrip_unit_type() {
|
||||||
let _init_guard = zebra_test::init();
|
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
|
// Block
|
||||||
|
@ -46,7 +49,7 @@ fn roundtrip_block_height() {
|
||||||
// Limit the random height to the valid on-disk range.
|
// Limit the random height to the valid on-disk range.
|
||||||
// Blocks outside this range are rejected before they reach the state.
|
// Blocks outside this range are rejected before they reach the state.
|
||||||
// (It would take decades to generate a valid chain this high.)
|
// (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)
|
assert_value_properties(val)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -74,7 +77,7 @@ fn roundtrip_transaction_location() {
|
||||||
|
|
||||||
proptest!(
|
proptest!(
|
||||||
|(mut val in any::<TransactionLocation>())| {
|
|(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)
|
assert_value_properties(val)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -142,7 +145,7 @@ fn roundtrip_output_location() {
|
||||||
|
|
||||||
proptest!(
|
proptest!(
|
||||||
|(mut val in any::<OutputLocation>())| {
|
|(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)
|
assert_value_properties(val)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -154,7 +157,7 @@ fn roundtrip_address_location() {
|
||||||
|
|
||||||
proptest!(
|
proptest!(
|
||||||
|(mut val in any::<AddressLocation>())| {
|
|(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)
|
assert_value_properties(val)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -166,7 +169,7 @@ fn roundtrip_address_balance_location() {
|
||||||
|
|
||||||
proptest!(
|
proptest!(
|
||||||
|(mut val in any::<AddressBalanceLocation>())| {
|
|(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)
|
assert_value_properties(val)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -185,8 +188,8 @@ fn roundtrip_address_unspent_output() {
|
||||||
|
|
||||||
proptest!(
|
proptest!(
|
||||||
|(mut val in any::<AddressUnspentOutput>())| {
|
|(mut val in any::<AddressUnspentOutput>())| {
|
||||||
*val.address_location_mut().height_mut() = val.address_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() = val.unspent_output_location().height().clamp(Height(0), MAX_ON_DISK_HEIGHT);
|
val.unspent_output_location_mut().height_mut().0 %= MAX_ON_DISK_HEIGHT.0 + 1;
|
||||||
|
|
||||||
assert_value_properties(val)
|
assert_value_properties(val)
|
||||||
}
|
}
|
||||||
|
@ -199,8 +202,8 @@ fn roundtrip_address_transaction() {
|
||||||
|
|
||||||
proptest!(
|
proptest!(
|
||||||
|(mut val in any::<AddressTransaction>())| {
|
|(mut val in any::<AddressTransaction>())| {
|
||||||
*val.address_location_mut().height_mut() = val.address_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 = val.transaction_location().height.clamp(Height(0), MAX_ON_DISK_HEIGHT);
|
val.transaction_location_mut().height.0 %= MAX_ON_DISK_HEIGHT.0 + 1;
|
||||||
|
|
||||||
assert_value_properties(val)
|
assert_value_properties(val)
|
||||||
}
|
}
|
||||||
|
@ -461,7 +464,6 @@ fn roundtrip_orchard_subtree_data() {
|
||||||
let _init_guard = zebra_test::init();
|
let _init_guard = zebra_test::init();
|
||||||
|
|
||||||
proptest!(|(mut val in any::<NoteCommitmentSubtreeData<orchard::tree::Node>>())| {
|
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;
|
val.end_height.0 %= MAX_ON_DISK_HEIGHT.0 + 1;
|
||||||
assert_value_properties(val)
|
assert_value_properties(val)
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue