use std::{
collections::{BTreeMap, HashMap},
sync::Arc,
};
use zebra_chain::{
block::Height,
orchard,
parallel::tree::NoteCommitmentTrees,
sapling, sprout,
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
transaction::Transaction,
};
use crate::{
request::{FinalizedBlock, Treestate},
service::finalized_state::{
disk_db::{DiskDb, DiskWriteBatch, ReadDisk, WriteDisk},
disk_format::RawBytes,
zebra_db::ZebraDb,
},
BoxError,
};
#[allow(unused_imports)]
use zebra_chain::subtree::NoteCommitmentSubtree;
impl ZebraDb {
pub fn contains_sprout_nullifier(&self, sprout_nullifier: &sprout::Nullifier) -> bool {
let sprout_nullifiers = self.db.cf_handle("sprout_nullifiers").unwrap();
self.db.zs_contains(&sprout_nullifiers, &sprout_nullifier)
}
pub fn contains_sapling_nullifier(&self, sapling_nullifier: &sapling::Nullifier) -> bool {
let sapling_nullifiers = self.db.cf_handle("sapling_nullifiers").unwrap();
self.db.zs_contains(&sapling_nullifiers, &sapling_nullifier)
}
pub fn contains_orchard_nullifier(&self, orchard_nullifier: &orchard::Nullifier) -> bool {
let orchard_nullifiers = self.db.cf_handle("orchard_nullifiers").unwrap();
self.db.zs_contains(&orchard_nullifiers, &orchard_nullifier)
}
#[allow(dead_code)]
pub fn contains_sprout_anchor(&self, sprout_anchor: &sprout::tree::Root) -> bool {
let sprout_anchors = self.db.cf_handle("sprout_anchors").unwrap();
self.db.zs_contains(&sprout_anchors, &sprout_anchor)
}
pub fn contains_sapling_anchor(&self, sapling_anchor: &sapling::tree::Root) -> bool {
let sapling_anchors = self.db.cf_handle("sapling_anchors").unwrap();
self.db.zs_contains(&sapling_anchors, &sapling_anchor)
}
pub fn contains_orchard_anchor(&self, orchard_anchor: &orchard::tree::Root) -> bool {
let orchard_anchors = self.db.cf_handle("orchard_anchors").unwrap();
self.db.zs_contains(&orchard_anchors, &orchard_anchor)
}
pub fn sprout_tree_for_tip(&self) -> Arc<sprout::tree::NoteCommitmentTree> {
if self.is_empty() {
return Arc::<sprout::tree::NoteCommitmentTree>::default();
}
let sprout_tree_cf = self.db.cf_handle("sprout_note_commitment_tree").unwrap();
let mut sprout_tree: Option<Arc<sprout::tree::NoteCommitmentTree>> =
self.db.zs_get(&sprout_tree_cf, &());
if sprout_tree.is_none() {
sprout_tree = self
.db
.zs_last_key_value(&sprout_tree_cf)
.map(|(_key, tree_value): (Height, _)| tree_value);
}
sprout_tree.expect("Sprout note commitment tree must exist if there is a finalized tip")
}
#[allow(clippy::unwrap_in_result)]
pub fn sprout_tree_by_anchor(
&self,
sprout_anchor: &sprout::tree::Root,
) -> Option<Arc<sprout::tree::NoteCommitmentTree>> {
let sprout_anchors_handle = self.db.cf_handle("sprout_anchors").unwrap();
self.db
.zs_get(&sprout_anchors_handle, sprout_anchor)
.map(Arc::new)
}
#[allow(dead_code)]
pub fn sprout_trees_full_map(
&self,
) -> HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>> {
let sprout_anchors_handle = self.db.cf_handle("sprout_anchors").unwrap();
self.db
.zs_items_in_range_unordered(&sprout_anchors_handle, ..)
}
pub fn sprout_trees_full_tip(
&self,
) -> impl Iterator<Item = (RawBytes, Arc<sprout::tree::NoteCommitmentTree>)> + '_ {
let sprout_trees = self.db.cf_handle("sprout_note_commitment_tree").unwrap();
self.db.zs_forward_range_iter(&sprout_trees, ..)
}
pub fn sapling_tree_for_tip(&self) -> Arc<sapling::tree::NoteCommitmentTree> {
let height = match self.finalized_tip_height() {
Some(h) => h,
None => return Default::default(),
};
self.sapling_tree_by_height(&height)
.expect("Sapling note commitment tree must exist if there is a finalized tip")
}
#[allow(clippy::unwrap_in_result)]
pub fn sapling_tree_by_height(
&self,
height: &Height,
) -> Option<Arc<sapling::tree::NoteCommitmentTree>> {
let tip_height = self.finalized_tip_height()?;
if *height > tip_height {
return None;
}
let sapling_trees = self.db.cf_handle("sapling_note_commitment_tree").unwrap();
let (_first_duplicate_height, tree) = self
.db
.zs_prev_key_value_back_from(&sapling_trees, height)
.expect(
"Sapling note commitment trees must exist for all heights below the finalized tip",
);
Some(Arc::new(tree))
}
pub fn sapling_tree_by_height_range<R>(
&self,
range: R,
) -> impl Iterator<Item = (Height, Arc<sapling::tree::NoteCommitmentTree>)> + '_
where
R: std::ops::RangeBounds<Height>,
{
let sapling_trees = self.db.cf_handle("sapling_note_commitment_tree").unwrap();
self.db.zs_forward_range_iter(&sapling_trees, range)
}
pub fn sapling_tree_by_reversed_height_range<R>(
&self,
range: R,
) -> impl Iterator<Item = (Height, Arc<sapling::tree::NoteCommitmentTree>)> + '_
where
R: std::ops::RangeBounds<Height>,
{
let sapling_trees = self.db.cf_handle("sapling_note_commitment_tree").unwrap();
self.db.zs_reverse_range_iter(&sapling_trees, range)
}
#[allow(clippy::unwrap_in_result)]
pub(in super::super) fn sapling_subtree_by_index(
&self,
index: impl Into<NoteCommitmentSubtreeIndex> + Copy,
) -> Option<NoteCommitmentSubtree<sapling::tree::Node>> {
let sapling_subtrees = self
.db
.cf_handle("sapling_note_commitment_subtree")
.unwrap();
let subtree_data: NoteCommitmentSubtreeData<sapling::tree::Node> =
self.db.zs_get(&sapling_subtrees, &index.into())?;
Some(subtree_data.with_index(index))
}
#[allow(clippy::unwrap_in_result)]
pub fn sapling_subtree_list_by_index_range(
&self,
range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex>,
) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<sapling::tree::Node>> {
let sapling_subtrees = self
.db
.cf_handle("sapling_note_commitment_subtree")
.unwrap();
self.db
.zs_forward_range_iter(&sapling_subtrees, range)
.collect()
}
#[allow(clippy::unwrap_in_result)]
fn sapling_subtree_for_tip(&self) -> Option<NoteCommitmentSubtree<sapling::tree::Node>> {
let sapling_subtrees = self
.db
.cf_handle("sapling_note_commitment_subtree")
.unwrap();
let (index, subtree_data): (
NoteCommitmentSubtreeIndex,
NoteCommitmentSubtreeData<sapling::tree::Node>,
) = self.db.zs_last_key_value(&sapling_subtrees)?;
let tip_height = self.finalized_tip_height()?;
if subtree_data.end_height != tip_height {
return None;
}
Some(subtree_data.with_index(index))
}
pub fn orchard_tree_for_tip(&self) -> Arc<orchard::tree::NoteCommitmentTree> {
let height = match self.finalized_tip_height() {
Some(h) => h,
None => return Default::default(),
};
self.orchard_tree_by_height(&height)
.expect("Orchard note commitment tree must exist if there is a finalized tip")
}
#[allow(clippy::unwrap_in_result)]
pub fn orchard_tree_by_height(
&self,
height: &Height,
) -> Option<Arc<orchard::tree::NoteCommitmentTree>> {
let tip_height = self.finalized_tip_height()?;
if *height > tip_height {
return None;
}
let orchard_trees = self.db.cf_handle("orchard_note_commitment_tree").unwrap();
let (_first_duplicate_height, tree) = self
.db
.zs_prev_key_value_back_from(&orchard_trees, height)
.expect(
"Orchard note commitment trees must exist for all heights below the finalized tip",
);
Some(Arc::new(tree))
}
pub fn orchard_tree_by_height_range<R>(
&self,
range: R,
) -> impl Iterator<Item = (Height, Arc<orchard::tree::NoteCommitmentTree>)> + '_
where
R: std::ops::RangeBounds<Height>,
{
let orchard_trees = self.db.cf_handle("orchard_note_commitment_tree").unwrap();
self.db.zs_forward_range_iter(&orchard_trees, range)
}
pub fn orchard_tree_by_reversed_height_range<R>(
&self,
range: R,
) -> impl Iterator<Item = (Height, Arc<orchard::tree::NoteCommitmentTree>)> + '_
where
R: std::ops::RangeBounds<Height>,
{
let orchard_trees = self.db.cf_handle("orchard_note_commitment_tree").unwrap();
self.db.zs_reverse_range_iter(&orchard_trees, range)
}
#[allow(clippy::unwrap_in_result)]
pub(in super::super) fn orchard_subtree_by_index(
&self,
index: impl Into<NoteCommitmentSubtreeIndex> + Copy,
) -> Option<NoteCommitmentSubtree<orchard::tree::Node>> {
let orchard_subtrees = self
.db
.cf_handle("orchard_note_commitment_subtree")
.unwrap();
let subtree_data: NoteCommitmentSubtreeData<orchard::tree::Node> =
self.db.zs_get(&orchard_subtrees, &index.into())?;
Some(subtree_data.with_index(index))
}
#[allow(clippy::unwrap_in_result)]
pub fn orchard_subtree_list_by_index_range(
&self,
range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex>,
) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>> {
let orchard_subtrees = self
.db
.cf_handle("orchard_note_commitment_subtree")
.unwrap();
self.db
.zs_forward_range_iter(&orchard_subtrees, range)
.collect()
}
#[allow(clippy::unwrap_in_result)]
fn orchard_subtree_for_tip(&self) -> Option<NoteCommitmentSubtree<orchard::tree::Node>> {
let orchard_subtrees = self
.db
.cf_handle("orchard_note_commitment_subtree")
.unwrap();
let (index, subtree_data): (
NoteCommitmentSubtreeIndex,
NoteCommitmentSubtreeData<orchard::tree::Node>,
) = self.db.zs_last_key_value(&orchard_subtrees)?;
let tip_height = self.finalized_tip_height()?;
if subtree_data.end_height != tip_height {
return None;
}
Some(subtree_data.with_index(index))
}
pub fn note_commitment_trees_for_tip(&self) -> NoteCommitmentTrees {
NoteCommitmentTrees {
sprout: self.sprout_tree_for_tip(),
sapling: self.sapling_tree_for_tip(),
sapling_subtree: self.sapling_subtree_for_tip(),
orchard: self.orchard_tree_for_tip(),
orchard_subtree: self.orchard_subtree_for_tip(),
}
}
}
impl DiskWriteBatch {
pub fn prepare_shielded_transaction_batch(
&mut self,
db: &DiskDb,
finalized: &FinalizedBlock,
) -> Result<(), BoxError> {
let FinalizedBlock { block, .. } = finalized;
for transaction in &block.transactions {
self.prepare_nullifier_batch(db, transaction)?;
}
Ok(())
}
#[allow(clippy::unwrap_in_result)]
pub fn prepare_nullifier_batch(
&mut self,
db: &DiskDb,
transaction: &Transaction,
) -> Result<(), BoxError> {
let sprout_nullifiers = db.cf_handle("sprout_nullifiers").unwrap();
let sapling_nullifiers = db.cf_handle("sapling_nullifiers").unwrap();
let orchard_nullifiers = db.cf_handle("orchard_nullifiers").unwrap();
for sprout_nullifier in transaction.sprout_nullifiers() {
self.zs_insert(&sprout_nullifiers, sprout_nullifier, ());
}
for sapling_nullifier in transaction.sapling_nullifiers() {
self.zs_insert(&sapling_nullifiers, sapling_nullifier, ());
}
for orchard_nullifier in transaction.orchard_nullifiers() {
self.zs_insert(&orchard_nullifiers, orchard_nullifier, ());
}
Ok(())
}
#[allow(clippy::unwrap_in_result)]
pub fn prepare_trees_batch(
&mut self,
zebra_db: &ZebraDb,
finalized: &FinalizedBlock,
prev_note_commitment_trees: Option<NoteCommitmentTrees>,
) -> Result<(), BoxError> {
let FinalizedBlock {
height,
treestate:
Treestate {
note_commitment_trees,
history_tree,
},
..
} = finalized;
let prev_sprout_tree = prev_note_commitment_trees.as_ref().map_or_else(
|| zebra_db.sprout_tree_for_tip(),
|prev_trees| prev_trees.sprout.clone(),
);
let prev_sapling_tree = prev_note_commitment_trees.as_ref().map_or_else(
|| zebra_db.sapling_tree_for_tip(),
|prev_trees| prev_trees.sapling.clone(),
);
let prev_orchard_tree = prev_note_commitment_trees.as_ref().map_or_else(
|| zebra_db.orchard_tree_for_tip(),
|prev_trees| prev_trees.orchard.clone(),
);
if height.is_min() || prev_sprout_tree != note_commitment_trees.sprout {
self.update_sprout_tree(zebra_db, ¬e_commitment_trees.sprout)
}
if height.is_min() || prev_sapling_tree != note_commitment_trees.sapling {
self.create_sapling_tree(zebra_db, height, ¬e_commitment_trees.sapling);
if let Some(subtree) = note_commitment_trees.sapling_subtree {
self.insert_sapling_subtree(zebra_db, &subtree);
}
}
if height.is_min() || prev_orchard_tree != note_commitment_trees.orchard {
self.create_orchard_tree(zebra_db, height, ¬e_commitment_trees.orchard);
if let Some(subtree) = note_commitment_trees.orchard_subtree {
self.insert_orchard_subtree(zebra_db, &subtree);
}
}
self.update_history_tree(zebra_db, history_tree);
Ok(())
}
pub fn update_sprout_tree(
&mut self,
zebra_db: &ZebraDb,
tree: &sprout::tree::NoteCommitmentTree,
) {
let sprout_anchors = zebra_db.db.cf_handle("sprout_anchors").unwrap();
let sprout_tree_cf = zebra_db
.db
.cf_handle("sprout_note_commitment_tree")
.unwrap();
self.zs_insert(&sprout_anchors, tree.root(), tree);
self.zs_insert(&sprout_tree_cf, (), tree);
}
pub fn delete_range_sprout_tree(&mut self, zebra_db: &ZebraDb, from: &Height, to: &Height) {
let sprout_tree_cf = zebra_db
.db
.cf_handle("sprout_note_commitment_tree")
.unwrap();
self.zs_delete_range(&sprout_tree_cf, from, to);
}
#[allow(dead_code)]
pub fn delete_sprout_anchor(&mut self, zebra_db: &ZebraDb, anchor: &sprout::tree::Root) {
let sprout_anchors = zebra_db.db.cf_handle("sprout_anchors").unwrap();
self.zs_delete(&sprout_anchors, anchor);
}
pub fn create_sapling_tree(
&mut self,
zebra_db: &ZebraDb,
height: &Height,
tree: &sapling::tree::NoteCommitmentTree,
) {
let sapling_anchors = zebra_db.db.cf_handle("sapling_anchors").unwrap();
let sapling_tree_cf = zebra_db
.db
.cf_handle("sapling_note_commitment_tree")
.unwrap();
self.zs_insert(&sapling_anchors, tree.root(), ());
self.zs_insert(&sapling_tree_cf, height, tree);
}
pub fn insert_sapling_subtree(
&mut self,
zebra_db: &ZebraDb,
subtree: &NoteCommitmentSubtree<sapling::tree::Node>,
) {
let sapling_subtree_cf = zebra_db
.db
.cf_handle("sapling_note_commitment_subtree")
.unwrap();
self.zs_insert(&sapling_subtree_cf, subtree.index, subtree.into_data());
}
pub fn delete_sapling_tree(&mut self, zebra_db: &ZebraDb, height: &Height) {
let sapling_tree_cf = zebra_db
.db
.cf_handle("sapling_note_commitment_tree")
.unwrap();
self.zs_delete(&sapling_tree_cf, height);
}
#[allow(dead_code)]
pub fn delete_range_sapling_tree(&mut self, zebra_db: &ZebraDb, from: &Height, to: &Height) {
let sapling_tree_cf = zebra_db
.db
.cf_handle("sapling_note_commitment_tree")
.unwrap();
self.zs_delete_range(&sapling_tree_cf, from, to);
}
#[allow(dead_code)]
pub fn delete_sapling_anchor(&mut self, zebra_db: &ZebraDb, anchor: &sapling::tree::Root) {
let sapling_anchors = zebra_db.db.cf_handle("sapling_anchors").unwrap();
self.zs_delete(&sapling_anchors, anchor);
}
pub fn delete_range_sapling_subtree(
&mut self,
zebra_db: &ZebraDb,
from: NoteCommitmentSubtreeIndex,
to: NoteCommitmentSubtreeIndex,
) {
let sapling_subtree_cf = zebra_db
.db
.cf_handle("sapling_note_commitment_subtree")
.unwrap();
self.zs_delete_range(&sapling_subtree_cf, from, to);
}
pub fn create_orchard_tree(
&mut self,
zebra_db: &ZebraDb,
height: &Height,
tree: &orchard::tree::NoteCommitmentTree,
) {
let orchard_anchors = zebra_db.db.cf_handle("orchard_anchors").unwrap();
let orchard_tree_cf = zebra_db
.db
.cf_handle("orchard_note_commitment_tree")
.unwrap();
self.zs_insert(&orchard_anchors, tree.root(), ());
self.zs_insert(&orchard_tree_cf, height, tree);
}
pub fn insert_orchard_subtree(
&mut self,
zebra_db: &ZebraDb,
subtree: &NoteCommitmentSubtree<orchard::tree::Node>,
) {
let orchard_subtree_cf = zebra_db
.db
.cf_handle("orchard_note_commitment_subtree")
.unwrap();
self.zs_insert(&orchard_subtree_cf, subtree.index, subtree.into_data());
}
pub fn delete_orchard_tree(&mut self, zebra_db: &ZebraDb, height: &Height) {
let orchard_tree_cf = zebra_db
.db
.cf_handle("orchard_note_commitment_tree")
.unwrap();
self.zs_delete(&orchard_tree_cf, height);
}
#[allow(dead_code)]
pub fn delete_range_orchard_tree(&mut self, zebra_db: &ZebraDb, from: &Height, to: &Height) {
let orchard_tree_cf = zebra_db
.db
.cf_handle("orchard_note_commitment_tree")
.unwrap();
self.zs_delete_range(&orchard_tree_cf, from, to);
}
#[allow(dead_code)]
pub fn delete_orchard_anchor(&mut self, zebra_db: &ZebraDb, anchor: &orchard::tree::Root) {
let orchard_anchors = zebra_db.db.cf_handle("orchard_anchors").unwrap();
self.zs_delete(&orchard_anchors, anchor);
}
pub fn delete_range_orchard_subtree(
&mut self,
zebra_db: &ZebraDb,
from: NoteCommitmentSubtreeIndex,
to: NoteCommitmentSubtreeIndex,
) {
let orchard_subtree_cf = zebra_db
.db
.cf_handle("orchard_note_commitment_subtree")
.unwrap();
self.zs_delete_range(&orchard_subtree_cf, from, to);
}
}