OXYGENHUB
# 5eb57578f4ef spectra / src / cli / admin.rs
2.6 KiB · Rust · last changed The command line in oxy's style: ticks and verbs, key-value rows, sections, hints, spinners; plain when piped 2 days ago source blame history raw
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.
10
pub async fn verify(ctx: &Ctx, json: bool) -> Result<i32> {
11
    let ledger = Ledger::open_read_only(&ctx.db()?).await?;
12
    let spin = if json { None } else { Some(ui::spinner("verifying the ledger")) };
13
    let v = ledger.verify(&mut Principals::default()).await?;
14
    if let Some(s) = spin {
15
        s.clear();
16
    }
17
    if json {
18
        println!("{}", serde_json::to_string(&v)?);
19
        return Ok(if v.clean() { 0 } else { 1 });
20
    }
21
    if v.clean() {
22
        println!("{} {}", ui::done("Verified"), ui::subject("the ledger"));
23
    } else {
24
        println!(
25
            "{} {} {}",
26
            ui::err(ui::glyph("✘", "x")),
27
            ui::err("The ledger does not verify:"),
28
            ui::err(format!("{} finding(s)", v.findings.len()))
29
        );
30
    }
31
    println!("{}", ui::kv("events", v.events));
32
    let last = v.last_checkpoint.map(|s| format!("last at #{s}")).unwrap_or_else(|| "none yet".into());
33
    println!("{}", ui::kv("signed", format!("{} checkpoint(s){}{last}", v.checkpoints, ui::sep())));
34
    if v.uncovered > 0 {
35
        println!("{}", ui::kv("pending", ui::dim(format!("{} event(s) since, chained and awaiting the next checkpoint", v.uncovered))));
36
    }
37
    println!("{}", ui::kv("head", ui::hash(hex::encode(v.head))));
38
    for f in &v.findings {
39
        println!("  {} {} {}", ui::id(format!("#{}", f.seq)), ui::err(format!("{:?}", f.problem)), f.detail);
40
    }
41
    Ok(if v.clean() { 0 } else { 1 })
42
}
43
44
/// `spectra admin check`: do the projections equal a rebuild?
45
pub async fn check(ctx: &Ctx) -> Result<i32> {
46
    let ledger = ctx.writer().await?;
47
    let differ = ledger.check().await?;
48
    if differ.is_empty() {
49
        println!("{} {}", ui::done("Checked"), ui::subject("every projection matches the ledger"));
50
        Ok(0)
51
    } else {
52
        println!("{} projections differ from the ledger: {}", ui::caution(), ui::err(differ.join(", ")));
53
        println!("  {} {}", ui::note(), ui::dim("rebuild them with spectra admin rebuild"));
54
        Ok(1)
55
    }
56
}
57
58
/// `spectra admin rebuild`: replay the ledger into every projection.
59
pub async fn rebuild(ctx: &Ctx) -> Result<i32> {
60
    let ledger = ctx.writer().await?;
61
    let spin = ui::spinner("replaying the ledger");
62
    ledger.rebuild().await?;
63
    spin.done(&format!("{} {}", ui::done("Rebuilt"), ui::subject(format!("projections from {} events", ledger.head().await.0))));
64
    Ok(0)
65
}