use std::sync::mpsc;
use zebra_chain::{block::Height, sprout};
use crate::service::finalized_state::{disk_db::DiskWriteBatch, ZebraDb};
use super::CancelFormatChange;
#[allow(clippy::unwrap_in_result)]
#[instrument(skip(upgrade_db, cancel_receiver))]
pub fn run(
_initial_tip_height: Height,
upgrade_db: &ZebraDb,
cancel_receiver: &mpsc::Receiver<CancelFormatChange>,
) -> Result<(), CancelFormatChange> {
let sprout_genesis_tree = sprout::tree::NoteCommitmentTree::default();
let sprout_tip_tree = upgrade_db.sprout_tree_for_tip();
let sapling_genesis_tree = upgrade_db
.sapling_tree_by_height(&Height(0))
.expect("caller has checked for genesis block");
let orchard_genesis_tree = upgrade_db
.orchard_tree_by_height(&Height(0))
.expect("caller has checked for genesis block");
let mut batch = DiskWriteBatch::new();
batch.update_sprout_tree(upgrade_db, &sprout_genesis_tree);
batch.update_sprout_tree(upgrade_db, &sprout_tip_tree);
batch.create_sapling_tree(upgrade_db, &Height(0), &sapling_genesis_tree);
batch.create_orchard_tree(upgrade_db, &Height(0), &orchard_genesis_tree);
if !matches!(cancel_receiver.try_recv(), Err(mpsc::TryRecvError::Empty)) {
return Err(CancelFormatChange);
}
upgrade_db
.write_batch(batch)
.expect("updating tree cached roots should always succeed");
Ok(())
}
pub fn quick_check(db: &ZebraDb) -> Result<(), String> {
if db.is_empty() {
return Ok(());
}
let sprout_genesis_tree = sprout::tree::NoteCommitmentTree::default();
let sprout_genesis_tree = db
.sprout_tree_by_anchor(&sprout_genesis_tree.root())
.expect("just checked for genesis block");
let sprout_tip_tree = db.sprout_tree_for_tip();
let sapling_genesis_tree = db
.sapling_tree_by_height(&Height(0))
.expect("just checked for genesis block");
let orchard_genesis_tree = db
.orchard_tree_by_height(&Height(0))
.expect("just checked for genesis block");
let sprout_result = sprout_genesis_tree
.cached_root()
.ok_or("no cached root in sprout genesis tree");
let sprout_tip_result = sprout_tip_tree
.cached_root()
.ok_or("no cached root in sprout tip tree");
let sapling_result = sapling_genesis_tree
.cached_root()
.ok_or("no cached root in sapling genesis tree");
let orchard_result = orchard_genesis_tree
.cached_root()
.ok_or("no cached root in orchard genesis tree");
if sprout_result.is_err()
|| sprout_tip_result.is_err()
|| sapling_result.is_err()
|| orchard_result.is_err()
{
let err = Err(format!(
"missing cached genesis root: sprout: {sprout_result:?}, {sprout_tip_result:?} \
sapling: {sapling_result:?}, orchard: {orchard_result:?}"
));
warn!(?err);
return err;
}
Ok(())
}
pub fn detailed_check(
db: &ZebraDb,
cancel_receiver: &mpsc::Receiver<CancelFormatChange>,
) -> Result<Result<(), String>, CancelFormatChange> {
let mut result = quick_check(db);
for (root, tree) in db.sprout_trees_full_map() {
if !matches!(cancel_receiver.try_recv(), Err(mpsc::TryRecvError::Empty)) {
return Err(CancelFormatChange);
}
if tree.cached_root().is_none() {
result = Err(format!(
"found un-cached sprout tree root after running genesis tree root fix \
{root:?}"
));
error!(?result);
}
}
for (height, tree) in db.sapling_tree_by_height_range(..) {
if !matches!(cancel_receiver.try_recv(), Err(mpsc::TryRecvError::Empty)) {
return Err(CancelFormatChange);
}
if tree.cached_root().is_none() {
result = Err(format!(
"found un-cached sapling tree root after running genesis tree root fix \
{height:?}"
));
error!(?result);
}
}
for (height, tree) in db.orchard_tree_by_height_range(..) {
if !matches!(cancel_receiver.try_recv(), Err(mpsc::TryRecvError::Empty)) {
return Err(CancelFormatChange);
}
if tree.cached_root().is_none() {
result = Err(format!(
"found un-cached orchard tree root after running genesis tree root fix \
{height:?}"
));
error!(?result);
}
}
Ok(result)
}