Skip to content

Commit 43bf51e

Browse files
committed
Adding individual epoch auditing in kt auditor example
1 parent f6d8942 commit 43bf51e

File tree

4 files changed

+117
-73
lines changed

4 files changed

+117
-73
lines changed

examples/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ runtime_metrics = []
2020
[dependencies]
2121
anyhow = "1"
2222
async-trait = "0.1"
23+
bytesize = "1"
2324
colored = "2"
2425
clap = { version = "4", features = ["derive"] }
2526
dialoguer = "0.11"

examples/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ You can also automatically audit the latest epoch with the `-l` parameter (for "
2121
```
2222
cargo run -p examples --release -- whatsapp-kt-auditor -l
2323
```
24+
or if you want to audit a specific epoch:
25+
```
26+
cargo run -p examples --release -- whatsapp-kt-auditor -e 42
27+
```
28+
2429

2530
### MySQL Demo
2631

examples/src/whatsapp_kt_auditor/auditor.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,7 @@ pub(crate) async fn audit_epoch(blob: akd::local_auditing::AuditBlob) -> Result<
5454
)
5555
.await
5656
{
57-
bail!(
58-
"Audit proof for epoch {} failed to verify with error: {}",
59-
end_epoch,
60-
akd_error
61-
)
57+
bail!("Audit proof for epoch {end_epoch} failed to verify with error: {akd_error}")
6258
} else {
6359
// verification passed, generate the appropriate QR code
6460
Ok(format!(
@@ -99,6 +95,22 @@ pub(crate) fn display_audit_proofs_info(info: &mut [EpochSummary]) -> Result<Str
9995
))
10096
}
10197

98+
pub(crate) async fn get_proof_from_epoch(url: &str, epoch: u64) -> Result<EpochSummary> {
99+
let params = vec![
100+
("list-type".to_string(), "2".to_string()),
101+
("prefix".to_string(), format!("{epoch}/")),
102+
];
103+
104+
let (mut keys, truncated_result) = get_xml(url, &params).await?;
105+
if truncated_result || keys.len() > 1 {
106+
bail!("Found multiple matches for epoch {epoch}, which is unexpected. Bailing...");
107+
}
108+
if keys.is_empty() {
109+
bail!("Could not find epoch {epoch}");
110+
}
111+
Ok(keys.remove(0))
112+
}
113+
102114
pub(crate) async fn list_proofs(url: &str) -> Result<Vec<EpochSummary>> {
103115
let mut results = vec![];
104116
let mut is_truncated = true;

examples/src/whatsapp_kt_auditor/mod.rs

Lines changed: 94 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ mod auditor;
1111

1212
use akd::local_auditing::AuditBlobName;
1313
use anyhow::{anyhow, bail, Result};
14-
use clap::Parser;
14+
use clap::{Parser, Subcommand};
1515
use dialoguer::theme::ColorfulTheme;
1616
use dialoguer::{Input, Select};
1717
use indicatif::{ProgressBar, ProgressStyle};
@@ -45,15 +45,27 @@ impl TryFrom<&str> for EpochSummary {
4545
}
4646

4747
#[derive(Parser, Debug, Clone)]
48+
#[clap(author, about, long_about = None)]
4849
pub(crate) struct CliArgs {
49-
/// Optional argument to indicate that only the latest epoch should be audited
50+
/// The type of command to run
51+
#[clap(subcommand)]
52+
command: Command,
53+
}
54+
55+
#[derive(Debug, Clone, Subcommand)]
56+
enum Command {
57+
/// For auditing a specific epoch
58+
#[clap(short_flag = 'e', name = "Choose a specific epoch to audit")]
59+
Epoch { epoch: u64 },
60+
/// For auditing all epochs through an interactive interface
5061
#[clap(
51-
long = "latest",
52-
short = 'l',
53-
name = "Audit only the latest epoch",
54-
default_value = "false"
62+
short_flag = 'i',
63+
name = "Load all epochs to be audited (this can take some time...)"
5564
)]
56-
audit_latest: bool,
65+
Interactive,
66+
/// Indicate that only the latest epoch should be audited
67+
#[clap(short_flag = 'l', name = "Audit only the latest epoch")]
68+
AuditLatest,
5769
}
5870

5971
#[derive(Debug)]
@@ -68,82 +80,96 @@ struct CliOption {
6880
}
6981

7082
pub(crate) async fn render_cli(args: CliArgs) -> Result<()> {
71-
let pb = start_progress_bar("Loading epochs...");
72-
let mut proofs = auditor::list_proofs(WHATSAPP_KT_DOMAIN).await?;
73-
finish_progress_bar(pb, auditor::display_audit_proofs_info(&mut proofs)?);
74-
75-
if args.audit_latest {
76-
// Just audit the latest epoch and exit
77-
let latest_epoch_summary = proofs.last().expect("No epochs found");
78-
do_epoch_audit(latest_epoch_summary).await?;
79-
return Ok(());
80-
}
81-
82-
let items: Vec<CliOption> = vec![
83-
CliOption {
84-
cli_type: CliType::Audit,
85-
text: "Audit".to_string(),
86-
},
87-
CliOption {
88-
cli_type: CliType::Quit,
89-
text: "Quit".to_string(),
90-
},
91-
];
92-
93-
loop {
94-
let selection = Select::with_theme(&ColorfulTheme::default())
95-
.items(
96-
&items
97-
.iter()
98-
.map(|item| item.text.clone())
99-
.collect::<Vec<String>>(),
100-
)
101-
.default(0)
102-
.interact_opt()?;
103-
104-
match selection {
105-
Some(index) => match items[index].cli_type {
106-
CliType::Audit => {
107-
let epoch_input: String = Input::new()
108-
.with_prompt("Audit which epoch?".to_string())
109-
.validate_with(|input: &String| -> Result<(), &str> {
110-
let int = input.parse::<usize>().map_err(|_| "Not a valid epoch")?;
111-
if 1 <= int && int <= proofs.len() {
112-
Ok(())
113-
} else {
114-
Err("Epoch is out of available range")
115-
}
116-
})
117-
.interact_text()?;
118-
let epoch = epoch_input.parse::<u64>()?;
119-
let maybe_proof = proofs.iter().find(|proof| proof.name.epoch == epoch);
120-
if let Some(epoch_summary) = maybe_proof {
121-
do_epoch_audit(epoch_summary).await?;
122-
} else {
123-
bail!("Could not find epoch {}", epoch);
83+
match args.command {
84+
Command::AuditLatest => {
85+
// Just audit the latest epoch and exit
86+
let proofs = load_all_proofs().await?;
87+
let latest_epoch_summary = proofs.last().expect("No epochs found");
88+
do_epoch_audit(latest_epoch_summary).await?;
89+
return Ok(());
90+
}
91+
Command::Epoch { epoch } => {
92+
let epoch_summary = auditor::get_proof_from_epoch(WHATSAPP_KT_DOMAIN, epoch).await?;
93+
do_epoch_audit(&epoch_summary).await?;
94+
return Ok(());
95+
}
96+
Command::Interactive => {
97+
let proofs = load_all_proofs().await?;
98+
let items: Vec<CliOption> = vec![
99+
CliOption {
100+
cli_type: CliType::Audit,
101+
text: "Audit".to_string(),
102+
},
103+
CliOption {
104+
cli_type: CliType::Quit,
105+
text: "Quit".to_string(),
106+
},
107+
];
108+
109+
loop {
110+
let selection = Select::with_theme(&ColorfulTheme::default())
111+
.items(
112+
&items
113+
.iter()
114+
.map(|item| item.text.clone())
115+
.collect::<Vec<String>>(),
116+
)
117+
.default(0)
118+
.interact_opt()?;
119+
120+
match selection {
121+
Some(index) => match items[index].cli_type {
122+
CliType::Audit => {
123+
let epoch_input: String = Input::new()
124+
.with_prompt("Audit which epoch?".to_string())
125+
.validate_with(|input: &String| -> Result<(), &str> {
126+
let int =
127+
input.parse::<usize>().map_err(|_| "Not a valid epoch")?;
128+
if 1 <= int && int <= proofs.len() {
129+
Ok(())
130+
} else {
131+
Err("Epoch is out of available range")
132+
}
133+
})
134+
.interact_text()?;
135+
let epoch = epoch_input.parse::<u64>()?;
136+
let maybe_proof = proofs.iter().find(|proof| proof.name.epoch == epoch);
137+
let Some(epoch_summary) = maybe_proof else {
138+
bail!("Could not find epoch {epoch}");
139+
};
140+
do_epoch_audit(epoch_summary).await?;
141+
}
142+
CliType::Quit => {
143+
break;
144+
}
145+
},
146+
None => {
147+
break;
124148
}
125149
}
126-
CliType::Quit => {
127-
break;
128-
}
129-
},
130-
None => {
131-
break;
132150
}
133151
}
134152
}
135153

136154
Ok(())
137155
}
138156

157+
async fn load_all_proofs() -> Result<Vec<EpochSummary>> {
158+
let pb = start_progress_bar("Loading epochs...");
159+
let mut proofs = auditor::list_proofs(WHATSAPP_KT_DOMAIN).await?;
160+
finish_progress_bar(pb, auditor::display_audit_proofs_info(&mut proofs)?);
161+
Ok(proofs)
162+
}
163+
139164
pub(crate) async fn do_epoch_audit(epoch_summary: &EpochSummary) -> Result<()> {
140165
let pb1 = start_progress_bar("Downloading proof...");
141166
let proof = auditor::get_proof(WHATSAPP_KT_DOMAIN, epoch_summary).await?;
142167
finish_progress_bar(
143168
pb1,
144169
format!(
145-
"Successfully downloaded proof for epoch {}.",
146-
epoch_summary.name.epoch
170+
"Successfully downloaded proof for epoch {}. ({})",
171+
epoch_summary.name.epoch,
172+
bytesize::ByteSize::b(proof.data.len() as u64)
147173
),
148174
);
149175

0 commit comments

Comments
 (0)