zebra_state/service/finalized_state/disk_format/upgrade/
cache_genesis_roots.rs1use crossbeam_channel::{Receiver, TryRecvError};
7use zebra_chain::{block::Height, sprout};
8
9use crate::service::finalized_state::{disk_db::DiskWriteBatch, ZebraDb};
10
11use super::CancelFormatChange;
12
13#[allow(clippy::unwrap_in_result)]
21#[instrument(skip(upgrade_db, cancel_receiver))]
22pub fn run(
23 _initial_tip_height: Height,
24 upgrade_db: &ZebraDb,
25 cancel_receiver: &Receiver<CancelFormatChange>,
26) -> Result<(), CancelFormatChange> {
27 let sprout_genesis_tree = sprout::tree::NoteCommitmentTree::default();
28 let sprout_tip_tree = upgrade_db.sprout_tree_for_tip();
29
30 let sapling_genesis_tree = upgrade_db
31 .sapling_tree_by_height(&Height(0))
32 .expect("caller has checked for genesis block");
33 let orchard_genesis_tree = upgrade_db
34 .orchard_tree_by_height(&Height(0))
35 .expect("caller has checked for genesis block");
36
37 let mut batch = DiskWriteBatch::new();
39
40 batch.update_sprout_tree(upgrade_db, &sprout_genesis_tree);
45 batch.update_sprout_tree(upgrade_db, &sprout_tip_tree);
47
48 batch.create_sapling_tree(upgrade_db, &Height(0), &sapling_genesis_tree);
49 batch.create_orchard_tree(upgrade_db, &Height(0), &orchard_genesis_tree);
50
51 if !matches!(cancel_receiver.try_recv(), Err(TryRecvError::Empty)) {
53 return Err(CancelFormatChange);
54 }
55
56 upgrade_db
57 .write_batch(batch)
58 .expect("updating tree cached roots should always succeed");
59
60 Ok(())
61}
62
63pub fn quick_check(db: &ZebraDb) -> Result<(), String> {
72 if db.is_empty() {
74 return Ok(());
75 }
76
77 let sprout_genesis_tree = sprout::tree::NoteCommitmentTree::default();
78 let sprout_genesis_tree = db
79 .sprout_tree_by_anchor(&sprout_genesis_tree.root())
80 .expect("just checked for genesis block");
81 let sprout_tip_tree = db.sprout_tree_for_tip();
82
83 let sapling_genesis_tree = db
84 .sapling_tree_by_height(&Height(0))
85 .expect("just checked for genesis block");
86 let orchard_genesis_tree = db
87 .orchard_tree_by_height(&Height(0))
88 .expect("just checked for genesis block");
89
90 let sprout_result = sprout_genesis_tree
92 .cached_root()
93 .ok_or("no cached root in sprout genesis tree");
94 let sprout_tip_result = sprout_tip_tree
95 .cached_root()
96 .ok_or("no cached root in sprout tip tree");
97
98 let sapling_result = sapling_genesis_tree
99 .cached_root()
100 .ok_or("no cached root in sapling genesis tree");
101 let orchard_result = orchard_genesis_tree
102 .cached_root()
103 .ok_or("no cached root in orchard genesis tree");
104
105 if sprout_result.is_err()
106 || sprout_tip_result.is_err()
107 || sapling_result.is_err()
108 || orchard_result.is_err()
109 {
110 let err = Err(format!(
111 "missing cached genesis root: sprout: {sprout_result:?}, {sprout_tip_result:?} \
112 sapling: {sapling_result:?}, orchard: {orchard_result:?}"
113 ));
114 warn!(?err);
115 return err;
116 }
117
118 Ok(())
119}
120
121pub fn detailed_check(
127 db: &ZebraDb,
128 cancel_receiver: &Receiver<CancelFormatChange>,
129) -> Result<Result<(), String>, CancelFormatChange> {
130 let mut result = quick_check(db);
133
134 for (root, tree) in db.sprout_trees_full_map() {
135 if !matches!(cancel_receiver.try_recv(), Err(TryRecvError::Empty)) {
137 return Err(CancelFormatChange);
138 }
139
140 if tree.cached_root().is_none() {
141 result = Err(format!(
142 "found un-cached sprout tree root after running genesis tree root fix \
143 {root:?}"
144 ));
145 error!(?result);
146 }
147 }
148
149 for (height, tree) in db.sapling_tree_by_height_range(..) {
150 if !matches!(cancel_receiver.try_recv(), Err(TryRecvError::Empty)) {
152 return Err(CancelFormatChange);
153 }
154
155 if tree.cached_root().is_none() {
156 result = Err(format!(
157 "found un-cached sapling tree root after running genesis tree root fix \
158 {height:?}"
159 ));
160 error!(?result);
161 }
162 }
163
164 for (height, tree) in db.orchard_tree_by_height_range(..) {
165 if !matches!(cancel_receiver.try_recv(), Err(TryRecvError::Empty)) {
167 return Err(CancelFormatChange);
168 }
169
170 if tree.cached_root().is_none() {
171 result = Err(format!(
172 "found un-cached orchard tree root after running genesis tree root fix \
173 {height:?}"
174 ));
175 error!(?result);
176 }
177 }
178
179 Ok(result)
180}