//! Reading note commitment trees. //! //! In the functions in this module: //! //! The block write task commits blocks to the finalized state before updating //! `chain` with a cached copy of the best non-finalized chain from //! `NonFinalizedState.chain_set`. Then the block commit task can commit additional blocks to //! the finalized state after we've cloned the `chain`. //! //! This means that some blocks can be in both: //! - the cached [`Chain`], and //! - the shared finalized [`ZebraDb`] reference. use std::sync::Arc; use zebra_chain::{ orchard, sapling, subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeIndex}, }; use crate::{ service::{finalized_state::ZebraDb, non_finalized_state::Chain}, HashOrHeight, }; /// Returns the Sapling /// [`NoteCommitmentTree`](sapling::tree::NoteCommitmentTree) specified by a /// hash or height, if it exists in the non-finalized `chain` or finalized `db`. pub fn sapling_tree( chain: Option, db: &ZebraDb, hash_or_height: HashOrHeight, ) -> Option> where C: AsRef, { // # Correctness // // Since sapling treestates are the same in the finalized and non-finalized // state, we check the most efficient alternative first. (`chain` is always // in memory, but `db` stores blocks on disk, with a memory cache.) chain .and_then(|chain| chain.as_ref().sapling_tree(hash_or_height)) .or_else(|| db.sapling_tree_by_hash_or_height(hash_or_height)) } /// Returns the Sapling /// [`NoteCommitmentSubtree`](NoteCommitmentSubtree) specified by an /// index, if it exists in the non-finalized `chain` or finalized `db`. #[allow(unused)] pub fn sapling_subtree( chain: Option, db: &ZebraDb, index: NoteCommitmentSubtreeIndex, ) -> Option>> where C: AsRef, { // # Correctness // // Since sapling treestates are the same in the finalized and non-finalized // state, we check the most efficient alternative first. (`chain` is always // in memory, but `db` stores blocks on disk, with a memory cache.) chain .and_then(|chain| chain.as_ref().sapling_subtree_by_index(index)) .or_else(|| db.sapling_subtree_by_index(index)) } /// Returns the Orchard /// [`NoteCommitmentTree`](orchard::tree::NoteCommitmentTree) specified by a /// hash or height, if it exists in the non-finalized `chain` or finalized `db`. pub fn orchard_tree( chain: Option, db: &ZebraDb, hash_or_height: HashOrHeight, ) -> Option> where C: AsRef, { // # Correctness // // Since orchard treestates are the same in the finalized and non-finalized // state, we check the most efficient alternative first. (`chain` is always // in memory, but `db` stores blocks on disk, with a memory cache.) chain .and_then(|chain| chain.as_ref().orchard_tree(hash_or_height)) .or_else(|| db.orchard_tree_by_hash_or_height(hash_or_height)) } /// Returns the Orchard /// [`NoteCommitmentSubtree`](NoteCommitmentSubtree) specified by an /// index, if it exists in the non-finalized `chain` or finalized `db`. #[allow(unused)] pub fn orchard_subtree( chain: Option, db: &ZebraDb, index: NoteCommitmentSubtreeIndex, ) -> Option>> where C: AsRef, { // # Correctness // // Since orchard treestates are the same in the finalized and non-finalized // state, we check the most efficient alternative first. (`chain` is always // in memory, but `db` stores blocks on disk, with a memory cache.) chain .and_then(|chain| chain.as_ref().orchard_subtree_by_index(index)) .or_else(|| db.orchard_subtree_by_index(index)) } #[cfg(feature = "getblocktemplate-rpcs")] /// Get the history tree of the provided chain. pub fn history_tree( chain: Option, db: &ZebraDb, hash_or_height: HashOrHeight, ) -> Option> where C: AsRef, { chain .and_then(|chain| chain.as_ref().history_tree(hash_or_height)) .or_else(|| Some(db.history_tree())) }