Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add(tests): Add snapshot tests for sprout database formats #7057

Merged
merged 9 commits into from
Jun 27, 2023
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5881,6 +5881,7 @@ dependencies = [
"once_cell",
"proptest",
"proptest-derive",
"rand 0.8.5",
"rayon",
"regex",
"rlimit",
Expand Down
2 changes: 1 addition & 1 deletion tower-batch-control/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ color-eyre = "0.6.2"
tinyvec = { version = "1.6.0", features = ["rustc_1_55"] }

ed25519-zebra = "4.0.0"
rand = { version = "0.8.5", package = "rand" }
rand = "0.8.5"

tokio = { version = "1.28.2", features = ["full", "tracing", "test-util"] }
tokio-test = "0.4.2"
Expand Down
4 changes: 2 additions & 2 deletions zebra-chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ zcash_address = { version = "0.2.1", optional = true }
proptest = { version = "1.2.0", optional = true }
proptest-derive = { version = "0.3.0", optional = true }

rand = { version = "0.8.5", optional = true, package = "rand" }
rand = { version = "0.8.5", optional = true }
rand_chacha = { version = "0.3.1", optional = true }

tokio = { version = "1.28.2", features = ["tracing"], optional = true }
Expand All @@ -137,7 +137,7 @@ tracing = "0.1.37"
proptest = "1.2.0"
proptest-derive = "0.3.0"

rand = { version = "0.8.5", package = "rand" }
rand = "0.8.5"
rand_chacha = "0.3.1"

tokio = { version = "1.28.2", features = ["full", "tracing", "test-util"] }
Expand Down
2 changes: 1 addition & 1 deletion zebra-consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ bellman = "0.14.0"
bls12_381 = "0.8.0"
halo2 = { package = "halo2_proofs", version = "0.3.0" }
jubjub = "0.10.0"
rand = { version = "0.8.5", package = "rand" }
rand = "0.8.5"
rayon = "1.7.0"

chrono = { version = "0.4.26", default-features = false, features = ["clock", "std"] }
Expand Down
2 changes: 1 addition & 1 deletion zebra-network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ lazy_static = "1.4.0"
num-integer = "0.1.45"
ordered-map = "0.4.2"
pin-project = "1.1.0"
rand = { version = "0.8.5", package = "rand" }
rand = "0.8.5"
rayon = "1.7.0"
regex = "1.8.4"
serde = { version = "1.0.164", features = ["serde_derive"] }
Expand Down
2 changes: 1 addition & 1 deletion zebra-rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ hex = { version = "0.4.3", features = ["serde"] }
serde = { version = "1.0.164", features = ["serde_derive"] }

# Experimental feature getblocktemplate-rpcs
rand = { version = "0.8.5", package = "rand", optional = true }
rand = { version = "0.8.5", optional = true }
# ECC deps used by getblocktemplate-rpcs feature
zcash_address = { version = "0.2.1", optional = true }

Expand Down
3 changes: 2 additions & 1 deletion zebra-state/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,11 @@ once_cell = "1.18.0"
spandoc = "0.2.2"

hex = { version = "0.4.3", features = ["serde"] }
insta = { version = "1.30.0", features = ["ron"] }
insta = { version = "1.30.0", features = ["ron", "redactions"] }

proptest = "1.2.0"
proptest-derive = "0.3.0"
rand = "0.8.5"

halo2 = { package = "halo2_proofs", version = "0.3.0" }
jubjub = "0.10.0"
Expand Down
102 changes: 101 additions & 1 deletion zebra-state/src/service/finalized_state/disk_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@
//! The [`crate::constants::DATABASE_FORMAT_VERSION`] constant must
//! be incremented each time the database format (column, serialization, etc) changes.

use std::{cmp::Ordering, fmt::Debug, path::Path, sync::Arc};
use std::{
cmp::Ordering,
collections::{BTreeMap, HashMap},
fmt::Debug,
ops::RangeBounds,
path::Path,
sync::Arc,
};

use itertools::Itertools;
use rlimit::increase_nofile_limit;
Expand Down Expand Up @@ -146,6 +153,7 @@ impl WriteDisk for DiskWriteBatch {
/// defined format
//
// TODO: just implement these methods directly on DiskDb
// move this trait, its methods, and support methods to another module
pub trait ReadDisk {
/// Returns true if a rocksdb column family `cf` does not contain any entries.
fn zs_is_empty<C>(&self, cf: &C) -> bool
Expand Down Expand Up @@ -202,6 +210,26 @@ pub trait ReadDisk {
C: rocksdb::AsColumnFamilyRef,
K: IntoDisk + FromDisk,
V: FromDisk;

/// Returns the keys and values in `cf` in `range`, in an ordered `BTreeMap`.
///
/// Holding this iterator open might delay block commit transactions.
fn zs_items_in_range_ordered<C, K, V, R>(&self, cf: &C, range: R) -> BTreeMap<K, V>
where
C: rocksdb::AsColumnFamilyRef,
K: IntoDisk + FromDisk + Ord,
V: FromDisk,
R: RangeBounds<K>;

/// Returns the keys and values in `cf` in `range`, in an unordered `HashMap`.
///
/// Holding this iterator open might delay block commit transactions.
fn zs_items_in_range_unordered<C, K, V, R>(&self, cf: &C, range: R) -> HashMap<K, V>
where
C: rocksdb::AsColumnFamilyRef,
K: IntoDisk + FromDisk + Eq + std::hash::Hash,
V: FromDisk,
R: RangeBounds<K>;
}

impl PartialEq for DiskDb {
Expand Down Expand Up @@ -342,6 +370,26 @@ impl ReadDisk for DiskDb {
})
.expect("unexpected database failure")
}

fn zs_items_in_range_ordered<C, K, V, R>(&self, cf: &C, range: R) -> BTreeMap<K, V>
where
C: rocksdb::AsColumnFamilyRef,
K: IntoDisk + FromDisk + Ord,
V: FromDisk,
R: RangeBounds<K>,
{
self.zs_range_iter(cf, range).collect()
}

fn zs_items_in_range_unordered<C, K, V, R>(&self, cf: &C, range: R) -> HashMap<K, V>
where
C: rocksdb::AsColumnFamilyRef,
K: IntoDisk + FromDisk + Eq + std::hash::Hash,
V: FromDisk,
R: RangeBounds<K>,
{
self.zs_range_iter(cf, range).collect()
}
}

impl DiskWriteBatch {
Expand All @@ -366,6 +414,58 @@ impl DiskWriteBatch {
}

impl DiskDb {
/// Returns an iterator over the items in `cf` in `range`.
///
/// Holding this iterator open might delay block commit transactions.
fn zs_range_iter<C, K, V, R>(&self, cf: &C, range: R) -> impl Iterator<Item = (K, V)> + '_
where
C: rocksdb::AsColumnFamilyRef,
K: IntoDisk + FromDisk,
V: FromDisk,
R: RangeBounds<K>,
{
use std::ops::Bound::{self, *};

// Replace with map() when it stabilises:
// https://github.com/rust-lang/rust/issues/86026
let map_to_vec = |bound: Bound<&K>| -> Bound<Vec<u8>> {
match bound {
Unbounded => Unbounded,
Included(x) => Included(x.as_bytes().as_ref().to_vec()),
Excluded(x) => Excluded(x.as_bytes().as_ref().to_vec()),
}
};

let start_bound = map_to_vec(range.start_bound());
let end_bound = map_to_vec(range.end_bound());
let range = (start_bound.clone(), end_bound);

let start_bound_vec =
if let Included(ref start_bound) | Excluded(ref start_bound) = start_bound {
start_bound.clone()
} else {
// Actually unused
Vec::new()
};

let start_mode = if matches!(start_bound, Unbounded) {
// Unbounded iterators start at the first item
rocksdb::IteratorMode::Start
} else {
rocksdb::IteratorMode::From(start_bound_vec.as_slice(), rocksdb::Direction::Forward)
};

// Reading multiple items from iterators has caused database hangs,
// in previous RocksDB versions
self.db
.iterator_cf(cf, start_mode)
.map(|result| result.expect("unexpected database failure"))
.map(|(key, value)| (key.to_vec(), value))
// Handle Excluded start and the end bound
.filter(move |(key, _value)| range.contains(key))
.map(|(key, value)| (K::from_bytes(key), V::from_bytes(value)))
}

/// The ideal open file limit for Zebra
const IDEAL_OPEN_FILE_LIMIT: u64 = 1024;

Expand Down
21 changes: 21 additions & 0 deletions zebra-state/src/service/finalized_state/disk_format/shielded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ impl IntoDisk for sprout::tree::Root {
}
}

impl FromDisk for sprout::tree::Root {
fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
let array: [u8; 32] = bytes.as_ref().try_into().unwrap();
array.into()
}
}

impl IntoDisk for sapling::tree::Root {
type Bytes = [u8; 32];

Expand All @@ -52,6 +59,13 @@ impl IntoDisk for sapling::tree::Root {
}
}

impl FromDisk for sapling::tree::Root {
fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
let array: [u8; 32] = bytes.as_ref().try_into().unwrap();
array.try_into().expect("finalized data must be valid")
}
}

impl IntoDisk for orchard::tree::Root {
type Bytes = [u8; 32];

Expand All @@ -60,6 +74,13 @@ impl IntoDisk for orchard::tree::Root {
}
}

impl FromDisk for orchard::tree::Root {
fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
let array: [u8; 32] = bytes.as_ref().try_into().unwrap();
array.try_into().expect("finalized data must be valid")
}
}

// The following implementations for the note commitment trees use `serde` and
// `bincode` because currently the inner Merkle tree frontier (from
// `incrementalmerkletree`) only supports `serde` for serialization. `bincode`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,13 @@ fn serialized_sprout_tree_root_equal() {
);
}

#[test]
fn roundtrip_sprout_tree_root() {
let _init_guard = zebra_test::init();

proptest!(|(val in any::<sprout::tree::Root>())| assert_value_properties(val));
}

// TODO: test note commitment tree round-trip, after implementing proptest::Arbitrary

// Sapling
Expand Down Expand Up @@ -347,6 +354,13 @@ fn serialized_sapling_tree_root_equal() {
);
}

#[test]
fn roundtrip_sapling_tree_root() {
let _init_guard = zebra_test::init();

proptest!(|(val in any::<sapling::tree::Root>())| assert_value_properties(val));
}

// TODO: test note commitment tree round-trip, after implementing proptest::Arbitrary

// Orchard
Expand Down Expand Up @@ -415,6 +429,13 @@ fn serialized_orchard_tree_root_equal() {
);
}

#[test]
fn roundtrip_orchard_tree_root() {
let _init_guard = zebra_test::init();

proptest!(|(val in any::<orchard::tree::Root>())| assert_value_properties(val));
}

// TODO: test note commitment tree round-trip, after implementing proptest::Arbitrary

// Chain
Expand Down
Loading