open-standards/smart-byte/smart-byte-rs/crates/smart-byte-vc/tests/w3c_vectors.rs
Transaction Science 07c4519e6c
Some checks failed
wai-fleet service / does this change touch it (push) Successful in 26s
joule-moq-http live HTTP egress / joule-moq-http · does this change touch it (push) Successful in 25s
WAI operation catalogue / does this change touch it (push) Successful in 27s
wai-int-module WAISM module of record / does this change touch it (push) Successful in 27s
wai-pack / wai-pack · does this change touch it (push) Successful in 18s
wai-quantum command line / does this change touch it (push) Successful in 16s
wai-quantum clippy / does this change touch it (push) Successful in 20s
wai-quantum contract / does this change touch it (push) Successful in 26s
wai-quantum docs, course and locales / does this change touch it (push) Successful in 22s
wai-quantum editors / does this change touch it (push) Successful in 21s
wai-quantum resource estimation / does this change touch it (push) Successful in 23s
wai-quantum for JavaScript / does this change touch it (push) Successful in 25s
wai-quantum-landscape-fpga reference design / does this change touch it (push) Successful in 24s
wai-quantum other formats / does this change touch it (push) Successful in 28s
wai-quantum OpenQASM reader / does this change touch it (push) Successful in 14s
wai-quantum pulse and dynamics / does this change touch it (push) Successful in 18s
wai-quantum for Python / does this change touch it (push) Successful in 16s
wai-quantum error correction and mitigation / does this change touch it (push) Successful in 21s
wai-quantum qudit jobs / does this change touch it (push) Successful in 16s
wai-ledger service / does this change touch it (push) Successful in 3m1s
wai-app and Create / wai-app · does this change touch it (push) Successful in 3m17s
wai-key-proxy / does this change touch it (push) Successful in 3m1s
wai-audit collector and player integration / does this change touch it (push) Successful in 3m12s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 3m20s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 3m18s
wai-ops-gpu integer operators on GPUs / does this change touch it (push) Successful in 2m58s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 3m18s
wai-quantum studio and desktop / does this change touch it (push) Successful in 19s
wai-quantum extension points / does this change touch it (push) Successful in 21s
wai-quantum service / does this change touch it (push) Successful in 18s
wai-quantum variational and algorithm library / does this change touch it (push) Successful in 17s
wai-quantum verdicts and repairs / does this change touch it (push) Successful in 55s
wai-quantum views in agent hosts / does this change touch it (push) Successful in 2m39s
wai-quantum on Windows / does this change touch it (push) Successful in 2m35s
WAI Studio / does this change touch it (push) Successful in 2m16s
wai-sink-swift native sink package / does this change touch it (push) Successful in 2m28s
wai-sink-android native sink library / does this change touch it (push) Successful in 2m38s
wai-ops-gpu integer operators on GPUs / Vulkan GPUs (jetson-hub) (push) Successful in 11m30s
wai-quantum service / service energy · linux packages (push) Failing after 12s
wai-ops-gpu integer operators on GPUs / Vulkan GPUs (framework-128gb) (push) Successful in 14m28s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (push) Successful in 29s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 21s
WAI examples / WAI examples · does this change touch them (push) Successful in 16s
wai-quantum landscape energy to solution / does this change touch it (push) Successful in 11s
wai-int-module WAISM module of record / the module of record in Chromium (push) Successful in 29s
wai-quantum landscape stage 0, the sampler against the exact law / does this change touch it (push) Successful in 20s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 17s
wai-quantum wasmQ / does this change touch it (push) Successful in 15s
wai-fleet service / service · linux (push) Successful in 3m12s
wai-gstreamer / wai-gstreamer · the plugin on a distribution's GStreamer (push) Successful in 3m38s
wai web player plugins / the browser run's room compiles · linux (push) Successful in 41s
WAI operation catalogue / the catalogue and every door · linux (push) Failing after 3m56s
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Successful in 14s
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 5m53s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Successful in 21s
wai web player plugins / the plugins' core under Node · linux (push) Successful in 3m46s
wai tv web application / the tv application, built to its baseline and checked · linux (push) Successful in 4m2s
wai-int-module WAISM module of record / linux · both checkers, wasmtime (Cranelift, Winch), Wasmi and Node (push) Successful in 7m8s
wai-quantum clippy / clippy, warnings denied, every feature set · linux (push) Successful in 5m58s
wai-pack / wai-pack · linux (push) Successful in 6m24s
wai-quantum editors / editors · linux (push) Successful in 4m5s
wai-quantum OpenQASM reader / corpus under wasm32-wasip2 (wasmtime) (push) Successful in 1m46s
wai-quantum contract / API compatibility · linux (push) Successful in 6m29s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs under wasm32-wasip2 (wasmtime) (push) Successful in 1m14s
wai-quantum other formats / the set under wasm32-wasip2 (wasmtime) (push) Successful in 3m4s
wai-quantum for JavaScript / javascript · linux (push) Successful in 6m37s
wai-quantum qudit jobs / the module and the conformance set under wasm32-wasip2 (wasmtime) (push) Successful in 1m58s
wai-quantum OpenQASM reader / reader and corpus · linux (push) Successful in 4m17s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Successful in 38s
wai-quantum other formats / readers, writers and the set · linux (push) Successful in 6m55s
wai-ledger service / service · linux (push) Successful in 1m59s
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Successful in 10m10s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs · linux, release (push) Successful in 5m52s
wai-quantum error correction and mitigation / codes, decoders, texts and mitigation · linux, release (push) Successful in 5m24s
wai-quantum extension points / the registry under wasm32-wasip2 (wasmtime) (push) Successful in 2m32s
wai-key-proxy / proxy · linux (push) Successful in 3m0s
wai-quantum resource estimation / estimation and calibration · linux, release (push) Successful in 10m50s
wai-quantum qudit jobs / the module, the job, the conformance set, the referee and the verb · linux (push) Successful in 5m41s
wai-quantum studio and desktop / studio · browser (push) Successful in 3m59s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (push) Successful in 2m46s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (push) Successful in 2m31s
wai-audit collector and player integration / collector · linux (push) Successful in 6m17s
wai-quantum extension points / the registry and the example · linux (push) Successful in 9m14s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m54s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (push) Successful in 6m17s
wai-quantum for Python / python · linux (push) Successful in 14m26s
wai-quantum views in agent hosts / views · a scripted host (push) Successful in 5m45s
wai-quantum views in agent hosts / views · the page and its engine (push) Successful in 5m55s
wai-quantum contract / schemas and the committed tool lists · linux (push) Successful in 23m16s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 13m17s
wai-quantum service / service · linux (push) Successful in 13m56s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (push) Successful in 17m15s
wai-quantum-landscape-fpga reference design / the member against the model of the logic, and the logic against the model · linux, release (push) Successful in 26m2s
wai-quantum command line / command line · linux (push) Successful in 42m5s
WAI Studio / Studio in WebKit · linux (push) Failing after 23m6s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Failing after 44m9s
joule-moq-http live HTTP egress / joule-moq-http, every target (push) Successful in 3m11s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Failing after 9m18s
wai-pack / wai-pack · linux-arm64 (push) Successful in 4m44s
wai-pack / wai-pack · the floor through a media prober (push) Failing after 1m4s
wai-pack / wai-pack · dependency licences (push) Successful in 2m14s
wai-int-module WAISM module of record / linux-arm64 · both checkers, wasmtime (Cranelift, Winch), Wasmi and Node (push) Successful in 8m0s
wai-quantum-landscape-fpga reference design / the board's code on the board's processor's architecture · linux-arm64, release (push) Successful in 2m39s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (push) Successful in 5m34s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Successful in 2m21s
joule-moq-room room service / joule-moq-room, every target (push) Failing after 8m45s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (push) Successful in 3m11s
wai-quantum extension points / the registry and the example · linux-arm64 (push) Successful in 7m8s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (push) Successful in 51s
wai-fleet service / service · macos (push) Successful in 44s
wai-pack / wai-pack · independent verifier (Python) (push) Successful in 22m28s
wai-quantum editors / editors · macos (push) Successful in 1m35s
wai-quantum command line / command line · macos (push) Successful in 3m13s
wai-quantum pulse and dynamics / the same bits · macos (push) Successful in 1m17s
wai-pack / wai-pack · macos (push) Successful in 3m58s
wai-ledger service / service · macos (push) Successful in 1m17s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 8m30s
wai-quantum qudit jobs / the same bits · macos (push) Successful in 2m9s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 1m0s
wai-quantum for Python / python · macos (push) Successful in 3m37s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (push) Failing after 8m26s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Failing after 7m31s
joule-code conformance gate / joule-moq-ingest, every target (push) Failing after 47m41s
joule-code conformance gate / joule-moq-relay, every target (push) Failing after 47m42s
wai-quantum service / service · macos (push) Successful in 8m7s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m10s
joule-moq-http in browsers / the egress and the protected room in Chromium, Firefox and WebKit · macos (push) Successful in 15m26s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 4m42s
wai peer delivery in browsers / peer delivery in Chromium, Firefox and WebKit · macos (push) Successful in 7m2s
wai protected floor in browsers / the protected floor in Chromium, Firefox and WebKit · macos (push) Successful in 2m55s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Successful in 16m18s
wai tv web application / the tv application in Chromium, Firefox and WebKit · macos (push) Successful in 7m8s
WAI Studio / Studio in Chromium and Firefox · macos (push) Failing after 11m49s
wai web player plugins / the three players in Chromium, Firefox and WebKit · macos (push) Successful in 18m59s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 22m40s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 15m51s
wai release binaries build on Windows / the seven release binaries · windows (push) Has been cancelled
wai-quantum on Windows / engine · windows (push) Has been cancelled
wai-quantum on Windows / command line · windows (push) Has been cancelled
wai-quantum on Windows / job service · windows (push) Has been cancelled
wai-quantum on Windows / python · windows (push) Has been cancelled
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · linux (push) Has been cancelled
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · macos (push) Has been cancelled
wai-quantum wasmQ / the reference modules rebuild to their modules of record · linux (push) Has been cancelled
wai-quantum wasmQ / interpreter and vectors under wasm32-wasip2 (wasmtime) (push) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (push) Has been cancelled
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Has been cancelled
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Has been cancelled
wai-quantum energy-landscape jobs / the same bits · macos (push) Has been cancelled
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Has been cancelled
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Has been cancelled
wai-quantum landscape stage 0, the sampler against the exact law / the small tier, to the byte · linux (push) Has been cancelled
wai-quantum landscape stage 0, the sampler against the exact law / the small tier, to the byte · macos (push) Has been cancelled
wai-quantum landscape energy to solution / the small tier, to the byte · linux (push) Has been cancelled
wai-quantum landscape energy to solution / the small tier, to the byte · macos (push) Has been cancelled
WAI examples / WAI examples · portable · linux (push) Has been cancelled
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (push) Has been cancelled
WAI examples / WAI examples · android-app · linux-arm64 (push) Has been cancelled
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Has been cancelled
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Has been cancelled
ed25519-dalek 3 across the standard: the dalek and RustCrypto generation together
Every requirement on ed25519-dalek 2, x25519-dalek 2, curve25519-dalek 4 and
signature 2 moves to 3, 3, 5 and 3 (51 in 48 manifests); hpke 0.13 -> 0.15. 52
lockfiles now resolve ed25519-dalek 3.0.0, the released stable. Each 2.x
feature set maps to the 3.x set with the same API: 2's defaults (fast, std,
zeroize) are 3's defaults plus `alloc`, which 3 no longer turns on and which
PKCS#8 encoding and `Signature::to_vec` need; 2's `std` is 3's `alloc`.

Key generation. dalek 3's `generate` takes a rand_core 0.10 RNG, and every
site passed a rand_core 0.6 one. dalek 2's `generate` was fill 32 bytes, then
`from_bytes`; that body is written out at the 50 call sites with the RNG each
already used, so no RNG or dependency changes. op-screening and
joule-code-identity take the 32 bytes from getrandom directly.

- jouleclaw-hpke on hpke 0.15 and rand 0.10. Measured across versions first:
  a seed names the same public key under 0.13 + rand 0.9 and 0.15 + rand
  0.10; the stored secret is now the clamped scalar of the same X25519 key,
  and a secret stored by either version opens what the other sealed. A test
  pins the seeded key. The suite is asked for by name (hkdfsha2 is no longer
  a default, ML-KEM is and is not used here).
- wai-rs's corpus signs with hazmat::raw_sign over dalek's own Sha512.
  HPKE's X25519 and the claim corpus's curve arithmetic are unchanged on 5.
- smart-byte: iroh 1.0.0-rc.0 pinned ed25519-dalek =3.0.0-pre.7; iroh 1 and
  iroh-blobs 0.104 take the stable 3. iroh-blobs is not called yet.
- sandbox-tee's amd-kds: c28dd2e46 moved sandbox to sha2 0.11 with rsa 0.9
  (digest 0.10) still under it, and the feature has not compiled since; the
  nightly-only job that builds it had not run. rsa 0.10 (digest 0.11,
  signature 3), and the test chain built field by field and signed over its
  DER, so x509-cert's builder and the signature traits it ties RSA to are not
  needed. 52 tests pass with amd-kds,intel-pcs.

Left where a third party has not released on the new curve: c2pa (wai-rs),
openmls and frost-ed25519 (joule-code, jcp-threshold), bulletproofs and snow
(smart-byte; smart-byte-zk keeps curve 4.1 because its scalars go into
bulletproofs, and says so), and the libp2p 0.56 and hpke-rs 0.7 lines.
Each is handed bytes, never a key type of ours.

Checked: all 53 workspaces `cargo check --all-targets` with every feature,
or the defaults where every feature fails for a reason the log shows is not
this (CUDA, livekit, an old wasm-bindgen); wai-rs as CI builds it and with
codecs and neural on macOS; gstreamer on Linux; the desktop app after the
Studio. Tests: jcp-rs 755 (one RAPL timing test failed once at load 229 on
the arm64 host, passes alone on main there and in the full run), wai-rs lib
1252, joule-code 694 + 88, smart-byte 183, op-screening 50, sandbox-tee 52,
joulecontract 17, jouleclaw-hpke 12. The wasmQ modules of record rebuild
byte for byte on x86_64 Linux (main's lockfile first, then this one): no
dalek code is in them, so only the lockfile digest moves. The views page's
script and styles are unchanged; its engine moved with its dependencies.
lockfiles current, advisories: nothing new.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-12 00:15:14 -05:00

225 lines
7.9 KiB
Rust

//! Integration tests against W3C VCDM 2.0 test vectors and the
//! VC-JWT / SD-JWT / Status List / cargo-bridge end-to-end paths.
use chrono::TimeZone;
use ed25519_dalek::SigningKey;
use rand::rngs::OsRng;
use serde_json::json;
use smart_byte_core::{Aid, JouleCost, KeyBook, Said, Signer};
use smart_byte_vc::{
BitstringStatusList, CredentialSubject, Disclosure, Holder, Issuer,
StatusListCredential, StatusPurpose, VC_CARGO_TYPE, VcBuilder,
VerifiableCredential, VerifiablePresentation, check_status,
issue_data_integrity, issue_sd_jwt, issue_vc_jwt, present_sd_jwt,
vc_envelope, vc_from_envelope, verify_data_integrity, verify_sd_jwt,
verify_vc_jwt,
};
fn load_fixture(name: &str) -> VerifiableCredential {
let path = format!(
"{}/tests/fixtures/{}",
env!("CARGO_MANIFEST_DIR"),
name
);
let bytes = std::fs::read(&path).expect("fixture read");
serde_json::from_slice(&bytes).expect("fixture parse")
}
#[test]
fn vcdm_v2_example_roundtrips() {
let vc = load_fixture("vcdm_v2_example.json");
vc.validate_shape().expect("shape");
let jcs_a = vc.to_jcs().expect("jcs a");
let again: VerifiableCredential =
serde_json::from_slice(&jcs_a).expect("reparse");
let jcs_b = again.to_jcs().expect("jcs b");
assert_eq!(jcs_a, jcs_b, "JCS round-trip must be stable");
assert_eq!(vc, again, "structural round-trip");
assert_eq!(vc.issuer.id().as_str(), "https://university.example/issuers/565049");
}
#[test]
fn vcdm_v2_data_integrity_sign_and_verify() {
let vc = load_fixture("vcdm_v2_example.json");
let sk = SigningKey::from_bytes(&{ use rand::RngCore as _; let mut secret = [0u8; 32]; OsRng.fill_bytes(&mut secret); secret });
let vk = sk.verifying_key();
let signed = issue_data_integrity(
vc,
"did:example:issuer#keys-1".parse().expect("iri"),
chrono::Utc.with_ymd_and_hms(2026, 5, 23, 12, 0, 0).single().expect("ts"),
&sk,
)
.expect("issue DI");
verify_data_integrity(&signed, &vk).expect("verify DI");
}
#[test]
fn vc_jwt_known_key_roundtrip() {
let vc = load_fixture("vcdm_v2_example.json");
let sk = SigningKey::from_bytes(&[7u8; 32]);
let vk = sk.verifying_key();
let (jwt, proof) = issue_vc_jwt(
&vc,
&sk,
Some("did:example:issuer#keys-1".to_string()),
)
.expect("issue jwt");
assert!(jwt.split('.').count() == 3);
assert_eq!(proof.type_, "JwtProof2020");
let decoded = verify_vc_jwt(&jwt, &vk).expect("verify jwt");
assert_eq!(decoded, vc);
}
#[test]
fn sd_jwt_discloses_two_of_five() {
let sk = SigningKey::from_bytes(&{ use rand::RngCore as _; let mut secret = [0u8; 32]; OsRng.fill_bytes(&mut secret); secret });
let vk = sk.verifying_key();
let payload = json!({
"given_name": "Alice",
"family_name": "Lockhart",
"birthdate": "1990-01-15",
"email": "[email protected]",
"country": "GB",
"iss": "did:example:issuer",
});
let (combined, disclosures, _proof) = issue_sd_jwt(
&payload,
&[
"given_name",
"family_name",
"birthdate",
"email",
"country",
],
&sk,
Some("did:example:issuer#keys-1".to_string()),
)
.expect("issue sd-jwt");
assert_eq!(disclosures.len(), 5);
let presented = present_sd_jwt(&combined, &["given_name", "country"])
.expect("holder present");
let merged = verify_sd_jwt(&presented, &vk).expect("verify presented");
assert_eq!(merged["given_name"], "Alice");
assert_eq!(merged["country"], "GB");
assert!(merged.get("family_name").is_none());
assert!(merged.get("birthdate").is_none());
assert!(merged.get("email").is_none());
}
#[test]
fn sd_jwt_disclosure_helpers_are_consistent() {
let d = Disclosure {
salt: "_26bc4LT-ac6q2KI6cBW5es".into(),
name: "family_name".into(),
value: json!("Möbius"),
};
let b64 = d.to_base64().expect("encode");
// re-decoding the base64 yields the disclosure JSON array
let bytes = base64::Engine::decode(
&base64::engine::general_purpose::URL_SAFE_NO_PAD,
&b64,
)
.expect("b64 decode");
let arr: serde_json::Value =
serde_json::from_slice(&bytes).expect("disclosure json");
assert_eq!(arr[0], "_26bc4LT-ac6q2KI6cBW5es");
assert_eq!(arr[1], "family_name");
assert_eq!(arr[2], "Möbius");
}
#[test]
fn status_list_revokes_index_42() {
// Issue a fresh status list credential with 1024 indices.
let subj = CredentialSubject {
id: Some("https://example.com/credentials/status/3"
.parse()
.expect("iri")),
claims: serde_json::Map::new(),
};
let status_vc = VcBuilder::new()
.type_tag("BitstringStatusListCredential")
.issuer(Issuer::Uri("did:example:issuer".parse().expect("iri")))
.subject(subj)
.build()
.expect("status vc");
let mut slc = StatusListCredential {
vc: status_vc,
purpose: StatusPurpose::revocation(),
bitstring: BitstringStatusList::new(1024),
};
slc.refresh_subject().expect("initial refresh");
let initial = slc.vc.credential_subject[0]
.claims
.get("encodedList")
.and_then(|v| v.as_str())
.expect("encodedList")
.to_string();
assert!(!check_status(&initial, 1024, 42).expect("check"));
// Revoke index 42.
slc.bitstring.set(42, true).expect("revoke");
slc.refresh_subject().expect("refresh after revoke");
let after = slc.vc.credential_subject[0]
.claims
.get("encodedList")
.and_then(|v| v.as_str())
.expect("encodedList")
.to_string();
assert!(check_status(&after, 1024, 42).expect("check 42"));
assert!(!check_status(&after, 1024, 41).expect("check 41"));
assert!(!check_status(&after, 1024, 43).expect("check 43"));
assert_ne!(initial, after);
}
#[test]
fn status_list_entry_fixture_parses() {
// The fixture carries a credentialStatus pointing at index 42.
let vc = load_fixture("vcdm_v2_with_status.json");
let status = vc.credential_status.as_ref().expect("status");
assert_eq!(status.type_, "BitstringStatusListEntry");
assert_eq!(
status.status_purpose.as_deref(),
Some("revocation")
);
assert_eq!(status.status_list_index.as_deref(), Some("42"));
}
#[test]
fn cargo_bridge_envelopes_a_vc_with_stable_said() {
let vc = load_fixture("vcdm_v2_example.json");
let issuer = Signer::single(
Aid::of_inception(Said::hash(b"vc-issuer")),
0,
SigningKey::from_bytes(&[0x22; 32]),
);
let holder = Aid::of_inception(Said::hash(b"vc-holder"));
let env_a = vc_envelope(&vc, &issuer, holder, [3; 16], JouleCost::measured(11)).expect("envelope a");
let env_b = vc_envelope(&vc, &issuer, holder, [3; 16], JouleCost::measured(11)).expect("envelope b");
// Issuance is deterministic: the same inputs give the same byte.
assert_eq!(env_a.identity(), env_b.identity());
assert_eq!(env_a.said(), env_b.said());
let mut keys = KeyBook::new();
keys.insert(issuer.aid, 0, issuer.key_state());
env_a.verify(&keys).expect("verifies");
// The cargo is registered as a verifiable credential.
assert_eq!(env_a.provenance.cargo.type_tag, VC_CARGO_TYPE);
// Round-trip extraction
let back = vc_from_envelope(&env_a).expect("from envelope");
assert_eq!(back, vc);
}
#[test]
fn presentation_with_holder_and_credential() {
let vc = load_fixture("vcdm_v2_example.json");
let holder = Holder::new("did:example:alice".parse().expect("did"));
let vp = VerifiablePresentation::new()
.expect("vp")
.with_holder(holder.did.clone())
.with_credential(vc);
vp.validate_shape().expect("shape");
assert_eq!(vp.verifiable_credential.len(), 1);
assert_eq!(
vp.holder.as_ref().map(|d| d.to_string()),
Some("did:example:alice".to_string())
);
}