Skip to main content

auths_cli/commands/
witness_set.rs

1//! `auths witness-set` — declare the spend-anchor witness set in your KEL.
2//!
3//! A finalized spend anchor is only as trustworthy as the witness set it names,
4//! and verifiers refuse a set the seller never committed to. `declare` computes
5//! the set's content SAID and anchors it in the identity's KEL via one `ixn`
6//! (the declaration verifiers resolve); `said` prints the SAID without touching
7//! the KEL. Thin presentation layer over
8//! [`auths_sdk::workflows::witness_set`] — no domain logic lives here.
9
10use anyhow::{Context, Result};
11use auths_sdk::workflows::witness_set::{
12    build_witness_set, declare_witness_set, resolve_declaration_alias,
13};
14use clap::{Parser, Subcommand};
15
16use crate::commands::executable::ExecutableCommand;
17use crate::config::CliConfig;
18use crate::factories::storage::build_auths_context;
19use crate::ux::format::{JsonResponse, is_json_mode};
20
21/// Declare the witness set your spend anchors commit to.
22#[derive(Parser, Debug, Clone)]
23#[command(
24    about = "Declare the spend-anchor witness set, anchored in your KEL",
25    after_help = "Examples:
26  auths witness-set said --member w1=<hex> --member w2=p256:<hex> --threshold 2
27  auths witness-set declare --member network=did:key:z6Mk... --threshold 1
28
29Member keys carry their curve in-band: a CESR verkey, a did:key, `<curve>:<hex>`,
30or bare hex (Ed25519 — the checkpoint-signing curve)."
31)]
32pub struct WitnessSetCommand {
33    #[command(subcommand)]
34    pub subcommand: WitnessSetSubcommand,
35}
36
37/// Witness-set subcommands.
38#[derive(Subcommand, Debug, Clone)]
39pub enum WitnessSetSubcommand {
40    /// Anchor the set's content SAID in your KEL via one `ixn`, so verifiers
41    /// hold your anchors to exactly this set.
42    Declare {
43        /// A declared member as `NAME=KEY` (repeatable).
44        #[clap(long = "member", value_name = "NAME=KEY", required = true)]
45        members: Vec<String>,
46
47        /// The finalization threshold `t` of the `t`-of-`N` set.
48        #[clap(long, value_name = "T")]
49        threshold: u32,
50
51        /// Keychain alias of your identity signing key (defaults to the
52        /// identity's primary key).
53        #[clap(long, value_name = "ALIAS")]
54        key: Option<String>,
55    },
56
57    /// Print the set's content SAID without touching the KEL (deterministic).
58    Said {
59        /// A declared member as `NAME=KEY` (repeatable).
60        #[clap(long = "member", value_name = "NAME=KEY", required = true)]
61        members: Vec<String>,
62
63        /// The finalization threshold `t` of the `t`-of-`N` set.
64        #[clap(long, value_name = "T")]
65        threshold: u32,
66    },
67}
68
69#[derive(serde::Serialize)]
70struct DeclareResponse {
71    set_said: String,
72    ixn_said: String,
73    sequence: u128,
74}
75
76impl ExecutableCommand for WitnessSetCommand {
77    fn execute(&self, ctx: &CliConfig) -> Result<()> {
78        match &self.subcommand {
79            WitnessSetSubcommand::Said { members, threshold } => print_said(members, *threshold),
80            WitnessSetSubcommand::Declare {
81                members,
82                threshold,
83                key,
84            } => declare(ctx, members, *threshold, key.clone()),
85        }
86    }
87}
88
89/// Compute and print the set's content SAID — no KEL, no keychain.
90fn print_said(members: &[String], threshold: u32) -> Result<()> {
91    let set = build_witness_set(members, threshold).map_err(anyhow::Error::new)?;
92    if is_json_mode() {
93        JsonResponse::success(
94            "witness-set said",
95            serde_json::json!({ "set_said": set.said }),
96        )
97        .print()?;
98    } else {
99        println!("{}", set.said);
100    }
101    Ok(())
102}
103
104/// Build the set and anchor its SAID in the identity's KEL via the SDK workflow.
105fn declare(ctx: &CliConfig, members: &[String], threshold: u32, key: Option<String>) -> Result<()> {
106    let set = build_witness_set(members, threshold).map_err(anyhow::Error::new)?;
107    let repo_path = auths_sdk::storage_layout::layout::resolve_repo_path(ctx.repo_path.clone())?;
108    let sdk_ctx = build_auths_context(
109        &repo_path,
110        &ctx.env_config,
111        Some(ctx.passphrase_provider.clone()),
112    )?;
113    let alias = resolve_declaration_alias(&sdk_ctx, key).map_err(anyhow::Error::new)?;
114    let declared = declare_witness_set(&sdk_ctx, &alias, &set)
115        .with_context(|| format!("Failed to declare witness set {}", set.said))?;
116
117    if is_json_mode() {
118        JsonResponse::success(
119            "witness-set declare",
120            DeclareResponse {
121                set_said: declared.set_said,
122                ixn_said: declared.ixn_said,
123                sequence: declared.sequence,
124            },
125        )
126        .print()?;
127    } else {
128        println!("✓ Witness set declared in your KEL:");
129        println!("  set SAID  {}", declared.set_said);
130        println!(
131            "  anchored  ixn {} (seq {})",
132            declared.ixn_said, declared.sequence
133        );
134        println!("  verifiers now hold your anchors to exactly this set");
135    }
136    Ok(())
137}