vta-sdk 0.5.0

SDK for Verifiable Trust Agents operating in Verifiable Trust Communities
Documentation
//! Wire types for the webvh DID update + key rotation messages.
//!
//! Both REST (`POST /contexts/{ctx_id}/dids/{scid}/update`) and DIDComm
//! (`update-did-webvh` / `rotate-did-webvh-keys`) carry these bodies.
//! The result body is identical for both operations — `rotate_did_webvh_keys`
//! is a thin wrapper that drives the same flow as `update_did_webvh`
//! after rebuilding the document with fresh key bytes.

use serde::{Deserialize, Serialize};
use serde_json::Value;

/// Caller-supplied parameters for an update.
///
/// `witnesses` is carried as opaque JSON to keep this crate free of a
/// `didwebvh-rs` dependency. The vta-service handler deserializes it
/// into the library's `Witnesses` enum at intake.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct UpdateDidWebvhBody {
    /// New DID document. `None` = keep existing. When `Some`, the VTA
    /// rotates `update_keys` + pre-rotation commitments as a parallel
    /// consequence.
    #[serde(default)]
    pub document: Option<Value>,
    /// Override pre-rotation count. `None` = keep current; `Some(0)`
    /// disables pre-rotation; `Some(n)` uses `n` new commitments.
    #[serde(default)]
    pub pre_rotation_count: Option<u32>,
    /// New witness configuration as raw JSON (matches the library's
    /// `Witnesses` enum on the wire). The vta-service handler
    /// deserializes into the typed shape.
    #[serde(default)]
    pub witnesses: Option<Value>,
    /// New watcher URLs. `None` = keep current; `Some(vec![])` disables.
    #[serde(default)]
    pub watchers: Option<Vec<String>>,
    /// New TTL in seconds. `None` = keep current.
    #[serde(default)]
    pub ttl: Option<u32>,
    /// Operator-facing audit label.
    #[serde(default)]
    pub label: Option<String>,
}

/// Caller-supplied parameters for a rotate-keys call.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct RotateDidWebvhKeysBody {
    /// Override pre-rotation count for the new commitment set.
    #[serde(default)]
    pub pre_rotation_count: Option<u32>,
    /// Operator-facing audit label.
    #[serde(default)]
    pub label: Option<String>,
}

/// Result of a successful update or rotate-keys call.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateDidWebvhResultBody {
    pub did: String,
    pub new_version_id: String,
    pub new_scid: String,
    pub new_log_entry: String,
    pub update_keys_count: u32,
    pub pre_rotation_key_count: u32,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn update_body_round_trips_minimal() {
        let body = UpdateDidWebvhBody::default();
        let json = serde_json::to_string(&body).unwrap();
        let restored: UpdateDidWebvhBody = serde_json::from_str(&json).unwrap();
        assert!(restored.document.is_none());
        assert!(restored.pre_rotation_count.is_none());
    }

    #[test]
    fn update_body_round_trips_full() {
        let body = UpdateDidWebvhBody {
            document: Some(serde_json::json!({"id": "did:webvh:abc"})),
            pre_rotation_count: Some(2),
            witnesses: None,
            watchers: Some(vec!["https://watcher.example.com".into()]),
            ttl: Some(3600),
            label: Some("rotate after audit".into()),
        };
        let json = serde_json::to_string(&body).unwrap();
        let restored: UpdateDidWebvhBody = serde_json::from_str(&json).unwrap();
        assert_eq!(restored.pre_rotation_count, Some(2));
        assert_eq!(restored.ttl, Some(3600));
        assert_eq!(restored.label.as_deref(), Some("rotate after audit"));
    }

    #[test]
    fn rotate_body_round_trips() {
        let body = RotateDidWebvhKeysBody {
            pre_rotation_count: Some(3),
            label: Some("scheduled".into()),
        };
        let json = serde_json::to_string(&body).unwrap();
        let restored: RotateDidWebvhKeysBody = serde_json::from_str(&json).unwrap();
        assert_eq!(restored.pre_rotation_count, Some(3));
    }

    #[test]
    fn result_body_round_trips() {
        let r = UpdateDidWebvhResultBody {
            did: "did:webvh:abc".into(),
            new_version_id: "3-zVer".into(),
            new_scid: "abc".into(),
            new_log_entry: "{\"versionId\":\"3-...\"}".into(),
            update_keys_count: 1,
            pre_rotation_key_count: 2,
        };
        let json = serde_json::to_string(&r).unwrap();
        let restored: UpdateDidWebvhResultBody = serde_json::from_str(&json).unwrap();
        assert_eq!(restored.update_keys_count, 1);
        assert_eq!(restored.new_version_id, "3-zVer");
    }
}