| 1 | |
|
| 2 | |
|
| 3 | | use super::{ui, Ctx}; |
| 4 | | use anyhow::Result; |
| 5 | | use spectra_core::ledger::Ledger; |
| 6 | | use spectra_core::principals::Principals; |
| 7 | |
|
| 8 | |
|
| 9 | |
|
| 10 | 18× | pub async fn verify(ctx: &Ctx, json: bool) -> Result<i32> { |
| 11 | 18× | let ledger = Ledger::open_read_only(&ctx.db()?).await?; |
| 12 | 17× | let spin = if json { None } else { Some(ui::spinner("verifying the ledger")) }; |
| 13 | 17× | let v = ledger.verify(&mut Principals::default()).await?; |
| 14 | 17× | if let Some(s) = spin { |
| 15 | 8× | s.clear(); |
| 16 | 9× | } |
| 17 | 17× | if json { |
| 18 | 9× | println!("{}", serde_json::to_string(&v)?); |
| 19 | 9× | return Ok(if v.clean() { 0 } else { 1 }); |
| 20 | 8× | } |
| 21 | 8× | if v.clean() { |
| 22 | 7× | println!("{} {}", ui::done("Verified"), ui::subject("the ledger")); |
| 23 | 7× | } else { |
| 24 | 1× | println!( |
| 25 | 1× | "{} {} {}", |
| 26 | 1× | ui::err(ui::glyph("✘", "x")), |
| 27 | 1× | ui::err("The ledger does not verify:"), |
| 28 | 1× | ui::err(format!("{} finding(s)", v.findings.len())) |
| 29 | 1× | ); |
| 30 | 1× | } |
| 31 | 8× | println!("{}", ui::kv("events", v.events)); |
| 32 | 8× | let last = v.last_checkpoint.map(|s| format!("last at #{s}")).unwrap_or_else(|| "none yet".into()); |
| 33 | 8× | println!("{}", ui::kv("signed", format!("{} checkpoint(s){}{last}", v.checkpoints, ui::sep()))); |
| 34 | 8× | if v.uncovered > 0 { |
| 35 | 0× | println!("{}", ui::kv("pending", ui::dim(format!("{} event(s) since, chained and awaiting the next checkpoint", v.uncovered)))); |
| 36 | 8× | } |
| 37 | 8× | println!("{}", ui::kv("head", ui::hash(hex::encode(v.head)))); |
| 38 | 8× | for f in &v.findings { |
| 39 | 1× | println!(" {} {} {}", ui::id(format!("#{}", f.seq)), ui::err(format!("{:?}", f.problem)), f.detail); |
| 40 | 1× | } |
| 41 | 8× | Ok(if v.clean() { 0 } else { 1 }) |
| 42 | 18× | } |
| 43 | |
|
| 44 | |
|
| 45 | 5× | pub async fn check(ctx: &Ctx) -> Result<i32> { |
| 46 | 5× | let ledger = ctx.writer().await?; |
| 47 | 5× | let differ = ledger.check().await?; |
| 48 | 5× | if differ.is_empty() { |
| 49 | 5× | println!("{} {}", ui::done("Checked"), ui::subject("every projection matches the ledger")); |
| 50 | 5× | Ok(0) |
| 51 | | } else { |
| 52 | 0× | println!("{} projections differ from the ledger: {}", ui::caution(), ui::err(differ.join(", "))); |
| 53 | 0× | println!(" {} {}", ui::note(), ui::dim("rebuild them with spectra admin rebuild")); |
| 54 | 0× | Ok(1) |
| 55 | | } |
| 56 | 5× | } |
| 57 | |
|
| 58 | |
|
| 59 | 2× | pub async fn rebuild(ctx: &Ctx) -> Result<i32> { |
| 60 | 2× | let ledger = ctx.writer().await?; |
| 61 | 2× | let spin = ui::spinner("replaying the ledger"); |
| 62 | 2× | ledger.rebuild().await?; |
| 63 | 2× | spin.done(&format!("{} {}", ui::done("Rebuilt"), ui::subject(format!("projections from {} events", ledger.head().await.0)))); |
| 64 | 2× | Ok(0) |
| 65 | 2× | } |