Skip to main content

ves_stark_verifier/
error.rs

1//! Error types for the VES STARK verifier
2
3use thiserror::Error;
4
5/// Current proof version (internal; used for compatibility checks where applicable)
6pub const PROOF_VERSION: u32 = 2;
7
8/// Maximum allowed proof size in bytes (10 MB)
9pub const MAX_PROOF_SIZE: usize = 10 * 1024 * 1024;
10
11/// Errors that can occur during proof verification
12#[derive(Debug, Error)]
13pub enum VerifierError {
14    /// Invalid proof structure
15    #[error("Invalid proof structure: {0}")]
16    InvalidProofStructure(String),
17
18    /// Public input mismatch
19    #[error("Public input mismatch: {0}")]
20    PublicInputMismatch(String),
21
22    /// FRI verification failed
23    #[error("FRI verification failed: {0}")]
24    FriVerificationFailed(String),
25
26    /// Constraint check failed
27    #[error("Constraint check failed: {0}")]
28    ConstraintCheckFailed(String),
29
30    /// Deserialization error
31    #[error("Deserialization error: {0}")]
32    DeserializationError(String),
33
34    /// Invalid policy hash
35    #[error("Invalid policy hash: expected {expected}, got {actual}")]
36    InvalidPolicyHash { expected: String, actual: String },
37
38    /// Proof verification failed
39    #[error("Proof verification failed: {0}")]
40    VerificationFailed(String),
41
42    // V2 Security Hardening - New Error Types
43    /// Policy ID mismatch between proof and expected policy
44    #[error(
45        "Policy mismatch: expected policy '{expected}', but proof was generated for '{actual}'"
46    )]
47    PolicyMismatch { expected: String, actual: String },
48
49    /// Limit value mismatch between proof and expected policy
50    #[error("Limit mismatch: expected {expected}, but proof was generated for {actual}")]
51    LimitMismatch { expected: u64, actual: u64 },
52
53    /// Invalid hex format in public inputs
54    #[error("Invalid hex format in field '{field}': {reason}")]
55    InvalidHexFormat { field: String, reason: String },
56
57    /// Unsupported proof version
58    #[error("Unsupported proof version {version}: only version {supported} is supported")]
59    UnsupportedProofVersion { version: u32, supported: u32 },
60
61    /// Witness commitment mismatch
62    #[error(
63        "Witness commitment mismatch: the proof's commitment doesn't match the expected commitment"
64    )]
65    WitnessCommitmentMismatch,
66
67    /// Strict verification requires a payload-derived amount binding artifact.
68    #[error("Payload amount binding required: {0}")]
69    PayloadAmountBindingRequired(String),
70
71    /// Proof is too large
72    #[error("Proof too large: {size} bytes exceeds maximum of {max_size} bytes")]
73    ProofTooLarge { size: usize, max_size: usize },
74}
75
76impl VerifierError {
77    /// Create an invalid proof structure error
78    pub fn invalid_structure<S: Into<String>>(msg: S) -> Self {
79        Self::InvalidProofStructure(msg.into())
80    }
81
82    /// Create a verification failed error
83    pub fn verification_failed<S: Into<String>>(msg: S) -> Self {
84        Self::VerificationFailed(msg.into())
85    }
86
87    /// Create a policy mismatch error
88    pub fn policy_mismatch(expected: &str, actual: &str) -> Self {
89        Self::PolicyMismatch {
90            expected: expected.to_string(),
91            actual: actual.to_string(),
92        }
93    }
94
95    /// Create a limit mismatch error
96    pub fn limit_mismatch(expected: u64, actual: u64) -> Self {
97        Self::LimitMismatch { expected, actual }
98    }
99
100    /// Create an invalid hex format error
101    pub fn invalid_hex(field: &str, reason: &str) -> Self {
102        Self::InvalidHexFormat {
103            field: field.to_string(),
104            reason: reason.to_string(),
105        }
106    }
107
108    /// Create an unsupported proof version error
109    pub fn unsupported_version(version: u32) -> Self {
110        Self::UnsupportedProofVersion {
111            version,
112            supported: PROOF_VERSION,
113        }
114    }
115}
116
117/// Validate a hex string field
118///
119/// Returns Ok(()) if the hex string is valid, or an error describing the issue.
120/// Valid hex strings must:
121/// - Have the expected length
122/// - Contain only lowercase hex digits (0-9, a-f)
123pub fn validate_hex_string(
124    field: &str,
125    value: &str,
126    expected_len: usize,
127) -> Result<(), VerifierError> {
128    // Check length
129    if value.len() != expected_len {
130        return Err(VerifierError::invalid_hex(
131            field,
132            &format!("expected {} characters, got {}", expected_len, value.len()),
133        ));
134    }
135
136    // Check that all characters are lowercase hex digits
137    for (i, c) in value.chars().enumerate() {
138        if !c.is_ascii_hexdigit() {
139            return Err(VerifierError::invalid_hex(
140                field,
141                &format!("invalid character '{}' at position {}", c, i),
142            ));
143        }
144        if c.is_ascii_uppercase() {
145            return Err(VerifierError::invalid_hex(
146                field,
147                &format!(
148                    "uppercase character '{}' at position {} (must be lowercase)",
149                    c, i
150                ),
151            ));
152        }
153    }
154
155    Ok(())
156}