use std::path::PathBuf;
use abscissa_core::{Application, Command, Runnable};
use clap::Parser;
use color_eyre::eyre::Result;
use zakura_chain::{block, parameters::Network};
use zakura_state::{RollbackFinalizedStateOptions, RollbackFinalizedStateSummary};
use crate::prelude::APPLICATION;
#[derive(Command, Debug, Default, Parser)]
pub struct RollbackStateCmd {
#[clap(long, help = "finalized block height to keep as the new tip")]
height: u32,
#[clap(long, short, help = "path to directory with the Zakura chain state")]
cache_dir: Option<PathBuf>,
#[clap(
long,
short,
required = true,
help = "the network of the chain to load"
)]
network: Network,
#[clap(long, help = "keep rolled-back blocks for non-finalized restore")]
keep_rolled_back_blocks: bool,
#[clap(long, help = "show the rollback plan without changing finalized state")]
dry_run: bool,
}
impl Runnable for RollbackStateCmd {
fn run(&self) {
let config = APPLICATION.config();
if let Err(error) = self.run_with_config(config.state.clone(), config.consensus.clone()) {
tracing::error!("Failed to roll back finalized state: {error}");
std::process::exit(1);
}
}
}
impl RollbackStateCmd {
#[allow(clippy::print_stdout)]
pub fn run_with_config(
&self,
mut state_config: zakura_state::Config,
consensus_config: zakura_consensus::config::Config,
) -> Result<()> {
if let Some(cache_dir) = self.cache_dir.clone() {
state_config.cache_dir = cache_dir;
}
let (_, max_checkpoint_height) =
zakura_consensus::router::init_checkpoint_list(consensus_config, &self.network);
let options = RollbackFinalizedStateOptions {
target_height: block::Height(self.height),
keep_rolled_back_blocks: self.keep_rolled_back_blocks,
max_checkpoint_height: Some(max_checkpoint_height),
};
if self.dry_run {
let preview = zakura_state::preview_rollback_finalized_state(
state_config,
&self.network,
options,
)?;
print_summary("rollback plan", &preview);
return Ok(());
}
let summary = zakura_state::rollback_finalized_state(state_config, &self.network, options)?;
print_summary("rollback complete", &summary);
Ok(())
}
}
#[allow(clippy::print_stdout)]
fn print_summary(label: &str, summary: &RollbackFinalizedStateSummary) {
println!("{label}:");
println!(
" old finalized tip: height {}, hash {}",
summary.old_tip.0 .0, summary.old_tip.1
);
println!(
" new finalized tip: height {}, hash {}",
summary.new_tip.0 .0, summary.new_tip.1
);
println!(" rolled-back blocks: {}", summary.rolled_back_count);
if let Some(backup) = &summary.backup {
println!(
" non-finalized backup: {} blocks in {}",
backup.block_count,
backup.path.display()
);
}
}