zebra_state/service/finalized_state/disk_format/upgrade/
add_ironwood_tree.rs1use crossbeam_channel::{Receiver, TryRecvError};
17use semver::Version;
18
19use zebra_chain::{block::Height, orchard};
20
21use crate::service::finalized_state::{DiskWriteBatch, ZebraDb};
22
23use super::{CancelFormatChange, DiskFormatUpgrade};
24
25pub struct Upgrade;
27
28impl DiskFormatUpgrade for Upgrade {
29 fn version(&self) -> Version {
30 Version::new(28, 0, 0)
31 }
32
33 fn description(&self) -> &'static str {
34 "add ironwood shielded pool state (genesis tree and anchor backfill)"
35 }
36
37 #[allow(clippy::unwrap_in_result)]
38 fn run(
39 &self,
40 _initial_tip_height: Height,
41 db: &ZebraDb,
42 cancel_receiver: &Receiver<CancelFormatChange>,
43 ) -> Result<(), CancelFormatChange> {
44 check_cancelled(cancel_receiver)?;
45
46 if db.finalized_tip_height().is_none() || has_genesis_ironwood_tree(db) {
49 return Ok(());
50 }
51
52 let ironwood_tree = orchard::tree::NoteCommitmentTree::default();
55 let mut batch = DiskWriteBatch::new();
56 batch.create_ironwood_tree(db, &Height::MIN, &ironwood_tree);
57
58 check_cancelled(cancel_receiver)?;
59
60 db.write_batch(batch)
61 .expect("backfilling the genesis Ironwood tree should always succeed");
62
63 Ok(())
64 }
65
66 fn validate(
67 &self,
68 db: &ZebraDb,
69 _cancel_receiver: &Receiver<CancelFormatChange>,
70 ) -> Result<Result<(), String>, CancelFormatChange> {
71 if db.finalized_tip_height().is_none() {
73 return Ok(Ok(()));
74 }
75
76 if !has_genesis_ironwood_tree(db) {
77 return Ok(Err(
78 "missing Ironwood note commitment tree for the genesis height".to_string(),
79 ));
80 }
81
82 let ironwood_tree = db.ironwood_tree_for_tip();
83 if !db.contains_ironwood_anchor(&ironwood_tree.root()) {
84 return Ok(Err(
85 "missing Ironwood anchor for the finalized tip's Ironwood tree".to_string(),
86 ));
87 }
88
89 Ok(Ok(()))
90 }
91}
92
93fn has_genesis_ironwood_tree(db: &ZebraDb) -> bool {
95 db.ironwood_tree_by_height_range(..=Height::MIN)
96 .next()
97 .is_some()
98}
99
100fn check_cancelled(
101 cancel_receiver: &Receiver<CancelFormatChange>,
102) -> Result<(), CancelFormatChange> {
103 match cancel_receiver.try_recv() {
104 Err(TryRecvError::Empty) => Ok(()),
105 _ => Err(CancelFormatChange),
106 }
107}