Skip to main content

cordance_cortex/
validator.rs

1//! Structural validator for `CortexReceiptV1Candidate`.
2//!
3//! Checks invariants that must hold before the receipt is emitted:
4//! - Correct schema tag.
5//! - `authority_boundary.candidate_only` is `true`.
6//! - All eight authority-grant flags are `false`.
7//! - `forbidden_uses` and `allowed_claim_language` are non-empty.
8
9use cordance_core::receipt::CortexReceiptV1Candidate;
10use cordance_core::schema;
11
12use crate::CortexError;
13
14/// Validate a receipt's structural invariants.
15///
16/// # Errors
17/// Returns `CortexError::Validation` with a description of the first failed
18/// check.
19pub fn validate_receipt(receipt: &CortexReceiptV1Candidate) -> Result<(), CortexError> {
20    // 1. Schema tag.
21    if receipt.schema != schema::CORDANCE_CORTEX_RECEIPT_V1_CANDIDATE {
22        return Err(CortexError::Validation(format!(
23            "wrong schema: expected '{}', got '{}'",
24            schema::CORDANCE_CORTEX_RECEIPT_V1_CANDIDATE,
25            receipt.schema,
26        )));
27    }
28
29    let ab = &receipt.authority_boundary;
30
31    // 2. candidate_only must be true.
32    if !ab.candidate_only {
33        return Err(CortexError::Validation(
34            "authority_boundary.candidate_only must be true".into(),
35        ));
36    }
37
38    // 3. All eight authority-grant flags must be false.
39    if ab.cortex_truth_allowed {
40        return Err(CortexError::Validation(
41            "authority_boundary.cortex_truth_allowed must be false".into(),
42        ));
43    }
44    if ab.cortex_admission_allowed {
45        return Err(CortexError::Validation(
46            "authority_boundary.cortex_admission_allowed must be false".into(),
47        ));
48    }
49    if ab.durable_promotion_allowed {
50        return Err(CortexError::Validation(
51            "authority_boundary.durable_promotion_allowed must be false".into(),
52        ));
53    }
54    if ab.memory_promotion_allowed {
55        return Err(CortexError::Validation(
56            "authority_boundary.memory_promotion_allowed must be false".into(),
57        ));
58    }
59    if ab.doctrine_promotion_allowed {
60        return Err(CortexError::Validation(
61            "authority_boundary.doctrine_promotion_allowed must be false".into(),
62        ));
63    }
64    if ab.trusted_history_allowed {
65        return Err(CortexError::Validation(
66            "authority_boundary.trusted_history_allowed must be false".into(),
67        ));
68    }
69    if ab.release_acceptance_allowed {
70        return Err(CortexError::Validation(
71            "authority_boundary.release_acceptance_allowed must be false".into(),
72        ));
73    }
74    if ab.runtime_authority_allowed {
75        return Err(CortexError::Validation(
76            "authority_boundary.runtime_authority_allowed must be false".into(),
77        ));
78    }
79
80    let body = &receipt.cordance_execution_receipt_v1;
81
82    // 4. forbidden_uses non-empty.
83    if body.forbidden_uses.is_empty() {
84        return Err(CortexError::Validation(
85            "forbidden_uses must not be empty".into(),
86        ));
87    }
88
89    // 5. allowed_claim_language non-empty.
90    if body.allowed_claim_language.is_empty() {
91        return Err(CortexError::Validation(
92            "allowed_claim_language must not be empty".into(),
93        ));
94    }
95
96    Ok(())
97}