use std::{collections::BTreeMap, sync::Arc};
use zebra_chain::{
orchard, sapling,
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
};
use crate::{
service::{finalized_state::ZebraDb, non_finalized_state::Chain},
HashOrHeight,
};
#[allow(unused_imports)]
use zebra_chain::subtree::NoteCommitmentSubtree;
pub fn sapling_tree<C>(
chain: Option<C>,
db: &ZebraDb,
hash_or_height: HashOrHeight,
) -> Option<Arc<sapling::tree::NoteCommitmentTree>>
where
C: AsRef<Chain>,
{
chain
.and_then(|chain| chain.as_ref().sapling_tree(hash_or_height))
.or_else(|| db.sapling_tree_by_hash_or_height(hash_or_height))
}
pub fn sapling_subtrees<C>(
chain: Option<C>,
db: &ZebraDb,
range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex> + Clone,
) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<sapling_crypto::Node>>
where
C: AsRef<Chain>,
{
subtrees(
chain,
range,
|chain, range| chain.sapling_subtrees_in_range(range),
|range| db.sapling_subtree_list_by_index_range(range),
)
}
pub fn orchard_tree<C>(
chain: Option<C>,
db: &ZebraDb,
hash_or_height: HashOrHeight,
) -> Option<Arc<orchard::tree::NoteCommitmentTree>>
where
C: AsRef<Chain>,
{
chain
.and_then(|chain| chain.as_ref().orchard_tree(hash_or_height))
.or_else(|| db.orchard_tree_by_hash_or_height(hash_or_height))
}
pub fn orchard_subtrees<C>(
chain: Option<C>,
db: &ZebraDb,
range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex> + Clone,
) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>>
where
C: AsRef<Chain>,
{
subtrees(
chain,
range,
|chain, range| chain.orchard_subtrees_in_range(range),
|range| db.orchard_subtree_list_by_index_range(range),
)
}
fn subtrees<C, Range, Node, ChainSubtreeFn, DbSubtreeFn>(
chain: Option<C>,
range: Range,
read_chain: ChainSubtreeFn,
read_disk: DbSubtreeFn,
) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<Node>>
where
C: AsRef<Chain>,
Node: PartialEq,
Range: std::ops::RangeBounds<NoteCommitmentSubtreeIndex> + Clone,
ChainSubtreeFn: FnOnce(
&Chain,
Range,
)
-> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<Node>>,
DbSubtreeFn:
FnOnce(Range) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<Node>>,
{
use std::ops::Bound::*;
let start_index = match range.start_bound().cloned() {
Included(start_index) => start_index,
Excluded(start_index) => (start_index.0 + 1).into(),
Unbounded => 0.into(),
};
let results = match chain.map(|chain| read_chain(chain.as_ref(), range.clone())) {
Some(chain_results) if chain_results.contains_key(&start_index) => return chain_results,
Some(chain_results) => {
let mut db_results = read_disk(range);
for (chain_index, chain_subtree) in chain_results {
let Some(db_subtree) = db_results.get(&chain_index) else {
db_results.insert(chain_index, chain_subtree);
continue;
};
if &chain_subtree != db_subtree {
break;
}
}
db_results
}
None => read_disk(range),
};
if results.contains_key(&start_index) {
results
} else {
BTreeMap::new()
}
}
pub fn history_tree<C>(
chain: Option<C>,
db: &ZebraDb,
hash_or_height: HashOrHeight,
) -> Option<Arc<zebra_chain::history_tree::HistoryTree>>
where
C: AsRef<Chain>,
{
chain
.and_then(|chain| chain.as_ref().history_tree(hash_or_height))
.or_else(|| Some(db.history_tree()))
}