use crate::did::web::WebResolver;
use crate::error::AcdpError;
use crate::types::primitives::{ContentHash, CtxId};
use crate::types::receipt::RegistryReceipt;
pub async fn verify_receipt_value(
value: &serde_json::Value,
expected_ctx_id: &CtxId,
body: &crate::types::body::Body,
recomputed_body_hash: &ContentHash,
producer_key_fingerprint: &str,
serving_authority: &str,
resolver: &WebResolver,
) -> Result<RegistryReceipt, AcdpError> {
let receipt = RegistryReceipt::from_value(value)?;
RegistryReceipt::validate_created_at_form(value)?;
let expected_did = crate::did::web::authority_to_did_web(serving_authority);
if receipt.registry_did != expected_did {
return Err(AcdpError::InvalidReceipt(format!(
"receipt registry_did '{}' ≠ serving authority's DID '{expected_did}'",
receipt.registry_did
)));
}
receipt.cross_check(
expected_ctx_id,
recomputed_body_hash,
producer_key_fingerprint,
)?;
receipt.cross_check_body(body)?;
let key_id = &receipt.signature.key_id;
let (did_part, fragment) = key_id.split_once('#').ok_or_else(|| {
AcdpError::InvalidReceipt(format!(
"receipt signature.key_id '{key_id}' has no fragment"
))
})?;
if did_part != receipt.registry_did {
return Err(AcdpError::InvalidReceipt(format!(
"receipt signature.key_id DID '{did_part}' ≠ registry_did '{}'",
receipt.registry_did
)));
}
let doc = resolver.resolve(did_part).await?;
let method = doc.find_by_fragment(fragment).ok_or_else(|| {
AcdpError::InvalidReceipt(format!(
"registry DID document has no verification method '#{fragment}' — \
receipt keys (including retired ones) must remain in verificationMethod"
))
})?;
let raw_hash = RegistryReceipt::preimage_hash_of_value(value)?;
match receipt.signature.algorithm.as_str() {
"ed25519" => {
let key = method
.ed25519_public_key_bytes()
.map_err(|e| AcdpError::InvalidReceipt(format!("receipt key extraction: {e}")))?;
receipt.verify_signature_against_hash(&raw_hash, Some(&key), None)?;
}
"ecdsa-p256" => {
let key = method
.ecdsa_p256_public_key_sec1()
.map_err(|e| AcdpError::InvalidReceipt(format!("receipt key extraction: {e}")))?;
receipt.verify_signature_against_hash(&raw_hash, None, Some(&key))?;
}
other => {
return Err(AcdpError::InvalidReceipt(format!(
"receipt signature algorithm '{other}' is not supported"
)));
}
}
Ok(receipt)
}