OXYGENHUB

Coverage / src/cli/admin.rs 90.9%

50 of 55 lines run, 7 of 16 functions · run #19 on main · run missed

1
//! `spectra admin`: the ledger's own checks (spec §9.5, §19).
2
3
use super::{ui, Ctx};
4
use anyhow::Result;
5
use spectra_core::ledger::Ledger;
6
use spectra_core::principals::Principals;
7
8
/// `spectra admin verify`: recompute the chain, check every checkpoint and
9
/// proof; exit 1 when anything is wrong.
1017×
pub async fn verify(ctx: &Ctx, json: bool) -> Result<i32> {
1117×
    let ledger = Ledger::open_read_only(&ctx.db()?).await?;
1216×
    let spin = if json { None } else { Some(ui::spinner("verifying the ledger")) };
1316×
    let v = ledger.verify(&mut Principals::default()).await?;
1416×
    if let Some(s) = spin {
157×
        s.clear();
169×
    }
1716×
    if json {
189×
        println!("{}", serde_json::to_string(&v)?);
199×
        return Ok(if v.clean() { 0 } else { 1 });
207×
    }
217×
    if v.clean() {
226×
        println!("{} {}", ui::done("Verified"), ui::subject("the ledger"));
236×
    } else {
241×
        println!(
251×
            "{} {} {}",
261×
            ui::err(ui::glyph("✘", "x")),
271×
            ui::err("The ledger does not verify:"),
281×
            ui::err(format!("{} finding(s)", v.findings.len()))
291×
        );
301×
    }
317×
    println!("{}", ui::kv("events", v.events));
327×
    let last = v.last_checkpoint.map(|s| format!("last at #{s}")).unwrap_or_else(|| "none yet".into());
337×
    println!("{}", ui::kv("signed", format!("{} checkpoint(s){}{last}", v.checkpoints, ui::sep())));
347×
    if v.uncovered > 0 {
350×
        println!("{}", ui::kv("pending", ui::dim(format!("{} event(s) since, chained and awaiting the next checkpoint", v.uncovered))));
367×
    }
377×
    println!("{}", ui::kv("head", ui::hash(hex::encode(v.head))));
387×
    for f in &v.findings {
391×
        println!("  {} {} {}", ui::id(format!("#{}", f.seq)), ui::err(format!("{:?}", f.problem)), f.detail);
401×
    }
417×
    Ok(if v.clean() { 0 } else { 1 })
4217×
}
43
44
/// `spectra admin check`: do the projections equal a rebuild?
455×
pub async fn check(ctx: &Ctx) -> Result<i32> {
465×
    let ledger = ctx.writer().await?;
475×
    let differ = ledger.check().await?;
485×
    if differ.is_empty() {
495×
        println!("{} {}", ui::done("Checked"), ui::subject("every projection matches the ledger"));
505×
        Ok(0)
51
    } else {
520×
        println!("{} projections differ from the ledger: {}", ui::caution(), ui::err(differ.join(", ")));
530×
        println!("  {} {}", ui::note(), ui::dim("rebuild them with spectra admin rebuild"));
540×
        Ok(1)
55
    }
565×
}
57
58
/// `spectra admin rebuild`: replay the ledger into every projection.
592×
pub async fn rebuild(ctx: &Ctx) -> Result<i32> {
602×
    let ledger = ctx.writer().await?;
612×
    let spin = ui::spinner("replaying the ledger");
622×
    ledger.rebuild().await?;
632×
    spin.done(&format!("{} {}", ui::done("Rebuilt"), ui::subject(format!("projections from {} events", ledger.head().await.0))));
642×
    Ok(0)
652×
}