ruvector_verified/
proof_store.rs1pub const WITNESS_TYPE_FORMAL_PROOF: u8 = 0x0E;
9
10#[derive(Debug, Clone)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct ProofAttestation {
17 pub proof_term_hash: [u8; 32],
19 pub environment_hash: [u8; 32],
21 pub verification_timestamp_ns: u64,
23 pub verifier_version: u32,
25 pub reduction_steps: u32,
27 pub cache_hit_rate_bps: u16,
29}
30
31pub const ATTESTATION_SIZE: usize = 32 + 32 + 8 + 4 + 4 + 2; impl ProofAttestation {
35 pub fn new(
37 proof_term_hash: [u8; 32],
38 environment_hash: [u8; 32],
39 reduction_steps: u32,
40 cache_hit_rate_bps: u16,
41 ) -> Self {
42 Self {
43 proof_term_hash,
44 environment_hash,
45 verification_timestamp_ns: current_timestamp_ns(),
46 verifier_version: 0x00_01_00_00, reduction_steps,
48 cache_hit_rate_bps,
49 }
50 }
51
52 pub fn to_bytes(&self) -> Vec<u8> {
54 let mut buf = Vec::with_capacity(ATTESTATION_SIZE);
55 buf.extend_from_slice(&self.proof_term_hash);
56 buf.extend_from_slice(&self.environment_hash);
57 buf.extend_from_slice(&self.verification_timestamp_ns.to_le_bytes());
58 buf.extend_from_slice(&self.verifier_version.to_le_bytes());
59 buf.extend_from_slice(&self.reduction_steps.to_le_bytes());
60 buf.extend_from_slice(&self.cache_hit_rate_bps.to_le_bytes());
61 buf
62 }
63
64 pub fn from_bytes(data: &[u8]) -> Result<Self, &'static str> {
66 if data.len() < ATTESTATION_SIZE {
67 return Err("attestation data too short");
68 }
69
70 let mut proof_term_hash = [0u8; 32];
71 proof_term_hash.copy_from_slice(&data[0..32]);
72
73 let mut environment_hash = [0u8; 32];
74 environment_hash.copy_from_slice(&data[32..64]);
75
76 let verification_timestamp_ns = u64::from_le_bytes(
77 data[64..72].try_into().map_err(|_| "bad timestamp")?
78 );
79 let verifier_version = u32::from_le_bytes(
80 data[72..76].try_into().map_err(|_| "bad version")?
81 );
82 let reduction_steps = u32::from_le_bytes(
83 data[76..80].try_into().map_err(|_| "bad steps")?
84 );
85 let cache_hit_rate_bps = u16::from_le_bytes(
86 data[80..82].try_into().map_err(|_| "bad rate")?
87 );
88
89 Ok(Self {
90 proof_term_hash,
91 environment_hash,
92 verification_timestamp_ns,
93 verifier_version,
94 reduction_steps,
95 cache_hit_rate_bps,
96 })
97 }
98
99 pub fn content_hash(&self) -> u64 {
101 let bytes = self.to_bytes();
102 let mut h: u64 = 0xcbf29ce484222325;
103 for &b in &bytes {
104 h ^= b as u64;
105 h = h.wrapping_mul(0x100000001b3);
106 }
107 h
108 }
109}
110
111pub fn create_attestation(
113 env: &crate::ProofEnvironment,
114 proof_id: u32,
115) -> ProofAttestation {
116 let mut proof_hash = [0u8; 32];
118 let id_bytes = proof_id.to_le_bytes();
119 proof_hash[0..4].copy_from_slice(&id_bytes);
120 proof_hash[4..8].copy_from_slice(&env.terms_allocated().to_le_bytes());
121
122 let mut env_hash = [0u8; 32];
123 let sym_count = env.symbols.len() as u32;
124 env_hash[0..4].copy_from_slice(&sym_count.to_le_bytes());
125
126 let stats = env.stats();
127 let cache_rate = if stats.cache_hits + stats.cache_misses > 0 {
128 ((stats.cache_hits * 10000) / (stats.cache_hits + stats.cache_misses)) as u16
129 } else {
130 0
131 };
132
133 ProofAttestation::new(
134 proof_hash,
135 env_hash,
136 stats.total_reductions as u32,
137 cache_rate,
138 )
139}
140
141fn current_timestamp_ns() -> u64 {
143 std::time::SystemTime::now()
144 .duration_since(std::time::UNIX_EPOCH)
145 .map(|d| d.as_nanos() as u64)
146 .unwrap_or(0)
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use crate::ProofEnvironment;
153
154 #[test]
155 fn test_witness_type_code() {
156 assert_eq!(WITNESS_TYPE_FORMAL_PROOF, 0x0E);
157 }
158
159 #[test]
160 fn test_attestation_size() {
161 assert_eq!(ATTESTATION_SIZE, 82);
162 }
163
164 #[test]
165 fn test_attestation_roundtrip() {
166 let att = ProofAttestation::new([1u8; 32], [2u8; 32], 42, 9500);
167 let bytes = att.to_bytes();
168 assert_eq!(bytes.len(), ATTESTATION_SIZE);
169
170 let att2 = ProofAttestation::from_bytes(&bytes).unwrap();
171 assert_eq!(att.proof_term_hash, att2.proof_term_hash);
172 assert_eq!(att.environment_hash, att2.environment_hash);
173 assert_eq!(att.verifier_version, att2.verifier_version);
174 assert_eq!(att.reduction_steps, att2.reduction_steps);
175 assert_eq!(att.cache_hit_rate_bps, att2.cache_hit_rate_bps);
176 }
177
178 #[test]
179 fn test_attestation_from_bytes_too_short() {
180 let result = ProofAttestation::from_bytes(&[0u8; 10]);
181 assert!(result.is_err());
182 }
183
184 #[test]
185 fn test_attestation_content_hash() {
186 let att1 = ProofAttestation::new([1u8; 32], [2u8; 32], 42, 9500);
187 let att2 = ProofAttestation::new([1u8; 32], [2u8; 32], 42, 9500);
188 let _h1 = att1.content_hash();
192 let _h2 = att2.content_hash();
193 }
194
195 #[test]
196 fn test_create_attestation() {
197 let mut env = ProofEnvironment::new();
198 let proof_id = env.alloc_term();
199 let att = create_attestation(&env, proof_id);
200 assert_eq!(att.verifier_version, 0x00_01_00_00);
201 assert!(att.verification_timestamp_ns > 0);
202 }
203
204 #[test]
205 fn test_verifier_version() {
206 let att = ProofAttestation::new([0u8; 32], [0u8; 32], 0, 0);
207 assert_eq!(att.verifier_version, 0x00_01_00_00);
208 }
209}