#![cfg(feature = "affinidi")]
use std::sync::Arc;
use affinidi_data_integrity::{DataIntegrityProof, SignOptions};
use affinidi_did_resolver_cache_sdk::{config::DIDCacheConfigBuilder, DIDCacheClient};
use affinidi_secrets_resolver::secrets::Secret;
use serde::{Deserialize, Serialize};
use trust_tasks_proof::affinidi::{CachedDidResolver, Verifier};
use trust_tasks_rs::{Payload, Proof, ProofVerifier, TrustTask, VerificationError};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Heartbeat {
issued_by: String,
nonce: u64,
}
impl Payload for Heartbeat {
const TYPE_URI: &'static str = "https://example.com/spec/heartbeat/0.1";
}
async fn local_resolver() -> CachedDidResolver {
let config = DIDCacheConfigBuilder::default().build();
let client = DIDCacheClient::new(config)
.await
.expect("local DIDCacheClient");
CachedDidResolver::new(Arc::new(client))
}
fn fresh_did_key() -> (Secret, String) {
let secret_no_kid = Secret::generate_ed25519(None, Some(&[42u8; 32]));
let pk_mb = secret_no_kid.get_public_keymultibase().expect("multikey");
let vm = format!("did:key:{pk_mb}#{pk_mb}");
let mut secret = Secret::generate_ed25519(Some(&vm), Some(&[42u8; 32]));
secret.id = vm.clone();
(secret, vm)
}
#[tokio::test]
async fn cached_resolver_verifies_did_key_signed_document() {
let resolver = Arc::new(local_resolver().await);
let verifier = Verifier::with_resolver(resolver);
let (secret, vm) = fresh_did_key();
let issuer_did = vm.split('#').next().unwrap().to_string();
let mut doc = TrustTask::for_payload(
"urn:uuid:cached-1",
Heartbeat {
issued_by: issuer_did.clone(),
nonce: 1,
},
);
doc.issuer = Some(issuer_did);
doc.recipient = Some("did:web:auditor.example".into());
doc.issued_at = Some(chrono::Utc::now());
let body = serde_json::to_value(&doc).unwrap();
let proof = DataIntegrityProof::sign(&body, &secret, SignOptions::new())
.await
.expect("sign");
let proof_value = serde_json::to_value(&proof).unwrap();
doc.proof = Some(serde_json::from_value::<Proof>(proof_value).unwrap());
verifier.verify(&doc).await.expect("valid proof verifies");
}
#[tokio::test]
async fn cached_resolver_surfaces_resolver_error_for_unknown_method() {
let resolver = Arc::new(local_resolver().await);
let verifier = Verifier::with_resolver(resolver);
let (secret, _vm) = fresh_did_key();
let mut doc = TrustTask::for_payload(
"urn:uuid:cached-2",
Heartbeat {
issued_by: "did:web:nonexistent.example".into(),
nonce: 2,
},
);
doc.issuer = Some("did:web:nonexistent.example".into());
doc.recipient = Some("did:web:auditor.example".into());
doc.issued_at = Some(chrono::Utc::now());
let body = serde_json::to_value(&doc).unwrap();
let mut proof = DataIntegrityProof::sign(&body, &secret, SignOptions::new())
.await
.expect("sign");
proof.verification_method = "did:web:nonexistent.example#key-0".into();
let proof_value = serde_json::to_value(&proof).unwrap();
doc.proof = Some(serde_json::from_value::<Proof>(proof_value).unwrap());
let err = verifier.verify(&doc).await.unwrap_err();
assert!(
!matches!(err, VerificationError::SignatureInvalid),
"unresolvable VM should not return SignatureInvalid, got {err:?}"
);
}