Skip to main content

provable_proof/
verify.rs

1use crate::envelope::KayrosEnvelope;
2use crate::types::{
3    EnvelopeVerifyDetails, EnvelopeVerifyInput, EnvelopeVerifyOverrides, EnvelopeVerifyResult,
4    EnvelopeVerifyWithInclusionOverrides,
5};
6use provable_sdk::{
7    verify, verify_with_inclusion, VerifyRequest, VerifyResult, VerifyResultDetails,
8    VerifyWithInclusionRequest,
9};
10
11fn normalize_hex(value: Option<&str>) -> Option<String> {
12    value.and_then(|input| {
13        let trimmed = input.trim();
14        if trimmed.is_empty() {
15            return None;
16        }
17        let normalized = trimmed.trim_start_matches("0x").to_lowercase();
18        if normalized.len() % 2 == 0 && normalized.chars().all(|ch| ch.is_ascii_hexdigit()) {
19            Some(normalized)
20        } else {
21            None
22        }
23    })
24}
25
26fn invalid_envelope_result(details: EnvelopeVerifyDetails, error: String) -> EnvelopeVerifyResult {
27    EnvelopeVerifyResult {
28        valid: false,
29        error: Some(error),
30        details: Some(details),
31    }
32}
33
34fn merge_details(
35    base: Option<VerifyResultDetails>,
36    extra: EnvelopeVerifyDetails,
37) -> EnvelopeVerifyDetails {
38    EnvelopeVerifyDetails {
39        verify: base.unwrap_or_else(|| {
40            let mut details = VerifyResultDetails::default();
41            details.lookup_mode = if extra.envelope_kayros_hash.is_some() {
42                "kayros_hash".to_string()
43            } else {
44                "data_item".to_string()
45            };
46            details.record_found = false;
47            details
48        }),
49        computed_data_item: extra.computed_data_item,
50        envelope_data_item: extra.envelope_data_item,
51        envelope_data_item_match: extra.envelope_data_item_match,
52        envelope_data_type: extra.envelope_data_type,
53        envelope_kayros_hash: extra.envelope_kayros_hash,
54    }
55}
56
57pub fn build_envelope_verify_request(
58    envelope: &KayrosEnvelope,
59    overrides: &EnvelopeVerifyOverrides,
60) -> provable_sdk::Result<EnvelopeVerifyInput> {
61    let computed_data_item = normalize_hex(Some(&envelope.compute_data_hash()?));
62    let envelope_data_item = envelope.get_data_hash();
63    let envelope_data_type = overrides
64        .data_type
65        .clone()
66        .or_else(|| envelope.get_data_type())
67        .or_else(|| envelope.get_data_type_label());
68    let envelope_kayros_hash = envelope.get_kayros_hash();
69
70    Ok(EnvelopeVerifyInput {
71        request: VerifyRequest {
72            data_type: envelope_data_type.clone(),
73            data_item: overrides
74                .data_item
75                .clone()
76                .or_else(|| computed_data_item.clone())
77                .and_then(|value| normalize_hex(Some(&value))),
78            kayros_hash: overrides
79                .kayros_hash
80                .clone()
81                .or_else(|| envelope_kayros_hash.clone())
82                .and_then(|value| normalize_hex(Some(&value))),
83            api_key: overrides.api_key.clone(),
84        },
85        details: EnvelopeVerifyDetails {
86            verify: VerifyResultDetails::default(),
87            computed_data_item: computed_data_item.clone(),
88            envelope_data_item: envelope_data_item.clone(),
89            envelope_data_item_match: match (
90                envelope_data_item.as_ref(),
91                computed_data_item.as_ref(),
92            ) {
93                (Some(expected), Some(computed)) => Some(expected == computed),
94                _ => None,
95            },
96            envelope_data_type,
97            envelope_kayros_hash,
98        },
99    })
100}
101
102pub fn verify_envelope(
103    envelope: &KayrosEnvelope,
104    overrides: &EnvelopeVerifyOverrides,
105) -> EnvelopeVerifyResult {
106    let input = match build_envelope_verify_request(envelope, overrides) {
107        Ok(input) => input,
108        Err(error) => {
109            return EnvelopeVerifyResult {
110                valid: false,
111                error: Some(error.0),
112                details: None,
113            }
114        }
115    };
116    if input.details.envelope_data_item_match == Some(false) {
117        let expected = input.details.envelope_data_item.clone().unwrap_or_default();
118        let computed = input.details.computed_data_item.clone().unwrap_or_default();
119        return invalid_envelope_result(
120            input.details,
121            format!(
122                "Envelope data_item mismatch: expected={} computed={}",
123                expected, computed
124            ),
125        );
126    }
127
128    let result: VerifyResult = verify(input.request.clone());
129    EnvelopeVerifyResult {
130        valid: result.valid,
131        error: result.error.clone(),
132        details: Some(merge_details(result.details, input.details)),
133    }
134}
135
136pub fn verify_envelope_with_inclusion(
137    envelope: &KayrosEnvelope,
138    overrides: &EnvelopeVerifyWithInclusionOverrides,
139) -> EnvelopeVerifyResult {
140    let input = match build_envelope_verify_request(envelope, &overrides.overrides) {
141        Ok(input) => input,
142        Err(error) => {
143            return EnvelopeVerifyResult {
144                valid: false,
145                error: Some(error.0),
146                details: None,
147            }
148        }
149    };
150    if input.details.envelope_data_item_match == Some(false) {
151        let expected = input.details.envelope_data_item.clone().unwrap_or_default();
152        let computed = input.details.computed_data_item.clone().unwrap_or_default();
153        return invalid_envelope_result(
154            input.details,
155            format!(
156                "Envelope data_item mismatch: expected={} computed={}",
157                expected, computed
158            ),
159        );
160    }
161    let request = VerifyWithInclusionRequest {
162        verify_request: input.request.clone(),
163        trusted_root_hash: overrides.trusted_root_hash.clone(),
164        trusted_level: overrides.trusted_level,
165        trusted_position: overrides.trusted_position,
166        levels_hash_type: overrides.levels_hash_type.clone(),
167        verify_batch_existence: overrides.verify_batch_existence,
168        level_checks: overrides.level_checks.clone(),
169    };
170    let result = verify_with_inclusion(request);
171    EnvelopeVerifyResult {
172        valid: result.valid,
173        error: result.error.clone(),
174        details: Some(merge_details(result.details, input.details)),
175    }
176}