1use crate::proof::{Proof, ProofNodeId};
19use std::collections::HashMap;
20use std::fmt;
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum SafetyProperty {
25 MemorySafety,
27 TypeSafety,
29 ControlFlowIntegrity,
31 ResourceBounds {
33 memory: Option<usize>,
34 time: Option<usize>,
35 },
36 Custom { name: String, description: String },
38}
39
40impl fmt::Display for SafetyProperty {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 Self::MemorySafety => write!(f, "MemorySafety"),
44 Self::TypeSafety => write!(f, "TypeSafety"),
45 Self::ControlFlowIntegrity => write!(f, "ControlFlowIntegrity"),
46 Self::ResourceBounds { memory, time } => {
47 write!(f, "ResourceBounds(memory=")?;
48 if let Some(m) = memory {
49 write!(f, "{}", m)?;
50 } else {
51 write!(f, "∞")?;
52 }
53 write!(f, ", time=")?;
54 if let Some(t) = time {
55 write!(f, "{}", t)?;
56 } else {
57 write!(f, "∞")?;
58 }
59 write!(f, ")")
60 }
61 Self::Custom { name, .. } => write!(f, "Custom({})", name),
62 }
63 }
64}
65
66#[derive(Debug, Clone)]
68pub struct VerificationCondition {
69 pub id: String,
71 pub property: SafetyProperty,
73 pub condition: String,
75 pub location: CodeLocation,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct CodeLocation {
82 pub function: String,
84 pub label: Option<String>,
86 pub line: Option<usize>,
88}
89
90impl fmt::Display for CodeLocation {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 write!(f, "{}", self.function)?;
93 if let Some(label) = &self.label {
94 write!(f, "::{}", label)?;
95 }
96 if let Some(line) = self.line {
97 write!(f, " (line {})", line)?;
98 }
99 Ok(())
100 }
101}
102
103#[derive(Debug)]
107pub struct ProofCarryingCode {
108 code: String,
110 properties: Vec<SafetyProperty>,
112 vcs: Vec<VerificationCondition>,
114 vc_proofs: HashMap<String, ProofNodeId>,
116 proof: Proof,
118}
119
120impl ProofCarryingCode {
121 #[must_use]
123 pub fn new(code: impl Into<String>) -> Self {
124 Self {
125 code: code.into(),
126 properties: Vec::new(),
127 vcs: Vec::new(),
128 vc_proofs: HashMap::new(),
129 proof: Proof::new(),
130 }
131 }
132
133 pub fn add_property(&mut self, property: SafetyProperty) {
135 self.properties.push(property);
136 }
137
138 pub fn add_vc(&mut self, vc: VerificationCondition) {
140 self.vcs.push(vc);
141 }
142
143 pub fn attach_proof(&mut self, vc_id: &str, proof_node: ProofNodeId) {
145 self.vc_proofs.insert(vc_id.to_string(), proof_node);
146 }
147
148 #[must_use]
150 pub fn proof(&self) -> &Proof {
151 &self.proof
152 }
153
154 pub fn proof_mut(&mut self) -> &mut Proof {
156 &mut self.proof
157 }
158
159 #[must_use]
161 pub fn code(&self) -> &str {
162 &self.code
163 }
164
165 #[must_use]
167 pub fn properties(&self) -> &[SafetyProperty] {
168 &self.properties
169 }
170
171 #[must_use]
173 pub fn verification_conditions(&self) -> &[VerificationCondition] {
174 &self.vcs
175 }
176
177 #[must_use]
179 pub fn is_complete(&self) -> bool {
180 self.vcs
181 .iter()
182 .all(|vc| self.vc_proofs.contains_key(&vc.id))
183 }
184
185 #[must_use]
187 pub fn verified_count(&self) -> usize {
188 self.vc_proofs.len()
189 }
190
191 #[must_use]
193 pub fn total_vc_count(&self) -> usize {
194 self.vcs.len()
195 }
196
197 #[must_use]
199 pub fn to_certificate(&self) -> String {
200 let mut cert = String::new();
201 cert.push_str("=== Proof-Carrying Code Certificate ===\n\n");
202
203 cert.push_str("Properties:\n");
204 for prop in &self.properties {
205 cert.push_str(&format!(" - {}\n", prop));
206 }
207 cert.push('\n');
208
209 cert.push_str(&format!(
210 "Verification Status: {}/{} VCs verified\n\n",
211 self.verified_count(),
212 self.total_vc_count()
213 ));
214
215 cert.push_str("Verification Conditions:\n");
216 for vc in &self.vcs {
217 cert.push_str(&format!(
218 " [{}] {} at {}\n",
219 vc.id, vc.property, vc.location
220 ));
221 if self.vc_proofs.contains_key(&vc.id) {
222 cert.push_str(" Status: VERIFIED ✓\n");
223 } else {
224 cert.push_str(" Status: UNVERIFIED ✗\n");
225 }
226 }
227
228 cert.push_str("\n=== End Certificate ===\n");
229 cert
230 }
231}
232
233pub struct PccBuilder {
235 pcc: ProofCarryingCode,
236}
237
238impl PccBuilder {
239 #[must_use]
241 pub fn new(code: impl Into<String>) -> Self {
242 Self {
243 pcc: ProofCarryingCode::new(code),
244 }
245 }
246
247 pub fn with_property(mut self, property: SafetyProperty) -> Self {
249 self.pcc.add_property(property);
250 self
251 }
252
253 pub fn with_vc(mut self, vc: VerificationCondition) -> Self {
255 self.pcc.add_vc(vc);
256 self
257 }
258
259 #[must_use]
261 pub fn build(self) -> ProofCarryingCode {
262 self.pcc
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn test_pcc_creation() {
272 let pcc = ProofCarryingCode::new("fn safe_add(a: i32, b: i32) -> i32 { a + b }");
273 assert_eq!(pcc.total_vc_count(), 0);
274 assert_eq!(pcc.verified_count(), 0);
275 assert!(pcc.is_complete());
276 }
277
278 #[test]
279 fn test_add_property() {
280 let mut pcc = ProofCarryingCode::new("code");
281 pcc.add_property(SafetyProperty::MemorySafety);
282 assert_eq!(pcc.properties().len(), 1);
283 }
284
285 #[test]
286 fn test_add_vc() {
287 let mut pcc = ProofCarryingCode::new("code");
288 let vc = VerificationCondition {
289 id: "vc1".to_string(),
290 property: SafetyProperty::TypeSafety,
291 condition: "typeof(x) = int".to_string(),
292 location: CodeLocation {
293 function: "main".to_string(),
294 label: Some("entry".to_string()),
295 line: Some(10),
296 },
297 };
298 pcc.add_vc(vc);
299 assert_eq!(pcc.total_vc_count(), 1);
300 assert!(!pcc.is_complete());
301 }
302
303 #[test]
304 fn test_attach_proof() {
305 let mut pcc = ProofCarryingCode::new("code");
306 let vc = VerificationCondition {
307 id: "vc1".to_string(),
308 property: SafetyProperty::TypeSafety,
309 condition: "typeof(x) = int".to_string(),
310 location: CodeLocation {
311 function: "main".to_string(),
312 label: None,
313 line: Some(5),
314 },
315 };
316 pcc.add_vc(vc);
317
318 let proof_node = pcc.proof_mut().add_axiom("typeof(x) = int");
319 pcc.attach_proof("vc1", proof_node);
320
321 assert!(pcc.is_complete());
322 assert_eq!(pcc.verified_count(), 1);
323 }
324
325 #[test]
326 fn test_pcc_builder() {
327 let pcc = PccBuilder::new("safe code")
328 .with_property(SafetyProperty::MemorySafety)
329 .with_property(SafetyProperty::TypeSafety)
330 .with_vc(VerificationCondition {
331 id: "vc1".to_string(),
332 property: SafetyProperty::MemorySafety,
333 condition: "bounds_check(array, index)".to_string(),
334 location: CodeLocation {
335 function: "access".to_string(),
336 label: None,
337 line: Some(15),
338 },
339 })
340 .build();
341
342 assert_eq!(pcc.properties().len(), 2);
343 assert_eq!(pcc.total_vc_count(), 1);
344 }
345
346 #[test]
347 fn test_certificate_generation() {
348 let mut pcc = ProofCarryingCode::new("test code");
349 pcc.add_property(SafetyProperty::MemorySafety);
350
351 let cert = pcc.to_certificate();
352 assert!(cert.contains("Proof-Carrying Code Certificate"));
353 assert!(cert.contains("MemorySafety"));
354 }
355
356 #[test]
357 fn test_resource_bounds_display() {
358 let prop = SafetyProperty::ResourceBounds {
359 memory: Some(1024),
360 time: Some(100),
361 };
362 let s = format!("{}", prop);
363 assert!(s.contains("1024"));
364 assert!(s.contains("100"));
365 }
366
367 #[test]
368 fn test_code_location_display() {
369 let loc = CodeLocation {
370 function: "process".to_string(),
371 label: Some("loop_head".to_string()),
372 line: Some(42),
373 };
374 let s = format!("{}", loc);
375 assert!(s.contains("process"));
376 assert!(s.contains("loop_head"));
377 assert!(s.contains("42"));
378 }
379}