//! `spectra admin`: the ledger's own checks (spec §9.5, §19). use super::{ui, Ctx}; use anyhow::Result; use spectra_core::ledger::Ledger; use spectra_core::principals::Principals; /// `spectra admin verify`: recompute the chain, check every checkpoint and /// proof; exit 1 when anything is wrong. pub async fn verify(ctx: &Ctx, json: bool) -> Result { let ledger = Ledger::open_read_only(&ctx.db()?).await?; let spin = if json { None } else { Some(ui::spinner("verifying the ledger")) }; let v = ledger.verify(&mut Principals::default()).await?; if let Some(s) = spin { s.clear(); } if json { println!("{}", serde_json::to_string(&v)?); return Ok(if v.clean() { 0 } else { 1 }); } if v.clean() { println!("{} {}", ui::done("Verified"), ui::subject("the ledger")); } else { println!( "{} {} {}", ui::err(ui::glyph("✘", "x")), ui::err("The ledger does not verify:"), ui::err(format!("{} finding(s)", v.findings.len())) ); } println!("{}", ui::kv("events", v.events)); let last = v.last_checkpoint.map(|s| format!("last at #{s}")).unwrap_or_else(|| "none yet".into()); println!("{}", ui::kv("signed", format!("{} checkpoint(s){}{last}", v.checkpoints, ui::sep()))); if v.uncovered > 0 { println!("{}", ui::kv("pending", ui::dim(format!("{} event(s) since, chained and awaiting the next checkpoint", v.uncovered)))); } println!("{}", ui::kv("head", ui::hash(hex::encode(v.head)))); for f in &v.findings { println!(" {} {} {}", ui::id(format!("#{}", f.seq)), ui::err(format!("{:?}", f.problem)), f.detail); } Ok(if v.clean() { 0 } else { 1 }) } /// `spectra admin check`: do the projections equal a rebuild? pub async fn check(ctx: &Ctx) -> Result { let ledger = ctx.writer().await?; let differ = ledger.check().await?; if differ.is_empty() { println!("{} {}", ui::done("Checked"), ui::subject("every projection matches the ledger")); Ok(0) } else { println!("{} projections differ from the ledger: {}", ui::caution(), ui::err(differ.join(", "))); println!(" {} {}", ui::note(), ui::dim("rebuild them with spectra admin rebuild")); Ok(1) } } /// `spectra admin rebuild`: replay the ledger into every projection. pub async fn rebuild(ctx: &Ctx) -> Result { let ledger = ctx.writer().await?; let spin = ui::spinner("replaying the ledger"); ledger.rebuild().await?; spin.done(&format!("{} {}", ui::done("Rebuilt"), ui::subject(format!("projections from {} events", ledger.head().await.0)))); Ok(0) }