1use std::{
16 collections::{BTreeMap, HashMap},
17 sync::Arc,
18};
19
20use zebra_chain::{
21 block::Height,
22 ironwood, orchard,
23 parallel::tree::NoteCommitmentTrees,
24 parameters::NetworkUpgrade,
25 sapling, sprout,
26 subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
27 transaction::Transaction,
28};
29
30use crate::{
31 request::{FinalizedBlock, Treestate},
32 service::finalized_state::{
33 disk_db::{DiskWriteBatch, ReadDisk, WriteDisk},
34 disk_format::RawBytes,
35 zebra_db::ZebraDb,
36 },
37 TransactionLocation,
38};
39
40#[allow(unused_imports)]
42use zebra_chain::subtree::NoteCommitmentSubtree;
43
44impl ZebraDb {
45 pub fn contains_sprout_nullifier(&self, sprout_nullifier: &sprout::Nullifier) -> bool {
49 let sprout_nullifiers = self.db.cf_handle("sprout_nullifiers").unwrap();
50 self.db.zs_contains(&sprout_nullifiers, &sprout_nullifier)
51 }
52
53 pub fn contains_sapling_nullifier(&self, sapling_nullifier: &sapling::Nullifier) -> bool {
55 let sapling_nullifiers = self.db.cf_handle("sapling_nullifiers").unwrap();
56 self.db.zs_contains(&sapling_nullifiers, &sapling_nullifier)
57 }
58
59 pub fn contains_orchard_nullifier(&self, orchard_nullifier: &orchard::Nullifier) -> bool {
61 let orchard_nullifiers = self.db.cf_handle("orchard_nullifiers").unwrap();
62 self.db.zs_contains(&orchard_nullifiers, &orchard_nullifier)
63 }
64
65 pub fn contains_ironwood_nullifier(&self, ironwood_nullifier: &ironwood::Nullifier) -> bool {
67 let ironwood_nullifiers = self.db.cf_handle("ironwood_nullifiers").unwrap();
68 self.db
69 .zs_contains(&ironwood_nullifiers, &ironwood_nullifier)
70 }
71
72 #[allow(clippy::unwrap_in_result)]
76 pub fn sprout_revealing_tx_loc(
77 &self,
78 sprout_nullifier: &sprout::Nullifier,
79 ) -> Option<TransactionLocation> {
80 let sprout_nullifiers = self.db.cf_handle("sprout_nullifiers").unwrap();
81 self.db.zs_get(&sprout_nullifiers, &sprout_nullifier)?
82 }
83
84 #[allow(clippy::unwrap_in_result)]
88 pub fn sapling_revealing_tx_loc(
89 &self,
90 sapling_nullifier: &sapling::Nullifier,
91 ) -> Option<TransactionLocation> {
92 let sapling_nullifiers = self.db.cf_handle("sapling_nullifiers").unwrap();
93 self.db.zs_get(&sapling_nullifiers, &sapling_nullifier)?
94 }
95
96 #[allow(clippy::unwrap_in_result)]
100 pub fn orchard_revealing_tx_loc(
101 &self,
102 orchard_nullifier: &orchard::Nullifier,
103 ) -> Option<TransactionLocation> {
104 let orchard_nullifiers = self.db.cf_handle("orchard_nullifiers").unwrap();
105 self.db.zs_get(&orchard_nullifiers, &orchard_nullifier)?
106 }
107
108 #[allow(clippy::unwrap_in_result)]
112 pub fn ironwood_revealing_tx_loc(
113 &self,
114 ironwood_nullifier: &ironwood::Nullifier,
115 ) -> Option<TransactionLocation> {
116 let ironwood_nullifiers = self.db.cf_handle("ironwood_nullifiers").unwrap();
117 self.db.zs_get(&ironwood_nullifiers, &ironwood_nullifier)?
118 }
119
120 #[allow(dead_code)]
122 pub fn contains_sprout_anchor(&self, sprout_anchor: &sprout::tree::Root) -> bool {
123 let sprout_anchors = self.db.cf_handle("sprout_anchors").unwrap();
124 self.db.zs_contains(&sprout_anchors, &sprout_anchor)
125 }
126
127 pub fn contains_sapling_anchor(&self, sapling_anchor: &sapling::tree::Root) -> bool {
129 let sapling_anchors = self.db.cf_handle("sapling_anchors").unwrap();
130 self.db.zs_contains(&sapling_anchors, &sapling_anchor)
131 }
132
133 pub fn contains_orchard_anchor(&self, orchard_anchor: &orchard::tree::Root) -> bool {
135 let orchard_anchors = self.db.cf_handle("orchard_anchors").unwrap();
136 self.db.zs_contains(&orchard_anchors, &orchard_anchor)
137 }
138
139 pub fn contains_ironwood_anchor(&self, ironwood_anchor: &orchard::tree::Root) -> bool {
143 let ironwood_anchors = self.db.cf_handle("ironwood_anchors").unwrap();
144 self.db.zs_contains(&ironwood_anchors, &ironwood_anchor)
145 }
146
147 pub fn sprout_tree_for_tip(&self) -> Arc<sprout::tree::NoteCommitmentTree> {
152 if self.is_empty() {
153 return Arc::<sprout::tree::NoteCommitmentTree>::default();
154 }
155
156 let sprout_tree_cf = self.db.cf_handle("sprout_note_commitment_tree").unwrap();
157
158 let mut sprout_tree: Option<Arc<sprout::tree::NoteCommitmentTree>> =
173 self.db.zs_get(&sprout_tree_cf, &());
174
175 if sprout_tree.is_none() {
176 sprout_tree = self
180 .db
181 .zs_last_key_value(&sprout_tree_cf)
182 .map(|(_key, tree_value): (Height, _)| tree_value);
183 }
184
185 sprout_tree.expect("Sprout note commitment tree must exist if there is a finalized tip")
186 }
187
188 #[allow(clippy::unwrap_in_result)]
192 pub fn sprout_tree_by_anchor(
193 &self,
194 sprout_anchor: &sprout::tree::Root,
195 ) -> Option<Arc<sprout::tree::NoteCommitmentTree>> {
196 let sprout_anchors_handle = self.db.cf_handle("sprout_anchors").unwrap();
197
198 self.db
199 .zs_get(&sprout_anchors_handle, sprout_anchor)
200 .map(Arc::new)
201 }
202
203 #[allow(dead_code)]
207 pub fn sprout_trees_full_map(
208 &self,
209 ) -> HashMap<sprout::tree::Root, Arc<sprout::tree::NoteCommitmentTree>> {
210 let sprout_anchors_handle = self.db.cf_handle("sprout_anchors").unwrap();
211
212 self.db
213 .zs_items_in_range_unordered(&sprout_anchors_handle, ..)
214 }
215
216 pub fn sprout_trees_full_tip(
219 &self,
220 ) -> impl Iterator<Item = (RawBytes, Arc<sprout::tree::NoteCommitmentTree>)> + '_ {
221 let sprout_trees = self.db.cf_handle("sprout_note_commitment_tree").unwrap();
222 self.db.zs_forward_range_iter(&sprout_trees, ..)
223 }
224
225 pub fn sapling_tree_for_tip(&self) -> Arc<sapling::tree::NoteCommitmentTree> {
230 let height = match self.finalized_tip_height() {
231 Some(h) => h,
232 None => return Default::default(),
233 };
234
235 self.sapling_tree_by_height(&height)
236 .expect("Sapling note commitment tree must exist if there is a finalized tip")
237 }
238
239 #[allow(clippy::unwrap_in_result)]
242 pub fn sapling_tree_by_height(
243 &self,
244 height: &Height,
245 ) -> Option<Arc<sapling::tree::NoteCommitmentTree>> {
246 let tip_height = self.finalized_tip_height()?;
247
248 if *height > tip_height {
251 return None;
252 }
253
254 let sapling_trees = self.db.cf_handle("sapling_note_commitment_tree").unwrap();
255
256 let (_first_duplicate_height, tree) = self
258 .db
259 .zs_prev_key_value_back_from(&sapling_trees, height)
260 .expect(
261 "Sapling note commitment trees must exist for all heights below the finalized tip",
262 );
263
264 Some(Arc::new(tree))
265 }
266
267 pub fn sapling_tree_by_height_range<R>(
269 &self,
270 range: R,
271 ) -> impl Iterator<Item = (Height, Arc<sapling::tree::NoteCommitmentTree>)> + '_
272 where
273 R: std::ops::RangeBounds<Height>,
274 {
275 let sapling_trees = self.db.cf_handle("sapling_note_commitment_tree").unwrap();
276 self.db.zs_forward_range_iter(&sapling_trees, range)
277 }
278
279 pub fn sapling_tree_by_reversed_height_range<R>(
281 &self,
282 range: R,
283 ) -> impl Iterator<Item = (Height, Arc<sapling::tree::NoteCommitmentTree>)> + '_
284 where
285 R: std::ops::RangeBounds<Height>,
286 {
287 let sapling_trees = self.db.cf_handle("sapling_note_commitment_tree").unwrap();
288 self.db.zs_reverse_range_iter(&sapling_trees, range)
289 }
290
291 #[allow(clippy::unwrap_in_result)]
299 pub(in super::super) fn sapling_subtree_by_index(
300 &self,
301 index: impl Into<NoteCommitmentSubtreeIndex> + Copy,
302 ) -> Option<NoteCommitmentSubtree<sapling_crypto::Node>> {
303 let sapling_subtrees = self
304 .db
305 .cf_handle("sapling_note_commitment_subtree")
306 .unwrap();
307
308 let subtree_data: NoteCommitmentSubtreeData<sapling_crypto::Node> =
309 self.db.zs_get(&sapling_subtrees, &index.into())?;
310
311 Some(subtree_data.with_index(index))
312 }
313
314 #[allow(clippy::unwrap_in_result)]
316 pub fn sapling_subtree_list_by_index_range(
317 &self,
318 range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex>,
319 ) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<sapling_crypto::Node>> {
320 let sapling_subtrees = self
321 .db
322 .cf_handle("sapling_note_commitment_subtree")
323 .unwrap();
324
325 self.db
326 .zs_forward_range_iter(&sapling_subtrees, range)
327 .collect()
328 }
329
330 #[allow(clippy::unwrap_in_result)]
332 fn sapling_subtree_for_tip(&self) -> Option<NoteCommitmentSubtree<sapling_crypto::Node>> {
333 let sapling_subtrees = self
334 .db
335 .cf_handle("sapling_note_commitment_subtree")
336 .unwrap();
337
338 let (index, subtree_data): (
339 NoteCommitmentSubtreeIndex,
340 NoteCommitmentSubtreeData<sapling_crypto::Node>,
341 ) = self.db.zs_last_key_value(&sapling_subtrees)?;
342
343 let tip_height = self.finalized_tip_height()?;
344 if subtree_data.end_height != tip_height {
345 return None;
346 }
347
348 Some(subtree_data.with_index(index))
349 }
350
351 pub fn orchard_tree_for_tip(&self) -> Arc<orchard::tree::NoteCommitmentTree> {
356 let height = match self.finalized_tip_height() {
357 Some(h) => h,
358 None => return Default::default(),
359 };
360
361 self.orchard_tree_by_height(&height)
362 .expect("Orchard note commitment tree must exist if there is a finalized tip")
363 }
364
365 #[allow(clippy::unwrap_in_result)]
368 pub fn orchard_tree_by_height(
369 &self,
370 height: &Height,
371 ) -> Option<Arc<orchard::tree::NoteCommitmentTree>> {
372 let tip_height = self.finalized_tip_height()?;
373
374 if *height > tip_height {
377 return None;
378 }
379
380 let orchard_trees = self.db.cf_handle("orchard_note_commitment_tree").unwrap();
381
382 let (_first_duplicate_height, tree) = self
384 .db
385 .zs_prev_key_value_back_from(&orchard_trees, height)
386 .expect(
387 "Orchard note commitment trees must exist for all heights below the finalized tip",
388 );
389
390 Some(Arc::new(tree))
391 }
392
393 fn tree_by_height_range<R>(
399 &self,
400 cf: &str,
401 range: R,
402 ) -> impl Iterator<Item = (Height, Arc<orchard::tree::NoteCommitmentTree>)> + '_
403 where
404 R: std::ops::RangeBounds<Height>,
405 {
406 let trees = self.db.cf_handle(cf).unwrap();
407 self.db.zs_forward_range_iter(&trees, range)
408 }
409
410 fn subtree_list_by_index_range(
412 &self,
413 cf: &str,
414 range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex>,
415 ) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>> {
416 let subtrees = self.db.cf_handle(cf).unwrap();
417 self.db.zs_forward_range_iter(&subtrees, range).collect()
418 }
419
420 #[allow(clippy::unwrap_in_result)]
422 fn subtree_for_tip(&self, cf: &str) -> Option<NoteCommitmentSubtree<orchard::tree::Node>> {
423 let subtrees = self.db.cf_handle(cf).unwrap();
424
425 let (index, subtree_data): (
426 NoteCommitmentSubtreeIndex,
427 NoteCommitmentSubtreeData<orchard::tree::Node>,
428 ) = self.db.zs_last_key_value(&subtrees)?;
429
430 let tip_height = self.finalized_tip_height()?;
431 if subtree_data.end_height != tip_height {
432 return None;
433 }
434
435 Some(subtree_data.with_index(index))
436 }
437
438 pub fn orchard_tree_by_height_range<R>(
440 &self,
441 range: R,
442 ) -> impl Iterator<Item = (Height, Arc<orchard::tree::NoteCommitmentTree>)> + '_
443 where
444 R: std::ops::RangeBounds<Height>,
445 {
446 self.tree_by_height_range("orchard_note_commitment_tree", range)
447 }
448
449 pub fn orchard_tree_by_reversed_height_range<R>(
451 &self,
452 range: R,
453 ) -> impl Iterator<Item = (Height, Arc<orchard::tree::NoteCommitmentTree>)> + '_
454 where
455 R: std::ops::RangeBounds<Height>,
456 {
457 let orchard_trees = self.db.cf_handle("orchard_note_commitment_tree").unwrap();
458 self.db.zs_reverse_range_iter(&orchard_trees, range)
459 }
460
461 #[allow(clippy::unwrap_in_result)]
469 pub(in super::super) fn orchard_subtree_by_index(
470 &self,
471 index: impl Into<NoteCommitmentSubtreeIndex> + Copy,
472 ) -> Option<NoteCommitmentSubtree<orchard::tree::Node>> {
473 let orchard_subtrees = self
474 .db
475 .cf_handle("orchard_note_commitment_subtree")
476 .unwrap();
477
478 let subtree_data: NoteCommitmentSubtreeData<orchard::tree::Node> =
479 self.db.zs_get(&orchard_subtrees, &index.into())?;
480
481 Some(subtree_data.with_index(index))
482 }
483
484 pub fn orchard_subtree_list_by_index_range(
486 &self,
487 range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex>,
488 ) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>> {
489 self.subtree_list_by_index_range("orchard_note_commitment_subtree", range)
490 }
491
492 fn orchard_subtree_for_tip(&self) -> Option<NoteCommitmentSubtree<orchard::tree::Node>> {
494 self.subtree_for_tip("orchard_note_commitment_subtree")
495 }
496
497 pub fn ironwood_tree_for_tip(&self) -> Arc<orchard::tree::NoteCommitmentTree> {
505 let height = match self.finalized_tip_height() {
506 Some(h) => h,
507 None => return Default::default(),
508 };
509
510 self.ironwood_tree_by_height(&height)
511 .expect("Ironwood note commitment tree must exist if there is a finalized tip")
512 }
513
514 #[allow(clippy::unwrap_in_result)]
517 pub fn ironwood_tree_by_height(
518 &self,
519 height: &Height,
520 ) -> Option<Arc<orchard::tree::NoteCommitmentTree>> {
521 let tip_height = self.finalized_tip_height()?;
522
523 if *height > tip_height {
526 return None;
527 }
528
529 let ironwood_trees = self.db.cf_handle("ironwood_note_commitment_tree").unwrap();
530
531 let nu6_3_activated = NetworkUpgrade::Nu6_3
543 .activation_height(&self.network())
544 .is_some_and(|activation| *height >= activation);
545
546 match self.db.zs_prev_key_value_back_from(&ironwood_trees, height) {
548 Some((_first_duplicate_height, tree)) => Some(Arc::new(tree)),
549 None if !nu6_3_activated => Some(Default::default()),
550 None => panic!(
551 "Ironwood note commitment tree must exist for all heights at or below the finalized \
552 tip from NU6.3 activation onward"
553 ),
554 }
555 }
556
557 pub fn ironwood_tree_by_height_range<R>(
559 &self,
560 range: R,
561 ) -> impl Iterator<Item = (Height, Arc<orchard::tree::NoteCommitmentTree>)> + '_
562 where
563 R: std::ops::RangeBounds<Height>,
564 {
565 self.tree_by_height_range("ironwood_note_commitment_tree", range)
566 }
567
568 pub fn ironwood_subtree_list_by_index_range(
570 &self,
571 range: impl std::ops::RangeBounds<NoteCommitmentSubtreeIndex>,
572 ) -> BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>> {
573 self.subtree_list_by_index_range("ironwood_note_commitment_subtree", range)
574 }
575
576 fn ironwood_subtree_for_tip(&self) -> Option<NoteCommitmentSubtree<orchard::tree::Node>> {
578 self.subtree_for_tip("ironwood_note_commitment_subtree")
579 }
580
581 pub fn note_commitment_trees_for_tip(&self) -> NoteCommitmentTrees {
586 NoteCommitmentTrees {
587 sprout: self.sprout_tree_for_tip(),
588 sapling: self.sapling_tree_for_tip(),
589 sapling_subtree: self.sapling_subtree_for_tip(),
590 orchard: self.orchard_tree_for_tip(),
591 orchard_subtree: self.orchard_subtree_for_tip(),
592 ironwood: self.ironwood_tree_for_tip(),
593 ironwood_subtree: self.ironwood_subtree_for_tip(),
594 }
595 }
596}
597
598impl DiskWriteBatch {
599 pub fn prepare_shielded_transaction_batch(
602 &mut self,
603 zebra_db: &ZebraDb,
604 finalized: &FinalizedBlock,
605 ) {
606 #[cfg(feature = "indexer")]
607 let FinalizedBlock { block, height, .. } = finalized;
608
609 #[cfg(feature = "indexer")]
611 for (tx_index, transaction) in block.transactions.iter().enumerate() {
612 let tx_loc = TransactionLocation::from_usize(*height, tx_index);
613 self.prepare_nullifier_batch(zebra_db, transaction, tx_loc);
614 }
615
616 #[cfg(not(feature = "indexer"))]
617 for transaction in &finalized.block.transactions {
618 self.prepare_nullifier_batch(zebra_db, transaction);
619 }
620 }
621
622 #[allow(clippy::unwrap_in_result)]
629 pub fn prepare_nullifier_batch(
630 &mut self,
631 zebra_db: &ZebraDb,
632 transaction: &Transaction,
633 #[cfg(feature = "indexer")] transaction_location: TransactionLocation,
634 ) {
635 let db = &zebra_db.db;
636 let sprout_nullifiers = db.cf_handle("sprout_nullifiers").unwrap();
637 let sapling_nullifiers = db.cf_handle("sapling_nullifiers").unwrap();
638 let orchard_nullifiers = db.cf_handle("orchard_nullifiers").unwrap();
639 let ironwood_nullifiers = db.cf_handle("ironwood_nullifiers").unwrap();
640
641 #[cfg(feature = "indexer")]
642 let insert_value = transaction_location;
643 #[cfg(not(feature = "indexer"))]
644 let insert_value = ();
645
646 for nullifier in transaction.sprout_nullifiers() {
648 self.zs_insert(&sprout_nullifiers, nullifier, insert_value);
649 }
650 for nullifier in transaction.sapling_nullifiers() {
651 self.zs_insert(&sapling_nullifiers, nullifier, insert_value);
652 }
653 for nullifier in transaction.orchard_nullifiers() {
654 self.zs_insert(&orchard_nullifiers, nullifier, insert_value);
655 }
656 for ironwood_nullifier in transaction.ironwood_nullifiers() {
657 self.zs_insert(&ironwood_nullifiers, ironwood_nullifier, insert_value);
658 }
659 }
660
661 pub fn prepare_trees_batch(
664 &mut self,
665 zebra_db: &ZebraDb,
666 finalized: &FinalizedBlock,
667 prev_note_commitment_trees: Option<NoteCommitmentTrees>,
668 ) {
669 let FinalizedBlock {
670 height,
671 treestate:
672 Treestate {
673 note_commitment_trees,
674 history_tree,
675 },
676 ..
677 } = finalized;
678
679 let prev_sprout_tree = prev_note_commitment_trees.as_ref().map_or_else(
680 || zebra_db.sprout_tree_for_tip(),
681 |prev_trees| prev_trees.sprout.clone(),
682 );
683 let prev_sapling_tree = prev_note_commitment_trees.as_ref().map_or_else(
684 || zebra_db.sapling_tree_for_tip(),
685 |prev_trees| prev_trees.sapling.clone(),
686 );
687 let prev_orchard_tree = prev_note_commitment_trees.as_ref().map_or_else(
688 || zebra_db.orchard_tree_for_tip(),
689 |prev_trees| prev_trees.orchard.clone(),
690 );
691 let prev_ironwood_tree = prev_note_commitment_trees.as_ref().map_or_else(
692 || zebra_db.ironwood_tree_for_tip(),
693 |prev_trees| prev_trees.ironwood.clone(),
694 );
695
696 if height.is_min() || prev_sprout_tree != note_commitment_trees.sprout {
698 self.update_sprout_tree(zebra_db, ¬e_commitment_trees.sprout)
699 }
700
701 if height.is_min() || prev_sapling_tree != note_commitment_trees.sapling {
703 self.create_sapling_tree(zebra_db, height, ¬e_commitment_trees.sapling);
704
705 if let Some(subtree) = note_commitment_trees.sapling_subtree {
706 self.insert_sapling_subtree(zebra_db, &subtree);
707 }
708 }
709
710 if height.is_min() || prev_orchard_tree != note_commitment_trees.orchard {
712 self.create_orchard_tree(zebra_db, height, ¬e_commitment_trees.orchard);
713
714 if let Some(subtree) = note_commitment_trees.orchard_subtree {
715 self.insert_orchard_subtree(zebra_db, &subtree);
716 }
717 }
718
719 if height.is_min() || prev_ironwood_tree != note_commitment_trees.ironwood {
721 self.create_ironwood_tree(zebra_db, height, ¬e_commitment_trees.ironwood);
722
723 if let Some(subtree) = note_commitment_trees.ironwood_subtree {
724 self.insert_ironwood_subtree(zebra_db, &subtree);
725 }
726 }
727
728 self.update_history_tree(zebra_db, history_tree);
729 }
730
731 pub fn update_sprout_tree(
735 &mut self,
736 zebra_db: &ZebraDb,
737 tree: &sprout::tree::NoteCommitmentTree,
738 ) {
739 let sprout_anchors = zebra_db.db.cf_handle("sprout_anchors").unwrap();
740 let sprout_tree_cf = zebra_db
741 .db
742 .cf_handle("sprout_note_commitment_tree")
743 .unwrap();
744
745 self.zs_insert(&sprout_anchors, tree.root(), tree);
748 self.zs_insert(&sprout_tree_cf, (), tree);
749 }
750
751 pub fn delete_range_sprout_tree(&mut self, zebra_db: &ZebraDb, from: &Height, to: &Height) {
757 let sprout_tree_cf = zebra_db
758 .db
759 .cf_handle("sprout_note_commitment_tree")
760 .unwrap();
761
762 self.zs_delete_range(&sprout_tree_cf, from, to);
764 }
765
766 #[allow(dead_code)]
768 pub fn delete_sprout_anchor(&mut self, zebra_db: &ZebraDb, anchor: &sprout::tree::Root) {
769 let sprout_anchors = zebra_db.db.cf_handle("sprout_anchors").unwrap();
770 self.zs_delete(&sprout_anchors, anchor);
771 }
772
773 pub fn create_sapling_tree(
778 &mut self,
779 zebra_db: &ZebraDb,
780 height: &Height,
781 tree: &sapling::tree::NoteCommitmentTree,
782 ) {
783 let sapling_anchors = zebra_db.db.cf_handle("sapling_anchors").unwrap();
784 let sapling_tree_cf = zebra_db
785 .db
786 .cf_handle("sapling_note_commitment_tree")
787 .unwrap();
788
789 self.zs_insert(&sapling_anchors, tree.root(), ());
790 self.zs_insert(&sapling_tree_cf, height, tree);
791 }
792
793 pub fn insert_sapling_subtree(
795 &mut self,
796 zebra_db: &ZebraDb,
797 subtree: &NoteCommitmentSubtree<sapling_crypto::Node>,
798 ) {
799 let sapling_subtree_cf = zebra_db
800 .db
801 .cf_handle("sapling_note_commitment_subtree")
802 .unwrap();
803 self.zs_insert(&sapling_subtree_cf, subtree.index, subtree.into_data());
804 }
805
806 pub fn delete_sapling_tree(&mut self, zebra_db: &ZebraDb, height: &Height) {
808 let sapling_tree_cf = zebra_db
809 .db
810 .cf_handle("sapling_note_commitment_tree")
811 .unwrap();
812 self.zs_delete(&sapling_tree_cf, height);
813 }
814
815 #[allow(dead_code)]
818 pub fn delete_range_sapling_tree(&mut self, zebra_db: &ZebraDb, from: &Height, to: &Height) {
819 let sapling_tree_cf = zebra_db
820 .db
821 .cf_handle("sapling_note_commitment_tree")
822 .unwrap();
823
824 self.zs_delete_range(&sapling_tree_cf, from, to);
826 }
827
828 #[allow(dead_code)]
830 pub fn delete_sapling_anchor(&mut self, zebra_db: &ZebraDb, anchor: &sapling::tree::Root) {
831 let sapling_anchors = zebra_db.db.cf_handle("sapling_anchors").unwrap();
832 self.zs_delete(&sapling_anchors, anchor);
833 }
834
835 pub fn delete_range_sapling_subtree(
838 &mut self,
839 zebra_db: &ZebraDb,
840 from: NoteCommitmentSubtreeIndex,
841 to: NoteCommitmentSubtreeIndex,
842 ) {
843 let sapling_subtree_cf = zebra_db
844 .db
845 .cf_handle("sapling_note_commitment_subtree")
846 .unwrap();
847
848 self.zs_delete_range(&sapling_subtree_cf, from, to);
850 }
851
852 pub fn create_orchard_tree(
857 &mut self,
858 zebra_db: &ZebraDb,
859 height: &Height,
860 tree: &orchard::tree::NoteCommitmentTree,
861 ) {
862 self.create_note_commitment_tree(
863 zebra_db,
864 "orchard_anchors",
865 "orchard_note_commitment_tree",
866 height,
867 tree,
868 );
869 }
870
871 pub fn insert_orchard_subtree(
873 &mut self,
874 zebra_db: &ZebraDb,
875 subtree: &NoteCommitmentSubtree<orchard::tree::Node>,
876 ) {
877 self.insert_note_commitment_subtree(zebra_db, "orchard_note_commitment_subtree", subtree);
878 }
879
880 pub fn create_ironwood_tree(
885 &mut self,
886 zebra_db: &ZebraDb,
887 height: &Height,
888 tree: &orchard::tree::NoteCommitmentTree,
889 ) {
890 self.create_note_commitment_tree(
891 zebra_db,
892 "ironwood_anchors",
893 "ironwood_note_commitment_tree",
894 height,
895 tree,
896 );
897 }
898
899 pub fn insert_ironwood_subtree(
901 &mut self,
902 zebra_db: &ZebraDb,
903 subtree: &NoteCommitmentSubtree<orchard::tree::Node>,
904 ) {
905 self.insert_note_commitment_subtree(zebra_db, "ironwood_note_commitment_subtree", subtree);
906 }
907
908 fn create_note_commitment_tree(
913 &mut self,
914 zebra_db: &ZebraDb,
915 anchors_cf: &str,
916 tree_cf: &str,
917 height: &Height,
918 tree: &orchard::tree::NoteCommitmentTree,
919 ) {
920 let anchors = zebra_db.db.cf_handle(anchors_cf).unwrap();
921 let tree_cf = zebra_db.db.cf_handle(tree_cf).unwrap();
922
923 self.zs_insert(&anchors, tree.root(), ());
924 self.zs_insert(&tree_cf, height, tree);
925 }
926
927 fn insert_note_commitment_subtree(
929 &mut self,
930 zebra_db: &ZebraDb,
931 subtree_cf: &str,
932 subtree: &NoteCommitmentSubtree<orchard::tree::Node>,
933 ) {
934 let subtree_cf = zebra_db.db.cf_handle(subtree_cf).unwrap();
935 self.zs_insert(&subtree_cf, subtree.index, subtree.into_data());
936 }
937
938 pub fn delete_orchard_tree(&mut self, zebra_db: &ZebraDb, height: &Height) {
940 let orchard_tree_cf = zebra_db
941 .db
942 .cf_handle("orchard_note_commitment_tree")
943 .unwrap();
944 self.zs_delete(&orchard_tree_cf, height);
945 }
946
947 #[allow(dead_code)]
950 pub fn delete_range_orchard_tree(&mut self, zebra_db: &ZebraDb, from: &Height, to: &Height) {
951 let orchard_tree_cf = zebra_db
952 .db
953 .cf_handle("orchard_note_commitment_tree")
954 .unwrap();
955
956 self.zs_delete_range(&orchard_tree_cf, from, to);
958 }
959
960 #[allow(dead_code)]
962 pub fn delete_orchard_anchor(&mut self, zebra_db: &ZebraDb, anchor: &orchard::tree::Root) {
963 let orchard_anchors = zebra_db.db.cf_handle("orchard_anchors").unwrap();
964 self.zs_delete(&orchard_anchors, anchor);
965 }
966
967 pub fn delete_range_orchard_subtree(
970 &mut self,
971 zebra_db: &ZebraDb,
972 from: NoteCommitmentSubtreeIndex,
973 to: NoteCommitmentSubtreeIndex,
974 ) {
975 let orchard_subtree_cf = zebra_db
976 .db
977 .cf_handle("orchard_note_commitment_subtree")
978 .unwrap();
979
980 self.zs_delete_range(&orchard_subtree_cf, from, to);
982 }
983}