change(state): Wrap commitment trees into `Arc` (#4757)

* Wrap Sprout note commitment trees into `Arc`

* Remove a redundant comment

* Rephrase a comment about chain forking

* Remove a redundant comment

The comment is not valid because Zebra uses `bridgetree::Frontier`s from
the `incrementalmerkletree` crate to represent its note commitment
trees. This `struct` does not support popping elements from the tree.

* Wrap Sapling commitment trees into `Arc`

* Remove unnecessary `as_ref`s

* Wrap Orchard commitment trees into `Arc`
This commit is contained in:
Marek 2022-07-15 02:39:41 +02:00 committed by GitHub
parent 61eeeb0b66
commit 485bac819d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 86 additions and 79 deletions

View File

@ -1,7 +1,7 @@
//! Checks for whether cited anchors are previously-computed note commitment //! Checks for whether cited anchors are previously-computed note commitment
//! tree roots. //! tree roots.
use std::collections::HashMap; use std::{collections::HashMap, sync::Arc};
use zebra_chain::sprout; use zebra_chain::sprout;
@ -29,7 +29,7 @@ pub(crate) fn anchors_refer_to_earlier_treestates(
if transaction.has_sprout_joinsplit_data() { if transaction.has_sprout_joinsplit_data() {
let mut interstitial_trees: HashMap< let mut interstitial_trees: HashMap<
sprout::tree::Root, sprout::tree::Root,
sprout::tree::NoteCommitmentTree, Arc<sprout::tree::NoteCommitmentTree>,
> = HashMap::new(); > = HashMap::new();
for joinsplit in transaction.sprout_groth16_joinsplits() { for joinsplit in transaction.sprout_groth16_joinsplits() {
@ -90,11 +90,13 @@ pub(crate) fn anchors_refer_to_earlier_treestates(
} }
}; };
let input_tree_inner = Arc::make_mut(&mut input_tree);
tracing::debug!(?joinsplit.anchor, "validated sprout anchor"); tracing::debug!(?joinsplit.anchor, "validated sprout anchor");
// Add new anchors to the interstitial note commitment tree. // Add new anchors to the interstitial note commitment tree.
for cm in joinsplit.commitments { for cm in joinsplit.commitments {
input_tree input_tree_inner
.append(cm) .append(cm)
.expect("note commitment should be appendable to the tree"); .expect("note commitment should be appendable to the tree");
} }

View File

@ -219,8 +219,8 @@ fn snapshot_block_and_transaction_data(state: &FinalizedState) {
.orchard_note_commitment_tree_by_height(&block::Height::MIN) .orchard_note_commitment_tree_by_height(&block::Height::MIN)
.expect("the genesis block in the database has an Orchard tree"); .expect("the genesis block in the database has an Orchard tree");
assert_eq!(sapling_tree, sapling::tree::NoteCommitmentTree::default()); assert_eq!(*sapling_tree, sapling::tree::NoteCommitmentTree::default());
assert_eq!(orchard_tree, orchard::tree::NoteCommitmentTree::default()); assert_eq!(*orchard_tree, orchard::tree::NoteCommitmentTree::default());
// Blocks // Blocks
let mut stored_block_hashes = Vec::new(); let mut stored_block_hashes = Vec::new();

View File

@ -12,6 +12,8 @@
//! The [`crate::constants::DATABASE_FORMAT_VERSION`] constant must //! The [`crate::constants::DATABASE_FORMAT_VERSION`] constant must
//! be incremented each time the database format (column, serialization, etc) changes. //! be incremented each time the database format (column, serialization, etc) changes.
use std::sync::Arc;
use zebra_chain::{ use zebra_chain::{
block::Height, history_tree::HistoryTree, orchard, sapling, sprout, transaction::Transaction, block::Height, history_tree::HistoryTree, orchard, sapling, sprout, transaction::Transaction,
}; };
@ -28,9 +30,9 @@ use crate::{
/// An argument wrapper struct for note commitment trees. /// An argument wrapper struct for note commitment trees.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct NoteCommitmentTrees { pub struct NoteCommitmentTrees {
sprout: sprout::tree::NoteCommitmentTree, sprout: Arc<sprout::tree::NoteCommitmentTree>,
sapling: sapling::tree::NoteCommitmentTree, sapling: Arc<sapling::tree::NoteCommitmentTree>,
orchard: orchard::tree::NoteCommitmentTree, orchard: Arc<orchard::tree::NoteCommitmentTree>,
} }
impl ZebraDb { impl ZebraDb {
@ -75,16 +77,17 @@ impl ZebraDb {
/// Returns the Sprout note commitment tree of the finalized tip /// Returns the Sprout note commitment tree of the finalized tip
/// or the empty tree if the state is empty. /// or the empty tree if the state is empty.
pub fn sprout_note_commitment_tree(&self) -> sprout::tree::NoteCommitmentTree { pub fn sprout_note_commitment_tree(&self) -> Arc<sprout::tree::NoteCommitmentTree> {
let height = match self.finalized_tip_height() { let height = match self.finalized_tip_height() {
Some(h) => h, Some(h) => h,
None => return Default::default(), None => return Default::default(),
}; };
let sprout_note_commitment_tree = self.db.cf_handle("sprout_note_commitment_tree").unwrap(); let sprout_nct_handle = self.db.cf_handle("sprout_note_commitment_tree").unwrap();
self.db self.db
.zs_get(&sprout_note_commitment_tree, &height) .zs_get(&sprout_nct_handle, &height)
.map(Arc::new)
.expect("Sprout note commitment tree must exist if there is a finalized tip") .expect("Sprout note commitment tree must exist if there is a finalized tip")
} }
@ -95,25 +98,27 @@ impl ZebraDb {
pub fn sprout_note_commitment_tree_by_anchor( pub fn sprout_note_commitment_tree_by_anchor(
&self, &self,
sprout_anchor: &sprout::tree::Root, sprout_anchor: &sprout::tree::Root,
) -> Option<sprout::tree::NoteCommitmentTree> { ) -> Option<Arc<sprout::tree::NoteCommitmentTree>> {
let sprout_anchors = self.db.cf_handle("sprout_anchors").unwrap(); let sprout_anchors_handle = self.db.cf_handle("sprout_anchors").unwrap();
self.db.zs_get(&sprout_anchors, sprout_anchor) self.db
.zs_get(&sprout_anchors_handle, sprout_anchor)
.map(Arc::new)
} }
/// Returns the Sapling note commitment tree of the finalized tip /// Returns the Sapling note commitment tree of the finalized tip
/// or the empty tree if the state is empty. /// or the empty tree if the state is empty.
pub fn sapling_note_commitment_tree(&self) -> sapling::tree::NoteCommitmentTree { pub fn sapling_note_commitment_tree(&self) -> Arc<sapling::tree::NoteCommitmentTree> {
let height = match self.finalized_tip_height() { let height = match self.finalized_tip_height() {
Some(h) => h, Some(h) => h,
None => return Default::default(), None => return Default::default(),
}; };
let sapling_note_commitment_tree = let sapling_nct_handle = self.db.cf_handle("sapling_note_commitment_tree").unwrap();
self.db.cf_handle("sapling_note_commitment_tree").unwrap();
self.db self.db
.zs_get(&sapling_note_commitment_tree, &height) .zs_get(&sapling_nct_handle, &height)
.map(Arc::new)
.expect("Sapling note commitment tree must exist if there is a finalized tip") .expect("Sapling note commitment tree must exist if there is a finalized tip")
} }
@ -123,25 +128,25 @@ impl ZebraDb {
pub fn sapling_note_commitment_tree_by_height( pub fn sapling_note_commitment_tree_by_height(
&self, &self,
height: &Height, height: &Height,
) -> Option<sapling::tree::NoteCommitmentTree> { ) -> Option<Arc<sapling::tree::NoteCommitmentTree>> {
let sapling_trees = self.db.cf_handle("sapling_note_commitment_tree").unwrap(); let sapling_trees = self.db.cf_handle("sapling_note_commitment_tree").unwrap();
self.db.zs_get(&sapling_trees, height) self.db.zs_get(&sapling_trees, height).map(Arc::new)
} }
/// Returns the Orchard note commitment tree of the finalized tip /// Returns the Orchard note commitment tree of the finalized tip
/// or the empty tree if the state is empty. /// or the empty tree if the state is empty.
pub fn orchard_note_commitment_tree(&self) -> orchard::tree::NoteCommitmentTree { pub fn orchard_note_commitment_tree(&self) -> Arc<orchard::tree::NoteCommitmentTree> {
let height = match self.finalized_tip_height() { let height = match self.finalized_tip_height() {
Some(h) => h, Some(h) => h,
None => return Default::default(), None => return Default::default(),
}; };
let orchard_note_commitment_tree = let orchard_nct_handle = self.db.cf_handle("orchard_note_commitment_tree").unwrap();
self.db.cf_handle("orchard_note_commitment_tree").unwrap();
self.db self.db
.zs_get(&orchard_note_commitment_tree, &height) .zs_get(&orchard_nct_handle, &height)
.map(Arc::new)
.expect("Orchard note commitment tree must exist if there is a finalized tip") .expect("Orchard note commitment tree must exist if there is a finalized tip")
} }
@ -151,10 +156,10 @@ impl ZebraDb {
pub fn orchard_note_commitment_tree_by_height( pub fn orchard_note_commitment_tree_by_height(
&self, &self,
height: &Height, height: &Height,
) -> Option<orchard::tree::NoteCommitmentTree> { ) -> Option<Arc<orchard::tree::NoteCommitmentTree>> {
let orchard_trees = self.db.cf_handle("orchard_note_commitment_tree").unwrap(); let orchard_trees = self.db.cf_handle("orchard_note_commitment_tree").unwrap();
self.db.zs_get(&orchard_trees, height) self.db.zs_get(&orchard_trees, height).map(Arc::new)
} }
/// Returns the shielded note commitment trees of the finalized tip /// Returns the shielded note commitment trees of the finalized tip
@ -238,21 +243,19 @@ impl DiskWriteBatch {
transaction: &Transaction, transaction: &Transaction,
note_commitment_trees: &mut NoteCommitmentTrees, note_commitment_trees: &mut NoteCommitmentTrees,
) -> Result<(), BoxError> { ) -> Result<(), BoxError> {
// Update the note commitment trees let sprout_nct = Arc::make_mut(&mut note_commitment_trees.sprout);
for sprout_note_commitment in transaction.sprout_note_commitments() { for sprout_note_commitment in transaction.sprout_note_commitments() {
note_commitment_trees sprout_nct.append(*sprout_note_commitment)?;
.sprout
.append(*sprout_note_commitment)?;
} }
let sapling_nct = Arc::make_mut(&mut note_commitment_trees.sapling);
for sapling_note_commitment in transaction.sapling_note_commitments() { for sapling_note_commitment in transaction.sapling_note_commitments() {
note_commitment_trees sapling_nct.append(*sapling_note_commitment)?;
.sapling
.append(*sapling_note_commitment)?;
} }
let orchard_nct = Arc::make_mut(&mut note_commitment_trees.orchard);
for orchard_note_commitment in transaction.orchard_note_commitments() { for orchard_note_commitment in transaction.orchard_note_commitments() {
note_commitment_trees orchard_nct.append(*orchard_note_commitment)?;
.orchard
.append(*orchard_note_commitment)?;
} }
Ok(()) Ok(())

View File

@ -385,9 +385,9 @@ impl NonFinalizedState {
fn parent_chain( fn parent_chain(
&mut self, &mut self,
parent_hash: block::Hash, parent_hash: block::Hash,
sprout_note_commitment_tree: sprout::tree::NoteCommitmentTree, sprout_note_commitment_tree: Arc<sprout::tree::NoteCommitmentTree>,
sapling_note_commitment_tree: sapling::tree::NoteCommitmentTree, sapling_note_commitment_tree: Arc<sapling::tree::NoteCommitmentTree>,
orchard_note_commitment_tree: orchard::tree::NoteCommitmentTree, orchard_note_commitment_tree: Arc<orchard::tree::NoteCommitmentTree>,
history_tree: HistoryTree, history_tree: HistoryTree,
) -> Result<Arc<Chain>, ValidateContextError> { ) -> Result<Arc<Chain>, ValidateContextError> {
match self.find_chain(|chain| chain.non_finalized_tip_hash() == parent_hash) { match self.find_chain(|chain| chain.non_finalized_tip_hash() == parent_hash) {

View File

@ -63,21 +63,23 @@ pub struct Chain {
/// The Sprout note commitment tree of the tip of this [`Chain`], /// The Sprout note commitment tree of the tip of this [`Chain`],
/// including all finalized notes, and the non-finalized notes in this chain. /// including all finalized notes, and the non-finalized notes in this chain.
pub(super) sprout_note_commitment_tree: sprout::tree::NoteCommitmentTree, pub(super) sprout_note_commitment_tree: Arc<sprout::tree::NoteCommitmentTree>,
/// The Sprout note commitment tree for each anchor. /// The Sprout note commitment tree for each anchor.
/// This is required for interstitial states. /// This is required for interstitial states.
pub(crate) sprout_trees_by_anchor: pub(crate) sprout_trees_by_anchor:
HashMap<sprout::tree::Root, sprout::tree::NoteCommitmentTree>, HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>>,
/// The Sapling note commitment tree of the tip of this [`Chain`], /// The Sapling note commitment tree of the tip of this [`Chain`],
/// including all finalized notes, and the non-finalized notes in this chain. /// including all finalized notes, and the non-finalized notes in this chain.
pub(super) sapling_note_commitment_tree: sapling::tree::NoteCommitmentTree, pub(super) sapling_note_commitment_tree: Arc<sapling::tree::NoteCommitmentTree>,
/// The Sapling note commitment tree for each height. /// The Sapling note commitment tree for each height.
pub(crate) sapling_trees_by_height: BTreeMap<block::Height, sapling::tree::NoteCommitmentTree>, pub(crate) sapling_trees_by_height:
BTreeMap<block::Height, Arc<sapling::tree::NoteCommitmentTree>>,
/// The Orchard note commitment tree of the tip of this [`Chain`], /// The Orchard note commitment tree of the tip of this [`Chain`],
/// including all finalized notes, and the non-finalized notes in this chain. /// including all finalized notes, and the non-finalized notes in this chain.
pub(super) orchard_note_commitment_tree: orchard::tree::NoteCommitmentTree, pub(super) orchard_note_commitment_tree: Arc<orchard::tree::NoteCommitmentTree>,
/// The Orchard note commitment tree for each height. /// The Orchard note commitment tree for each height.
pub(crate) orchard_trees_by_height: BTreeMap<block::Height, orchard::tree::NoteCommitmentTree>, pub(crate) orchard_trees_by_height:
BTreeMap<block::Height, Arc<orchard::tree::NoteCommitmentTree>>,
/// The ZIP-221 history tree of the tip of this [`Chain`], /// The ZIP-221 history tree of the tip of this [`Chain`],
/// including all finalized blocks, and the non-finalized `blocks` in this chain. /// including all finalized blocks, and the non-finalized `blocks` in this chain.
pub(crate) history_tree: HistoryTree, pub(crate) history_tree: HistoryTree,
@ -125,9 +127,9 @@ impl Chain {
/// Create a new Chain with the given trees and network. /// Create a new Chain with the given trees and network.
pub(crate) fn new( pub(crate) fn new(
network: Network, network: Network,
sprout_note_commitment_tree: sprout::tree::NoteCommitmentTree, sprout_note_commitment_tree: Arc<sprout::tree::NoteCommitmentTree>,
sapling_note_commitment_tree: sapling::tree::NoteCommitmentTree, sapling_note_commitment_tree: Arc<sapling::tree::NoteCommitmentTree>,
orchard_note_commitment_tree: orchard::tree::NoteCommitmentTree, orchard_note_commitment_tree: Arc<orchard::tree::NoteCommitmentTree>,
history_tree: HistoryTree, history_tree: HistoryTree,
finalized_tip_chain_value_pools: ValueBalance<NonNegative>, finalized_tip_chain_value_pools: ValueBalance<NonNegative>,
) -> Self { ) -> Self {
@ -264,15 +266,16 @@ impl Chain {
/// Fork a chain at the block with the given hash, if it is part of this /// Fork a chain at the block with the given hash, if it is part of this
/// chain. /// chain.
/// ///
/// The trees must match the trees of the finalized tip and are used /// The passed trees must match the trees of the finalized tip. They are
/// to rebuild them after the fork. /// extended by the commitments from the newly forked chain up to the passed
/// `fork_tip`.
#[allow(clippy::unwrap_in_result)] #[allow(clippy::unwrap_in_result)]
pub fn fork( pub fn fork(
&self, &self,
fork_tip: block::Hash, fork_tip: block::Hash,
sprout_note_commitment_tree: sprout::tree::NoteCommitmentTree, sprout_note_commitment_tree: Arc<sprout::tree::NoteCommitmentTree>,
sapling_note_commitment_tree: sapling::tree::NoteCommitmentTree, sapling_note_commitment_tree: Arc<sapling::tree::NoteCommitmentTree>,
orchard_note_commitment_tree: orchard::tree::NoteCommitmentTree, orchard_note_commitment_tree: Arc<orchard::tree::NoteCommitmentTree>,
history_tree: HistoryTree, history_tree: HistoryTree,
) -> Result<Option<Self>, ValidateContextError> { ) -> Result<Option<Self>, ValidateContextError> {
if !self.height_by_hash.contains_key(&fork_tip) { if !self.height_by_hash.contains_key(&fork_tip) {
@ -290,29 +293,27 @@ impl Chain {
forked.pop_tip(); forked.pop_tip();
} }
let sprout_nct = Arc::make_mut(&mut forked.sprout_note_commitment_tree);
let sapling_nct = Arc::make_mut(&mut forked.sapling_note_commitment_tree);
let orchard_nct = Arc::make_mut(&mut forked.orchard_note_commitment_tree);
// Rebuild the note commitment trees, starting from the finalized tip tree. // Rebuild the note commitment trees, starting from the finalized tip tree.
// TODO: change to a more efficient approach by removing nodes
// from the tree of the original chain (in [`Self::pop_tip`]).
// See https://github.com/ZcashFoundation/zebra/issues/2378
for block in forked.blocks.values() { for block in forked.blocks.values() {
for transaction in block.block.transactions.iter() { for transaction in block.block.transactions.iter() {
for sprout_note_commitment in transaction.sprout_note_commitments() { for sprout_note_commitment in transaction.sprout_note_commitments() {
forked sprout_nct
.sprout_note_commitment_tree
.append(*sprout_note_commitment) .append(*sprout_note_commitment)
.expect("must work since it was already appended before the fork"); .expect("must work since it was already appended before the fork");
} }
for sapling_note_commitment in transaction.sapling_note_commitments() { for sapling_note_commitment in transaction.sapling_note_commitments() {
forked sapling_nct
.sapling_note_commitment_tree
.append(*sapling_note_commitment) .append(*sapling_note_commitment)
.expect("must work since it was already appended before the fork"); .expect("must work since it was already appended before the fork");
} }
for orchard_note_commitment in transaction.orchard_note_commitments() { for orchard_note_commitment in transaction.orchard_note_commitments() {
forked orchard_nct
.orchard_note_commitment_tree
.append(*orchard_note_commitment) .append(*orchard_note_commitment)
.expect("must work since it was already appended before the fork"); .expect("must work since it was already appended before the fork");
} }
@ -401,11 +402,11 @@ impl Chain {
pub fn sapling_tree( pub fn sapling_tree(
&self, &self,
hash_or_height: HashOrHeight, hash_or_height: HashOrHeight,
) -> Option<&sapling::tree::NoteCommitmentTree> { ) -> Option<Arc<sapling::tree::NoteCommitmentTree>> {
let height = let height =
hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?; hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
self.sapling_trees_by_height.get(&height) self.sapling_trees_by_height.get(&height).cloned()
} }
/// Returns the Orchard /// Returns the Orchard
@ -414,11 +415,11 @@ impl Chain {
pub fn orchard_tree( pub fn orchard_tree(
&self, &self,
hash_or_height: HashOrHeight, hash_or_height: HashOrHeight,
) -> Option<&orchard::tree::NoteCommitmentTree> { ) -> Option<Arc<orchard::tree::NoteCommitmentTree>> {
let height = let height =
hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?; hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?;
self.orchard_trees_by_height.get(&height) self.orchard_trees_by_height.get(&height).cloned()
} }
/// Returns the block hash of the tip block. /// Returns the block hash of the tip block.
@ -639,9 +640,9 @@ impl Chain {
/// Useful when forking, where the trees are rebuilt anyway. /// Useful when forking, where the trees are rebuilt anyway.
fn with_trees( fn with_trees(
&self, &self,
sprout_note_commitment_tree: sprout::tree::NoteCommitmentTree, sprout_note_commitment_tree: Arc<sprout::tree::NoteCommitmentTree>,
sapling_note_commitment_tree: sapling::tree::NoteCommitmentTree, sapling_note_commitment_tree: Arc<sapling::tree::NoteCommitmentTree>,
orchard_note_commitment_tree: orchard::tree::NoteCommitmentTree, orchard_note_commitment_tree: Arc<orchard::tree::NoteCommitmentTree>,
history_tree: HistoryTree, history_tree: HistoryTree,
) -> Self { ) -> Self {
Chain { Chain {
@ -1203,8 +1204,10 @@ impl UpdateWith<Option<transaction::JoinSplitData<Groth16Proof>>> for Chain {
joinsplit_data: &Option<transaction::JoinSplitData<Groth16Proof>>, joinsplit_data: &Option<transaction::JoinSplitData<Groth16Proof>>,
) -> Result<(), ValidateContextError> { ) -> Result<(), ValidateContextError> {
if let Some(joinsplit_data) = joinsplit_data { if let Some(joinsplit_data) = joinsplit_data {
let sprout_ncm = Arc::make_mut(&mut self.sprout_note_commitment_tree);
for cm in joinsplit_data.note_commitments() { for cm in joinsplit_data.note_commitments() {
self.sprout_note_commitment_tree.append(*cm)?; sprout_ncm.append(*cm)?;
} }
check::nullifier::add_to_non_finalized_chain_unique( check::nullifier::add_to_non_finalized_chain_unique(
@ -1245,11 +1248,13 @@ where
sapling_shielded_data: &Option<sapling::ShieldedData<AnchorV>>, sapling_shielded_data: &Option<sapling::ShieldedData<AnchorV>>,
) -> Result<(), ValidateContextError> { ) -> Result<(), ValidateContextError> {
if let Some(sapling_shielded_data) = sapling_shielded_data { if let Some(sapling_shielded_data) = sapling_shielded_data {
let sapling_nct = Arc::make_mut(&mut self.sapling_note_commitment_tree);
// The `_u` here indicates that the Sapling note commitment is // The `_u` here indicates that the Sapling note commitment is
// specified only by the `u`-coordinate of the Jubjub curve // specified only by the `u`-coordinate of the Jubjub curve
// point `(u, v)`. // point `(u, v)`.
for cm_u in sapling_shielded_data.note_commitments() { for cm_u in sapling_shielded_data.note_commitments() {
self.sapling_note_commitment_tree.append(*cm_u)?; sapling_nct.append(*cm_u)?;
} }
check::nullifier::add_to_non_finalized_chain_unique( check::nullifier::add_to_non_finalized_chain_unique(
@ -1291,8 +1296,10 @@ impl UpdateWith<Option<orchard::ShieldedData>> for Chain {
orchard_shielded_data: &Option<orchard::ShieldedData>, orchard_shielded_data: &Option<orchard::ShieldedData>,
) -> Result<(), ValidateContextError> { ) -> Result<(), ValidateContextError> {
if let Some(orchard_shielded_data) = orchard_shielded_data { if let Some(orchard_shielded_data) = orchard_shielded_data {
let orchard_nct = Arc::make_mut(&mut self.orchard_note_commitment_tree);
for cm_x in orchard_shielded_data.note_commitments() { for cm_x in orchard_shielded_data.note_commitments() {
self.orchard_note_commitment_tree.append(*cm_x)?; orchard_nct.append(*cm_x)?;
} }
check::nullifier::add_to_non_finalized_chain_unique( check::nullifier::add_to_non_finalized_chain_unique(

View File

@ -97,7 +97,6 @@ where
// we check the most efficient alternative first. (`chain` is always in // we check the most efficient alternative first. (`chain` is always in
// memory, but `db` stores transactions on disk, with a memory cache.) // memory, but `db` stores transactions on disk, with a memory cache.)
chain chain
.as_ref()
.and_then(|chain| { .and_then(|chain| {
chain chain
.as_ref() .as_ref()
@ -128,9 +127,7 @@ where
// state, we check the most efficient alternative first. (`chain` is always // state, we check the most efficient alternative first. (`chain` is always
// in memory, but `db` stores blocks on disk, with a memory cache.) // in memory, but `db` stores blocks on disk, with a memory cache.)
chain chain
.as_ref() .and_then(|chain| chain.as_ref().sapling_tree(hash_or_height))
.and_then(|chain| chain.as_ref().sapling_tree(hash_or_height).cloned())
.map(Arc::new)
.or_else(|| db.sapling_tree(hash_or_height)) .or_else(|| db.sapling_tree(hash_or_height))
} }
@ -155,9 +152,7 @@ where
// state, we check the most efficient alternative first. (`chain` is always // state, we check the most efficient alternative first. (`chain` is always
// in memory, but `db` stores blocks on disk, with a memory cache.) // in memory, but `db` stores blocks on disk, with a memory cache.)
chain chain
.as_ref() .and_then(|chain| chain.as_ref().orchard_tree(hash_or_height))
.and_then(|chain| chain.as_ref().orchard_tree(hash_or_height).cloned())
.map(Arc::new)
.or_else(|| db.orchard_tree(hash_or_height)) .or_else(|| db.orchard_tree(hash_or_height))
} }