fix(note-commitment-trees): Populate subtrees (#7636)

* add `sapling_subtree_for_tip` and `orchard_subtree_for_tip` methods to `ZebraDb`

* add methods for non finalized state, move functions

* call `zs_last_key_value` the right way

* fix and simplify `*_subtree_for_tip` methods

Co-authored-by: Arya <aryasolhi@gmail.com>

* apply filter

* rename all tree and subtree methods that use tip

* rename tip tree and subtree methods in non finalized chain

* apply simplify suggestions

Co-authored-by: teor <teor@riseup.net>

---------

Co-authored-by: Arya <aryasolhi@gmail.com>
Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
Alfredo Garcia 2023-10-08 23:02:04 -03:00 committed by GitHub
parent c498eee67f
commit 1d45938e0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 116 additions and 50 deletions

View File

@ -242,8 +242,8 @@ impl FinalizedState {
let block = checkpoint_verified.block.clone();
let mut history_tree = self.db.history_tree();
let prev_note_commitment_trees =
prev_note_commitment_trees.unwrap_or_else(|| self.db.note_commitment_trees());
let prev_note_commitment_trees = prev_note_commitment_trees
.unwrap_or_else(|| self.db.note_commitment_trees_for_tip());
// Update the note commitment trees.
let mut note_commitment_trees = prev_note_commitment_trees.clone();

View File

@ -207,12 +207,13 @@ fn quick_check_sapling_subtrees(db: &ZebraDb) -> Result<(), &'static str> {
return Ok(());
}
let Some(NoteCommitmentSubtreeIndex(tip_subtree_index)) = db.sapling_tree().subtree_index()
let Some(NoteCommitmentSubtreeIndex(tip_subtree_index)) =
db.sapling_tree_for_tip().subtree_index()
else {
return Ok(());
};
if tip_subtree_index == 0 && !db.sapling_tree().is_complete_subtree() {
if tip_subtree_index == 0 && !db.sapling_tree_for_tip().is_complete_subtree() {
return Ok(());
}
@ -260,12 +261,13 @@ fn quick_check_orchard_subtrees(db: &ZebraDb) -> Result<(), &'static str> {
return Ok(());
}
let Some(NoteCommitmentSubtreeIndex(tip_subtree_index)) = db.orchard_tree().subtree_index()
let Some(NoteCommitmentSubtreeIndex(tip_subtree_index)) =
db.orchard_tree_for_tip().subtree_index()
else {
return Ok(());
};
if tip_subtree_index == 0 && !db.orchard_tree().is_complete_subtree() {
if tip_subtree_index == 0 && !db.orchard_tree_for_tip().is_complete_subtree() {
return Ok(());
}
@ -333,13 +335,13 @@ fn check_sapling_subtrees(
cancel_receiver: &mpsc::Receiver<CancelFormatChange>,
) -> Result<Result<(), &'static str>, CancelFormatChange> {
let Some(NoteCommitmentSubtreeIndex(mut first_incomplete_subtree_index)) =
db.sapling_tree().subtree_index()
db.sapling_tree_for_tip().subtree_index()
else {
return Ok(Ok(()));
};
// If there are no incomplete subtrees in the tree, also expect a subtree for the final index.
if db.sapling_tree().is_complete_subtree() {
if db.sapling_tree_for_tip().is_complete_subtree() {
first_incomplete_subtree_index += 1;
}
@ -463,13 +465,13 @@ fn check_orchard_subtrees(
cancel_receiver: &mpsc::Receiver<CancelFormatChange>,
) -> Result<Result<(), &'static str>, CancelFormatChange> {
let Some(NoteCommitmentSubtreeIndex(mut first_incomplete_subtree_index)) =
db.orchard_tree().subtree_index()
db.orchard_tree_for_tip().subtree_index()
else {
return Ok(Ok(()));
};
// If there are no incomplete subtrees in the tree, also expect a subtree for the final index.
if db.orchard_tree().is_complete_subtree() {
if db.orchard_tree_for_tip().is_complete_subtree() {
first_incomplete_subtree_index += 1;
}

View File

@ -247,9 +247,9 @@ fn snapshot_block_and_transaction_data(state: &FinalizedState) {
let mut stored_sapling_trees = Vec::new();
let mut stored_orchard_trees = Vec::new();
let sprout_tree_at_tip = state.sprout_tree();
let sapling_tree_at_tip = state.sapling_tree();
let orchard_tree_at_tip = state.orchard_tree();
let sprout_tree_at_tip = state.sprout_tree_for_tip();
let sapling_tree_at_tip = state.sapling_tree_for_tip();
let orchard_tree_at_tip = state.orchard_tree_for_tip();
// Test the history tree.
//

View File

@ -83,7 +83,7 @@ impl ZebraDb {
/// Returns the Sprout note commitment tree of the finalized tip
/// or the empty tree if the state is empty.
pub fn sprout_tree(&self) -> Arc<sprout::tree::NoteCommitmentTree> {
pub fn sprout_tree_for_tip(&self) -> Arc<sprout::tree::NoteCommitmentTree> {
if self.is_empty() {
return Arc::<sprout::tree::NoteCommitmentTree>::default();
}
@ -161,7 +161,7 @@ impl ZebraDb {
/// Returns the Sapling note commitment tree of the finalized tip or the empty tree if the state
/// is empty.
pub fn sapling_tree(&self) -> Arc<sapling::tree::NoteCommitmentTree> {
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(),
@ -303,11 +303,32 @@ impl ZebraDb {
}
}
/// Get the sapling note commitment subtress for the finalized tip.
#[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 != tip_height {
return None;
}
Some(subtree_data.with_index(index))
}
// Orchard trees
/// Returns the Orchard note commitment tree of the finalized tip or the empty tree if the state
/// is empty.
pub fn orchard_tree(&self) -> Arc<orchard::tree::NoteCommitmentTree> {
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(),
@ -449,15 +470,38 @@ impl ZebraDb {
}
}
/// Get the orchard note commitment subtress for the finalized tip.
#[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 != tip_height {
return None;
}
Some(subtree_data.with_index(index))
}
/// Returns the shielded note commitment trees of the finalized tip
/// or the empty trees if the state is empty.
pub fn note_commitment_trees(&self) -> NoteCommitmentTrees {
/// Additionally, returns the sapling and orchard subtrees for the finalized tip if
/// the current subtree is finalizing in the tip, None otherwise.
pub fn note_commitment_trees_for_tip(&self) -> NoteCommitmentTrees {
NoteCommitmentTrees {
sprout: self.sprout_tree(),
sapling: self.sapling_tree(),
sapling_subtree: None,
orchard: self.orchard_tree(),
orchard_subtree: None,
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(),
}
}
}
@ -571,10 +615,10 @@ impl DiskWriteBatch {
// Store the Sapling tree only if it is not already present at the previous height.
if height.is_min()
|| prev_note_commitment_trees
.as_ref()
.map_or_else(|| zebra_db.sapling_tree(), |trees| trees.sapling.clone())
!= trees.sapling
|| prev_note_commitment_trees.as_ref().map_or_else(
|| zebra_db.sapling_tree_for_tip(),
|trees| trees.sapling.clone(),
) != trees.sapling
{
self.zs_insert(&sapling_tree_cf, height, trees.sapling);
}
@ -582,7 +626,7 @@ impl DiskWriteBatch {
// Store the Orchard tree only if it is not already present at the previous height.
if height.is_min()
|| prev_note_commitment_trees
.map_or_else(|| zebra_db.orchard_tree(), |trees| trees.orchard)
.map_or_else(|| zebra_db.orchard_tree_for_tip(), |trees| trees.orchard)
!= trees.orchard
{
self.zs_insert(&orchard_tree_cf, height, trees.orchard);

View File

@ -284,9 +284,9 @@ impl NonFinalizedState {
let chain = Chain::new(
self.network,
finalized_tip_height,
finalized_state.sprout_tree(),
finalized_state.sapling_tree(),
finalized_state.orchard_tree(),
finalized_state.sprout_tree_for_tip(),
finalized_state.sapling_tree_for_tip(),
finalized_state.orchard_tree_for_tip(),
finalized_state.history_tree(),
finalized_state.finalized_value_pool(),
);

View File

@ -504,7 +504,7 @@ impl Chain {
/// # Panics
///
/// If this chain has no sprout trees. (This should be impossible.)
pub fn sprout_note_commitment_tree(&self) -> Arc<sprout::tree::NoteCommitmentTree> {
pub fn sprout_note_commitment_tree_for_tip(&self) -> Arc<sprout::tree::NoteCommitmentTree> {
self.sprout_trees_by_height
.last_key_value()
.expect("only called while sprout_trees_by_height is populated")
@ -668,7 +668,7 @@ impl Chain {
/// # Panics
///
/// If this chain has no sapling trees. (This should be impossible.)
pub fn sapling_note_commitment_tree(&self) -> Arc<sapling::tree::NoteCommitmentTree> {
pub fn sapling_note_commitment_tree_for_tip(&self) -> Arc<sapling::tree::NoteCommitmentTree> {
self.sapling_trees_by_height
.last_key_value()
.expect("only called while sapling_trees_by_height is populated")
@ -737,6 +737,16 @@ impl Chain {
.collect()
}
/// Returns the Sapling [`NoteCommitmentSubtree`] if it was completed at the tip height.
pub fn sapling_subtree_for_tip(&self) -> Option<NoteCommitmentSubtree<sapling::tree::Node>> {
if !self.is_empty() {
let tip = self.non_finalized_tip_height();
self.sapling_subtree(tip.into())
} else {
None
}
}
/// Adds the Sapling `tree` to the tree and anchor indexes at `height`.
///
/// `height` can be either:
@ -869,7 +879,7 @@ impl Chain {
/// # Panics
///
/// If this chain has no orchard trees. (This should be impossible.)
pub fn orchard_note_commitment_tree(&self) -> Arc<orchard::tree::NoteCommitmentTree> {
pub fn orchard_note_commitment_tree_for_tip(&self) -> Arc<orchard::tree::NoteCommitmentTree> {
self.orchard_trees_by_height
.last_key_value()
.expect("only called while orchard_trees_by_height is populated")
@ -939,6 +949,16 @@ impl Chain {
.collect()
}
/// Returns the Orchard [`NoteCommitmentSubtree`] if it was completed at the tip height.
pub fn orchard_subtree_for_tip(&self) -> Option<NoteCommitmentSubtree<orchard::tree::Node>> {
if !self.is_empty() {
let tip = self.non_finalized_tip_height();
self.orchard_subtree(tip.into())
} else {
None
}
}
/// Adds the Orchard `tree` to the tree and anchor indexes at `height`.
///
/// `height` can be either:
@ -1387,11 +1407,11 @@ impl Chain {
// Prepare data for parallel execution
let mut nct = NoteCommitmentTrees {
sprout: self.sprout_note_commitment_tree(),
sapling: self.sapling_note_commitment_tree(),
sapling_subtree: None,
orchard: self.orchard_note_commitment_tree(),
orchard_subtree: None,
sprout: self.sprout_note_commitment_tree_for_tip(),
sapling: self.sapling_note_commitment_tree_for_tip(),
sapling_subtree: self.sapling_subtree_for_tip(),
orchard: self.orchard_note_commitment_tree_for_tip(),
orchard_subtree: self.orchard_subtree_for_tip(),
};
let mut tree_result = None;
@ -1427,8 +1447,8 @@ impl Chain {
.insert(subtree.index, subtree.into_data());
}
let sapling_root = self.sapling_note_commitment_tree().root();
let orchard_root = self.orchard_note_commitment_tree().root();
let sapling_root = self.sapling_note_commitment_tree_for_tip().root();
let orchard_root = self.orchard_note_commitment_tree_for_tip().root();
// TODO: update the history trees in a rayon thread, if they show up in CPU profiles
let mut history_tree = self.history_block_commitment_tree();

View File

@ -328,9 +328,9 @@ fn finalized_equals_pushed_genesis() -> Result<()> {
let mut partial_chain = Chain::new(
network,
full_chain.non_finalized_tip_height(),
full_chain.sprout_note_commitment_tree(),
full_chain.sapling_note_commitment_tree(),
full_chain.orchard_note_commitment_tree(),
full_chain.sprout_note_commitment_tree_for_tip(),
full_chain.sapling_note_commitment_tree_for_tip(),
full_chain.orchard_note_commitment_tree_for_tip(),
full_chain.history_block_commitment_tree(),
full_chain.chain_value_pools,
);
@ -406,9 +406,9 @@ fn finalized_equals_pushed_history_tree() -> Result<()> {
let mut partial_chain = Chain::new(
network,
Height(finalized_count.try_into().unwrap()),
full_chain.sprout_note_commitment_tree(),
full_chain.sapling_note_commitment_tree(),
full_chain.orchard_note_commitment_tree(),
full_chain.sprout_note_commitment_tree_for_tip(),
full_chain.sapling_note_commitment_tree_for_tip(),
full_chain.orchard_note_commitment_tree_for_tip(),
full_chain.history_block_commitment_tree(),
full_chain.chain_value_pools,
);

View File

@ -513,8 +513,8 @@ fn history_tree_is_updated_for_network_upgrade(
let tree = NonEmptyHistoryTree::from_block(
Network::Mainnet,
activation_block.clone(),
&chain.sapling_note_commitment_tree().root(),
&chain.orchard_note_commitment_tree().root(),
&chain.sapling_note_commitment_tree_for_tip().root(),
&chain.orchard_note_commitment_tree_for_tip().root(),
)
.unwrap();
@ -598,8 +598,8 @@ fn commitment_is_validated_for_network_upgrade(network: Network, network_upgrade
let tree = NonEmptyHistoryTree::from_block(
Network::Mainnet,
activation_block.clone(),
&chain.sapling_note_commitment_tree().root(),
&chain.orchard_note_commitment_tree().root(),
&chain.sapling_note_commitment_tree_for_tip().root(),
&chain.orchard_note_commitment_tree_for_tip().root(),
)
.unwrap();