Skip to content

Commit ae5be15

Browse files
committed
include witness data in audit export packages
each proof entry now carries preimage fields, hash function, personalization, and recompute instructions. full credential path.
1 parent c3cf511 commit ae5be15

1 file changed

Lines changed: 25 additions & 7 deletions

File tree

src/bin/zap1_export.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct ProofEntry {
3434
root: String,
3535
anchor_txid: Option<String>,
3636
anchor_height: Option<u32>,
37+
witness: serde_json::Value,
3738
}
3839

3940
#[derive(Debug, Serialize)]
@@ -84,9 +85,13 @@ struct LifecycleResponse {
8485
}
8586

8687
#[derive(Debug, Deserialize)]
88+
#[allow(dead_code)]
8789
struct LifecycleEvent {
8890
leaf_hash: String,
8991
event_type: String,
92+
serial_number: Option<String>,
93+
created_at: Option<String>,
94+
anchored: Option<bool>,
9095
}
9196

9297
struct Cli {
@@ -104,8 +109,8 @@ async fn main() -> Result<()> {
104109
.timeout(std::time::Duration::from_secs(15))
105110
.build()?;
106111

107-
// get leaf hashes to export
108-
let leaf_hashes = collect_leaf_hashes(&client, &cli).await?;
112+
// get leaf hashes and lifecycle events
113+
let (leaf_hashes, leaf_events) = collect_leaf_hashes(&client, &cli).await?;
109114

110115
if leaf_hashes.is_empty() {
111116
eprintln!("no matching leaves found");
@@ -134,6 +139,9 @@ async fn main() -> Result<()> {
134139
.await
135140
.with_context(|| format!("invalid proof JSON for {}", &hash[..12]))?;
136141

142+
// find the lifecycle event for this leaf to get witness fields
143+
let lifecycle_event = leaf_events.iter().find(|e| e.leaf_hash == *hash);
144+
137145
proofs.push(ProofEntry {
138146
leaf_hash: bundle.leaf.hash,
139147
event_type: bundle.leaf.event_type,
@@ -148,6 +156,13 @@ async fn main() -> Result<()> {
148156
root: bundle.root.hash,
149157
anchor_txid: bundle.anchor.txid,
150158
anchor_height: bundle.anchor.height,
159+
witness: serde_json::json!({
160+
"wallet_hash_preimage": cli.wallet_hash,
161+
"serial_number": lifecycle_event.and_then(|e| e.serial_number.clone()),
162+
"hash_function": "BLAKE2b-256",
163+
"personalization": "NordicShield_",
164+
"recompute": "hash(type_byte || length_prefixed_fields) with NordicShield_ personalization",
165+
}),
151166
});
152167
}
153168

@@ -189,7 +204,10 @@ async fn main() -> Result<()> {
189204
Ok(())
190205
}
191206

192-
async fn collect_leaf_hashes(client: &reqwest::Client, cli: &Cli) -> Result<Vec<String>> {
207+
async fn collect_leaf_hashes(
208+
client: &reqwest::Client,
209+
cli: &Cli,
210+
) -> Result<(Vec<String>, Vec<LifecycleEvent>)> {
193211
if let Some(wh) = &cli.wallet_hash {
194212
let url = format!("{}/lifecycle/{}", cli.api_url, wh);
195213
let resp = client
@@ -199,14 +217,14 @@ async fn collect_leaf_hashes(client: &reqwest::Client, cli: &Cli) -> Result<Vec<
199217
.context("failed to fetch lifecycle")?;
200218
let body: LifecycleResponse = resp.json().await.context("invalid lifecycle JSON")?;
201219

202-
let hashes: Vec<String> = body
220+
let filtered: Vec<LifecycleEvent> = body
203221
.events
204-
.iter()
222+
.into_iter()
205223
.filter(|e| cli.event_types.is_empty() || cli.event_types.contains(&e.event_type))
206-
.map(|e| e.leaf_hash.clone())
207224
.collect();
225+
let hashes: Vec<String> = filtered.iter().map(|e| e.leaf_hash.clone()).collect();
208226

209-
Ok(hashes)
227+
Ok((hashes, filtered))
210228
} else {
211229
Err(anyhow!("--wallet-hash required to scope the export"))
212230
}

0 commit comments

Comments
 (0)