|
|
|
|
| use crate::proof_ir::{ProofTerm, ProofContext, ProofObligation};
|
| use crate::checker::TypeChecker;
|
| use crate::rollback::{RollbackManager, WormCheckpoint};
|
| use crate::schema::{ProofEvent, ViolationEvent};
|
| use anyhow::{Result, Context};
|
| use tracing::{warn, error, info};
|
|
|
| pub struct ProofValidator {
|
| checker: TypeChecker,
|
| rollback_mgr: RollbackManager,
|
| proof_context: ProofContext,
|
| violation_count: u64,
|
| }
|
|
|
| impl ProofValidator {
|
| pub fn new(rollback_mgr: RollbackManager) -> Self {
|
| Self {
|
| checker: TypeChecker::new(),
|
| rollback_mgr,
|
| proof_context: ProofContext::new(),
|
| violation_count: 0,
|
| }
|
| }
|
|
|
|
|
|
|
| pub fn validate_mutation(
|
| &mut self,
|
| mutation: &str,
|
| invariants: &[String],
|
| ) -> Result<()> {
|
|
|
| let obligation = ProofObligation::InvariantPreservation {
|
| address: 0,
|
| old_value: 0,
|
| new_value: 0,
|
| invariants: invariants.to_vec(),
|
| };
|
|
|
|
|
| let proof_term = self.proof_context.construct_proof(&obligation)?;
|
|
|
| match self.checker.check(&proof_term) {
|
| Ok(_) => {
|
|
|
| self.proof_context.add_proof(obligation, proof_term);
|
| self.emit_audit(ProofEvent::Validated {
|
| mutation: mutation.to_string(),
|
| });
|
| Ok(())
|
| }
|
| Err(e) => {
|
| self.violation_count += 1;
|
| error!(?mutation, violation = %e, "Invariant violation detected");
|
|
|
|
|
| self.emit_audit(ProofEvent::Violated {
|
| mutation: mutation.to_string(),
|
| error: e.to_string(),
|
| violation_id: self.violation_count,
|
| });
|
|
|
|
|
| let checkpoint = self.rollback_mgr.last_valid_checkpoint()
|
| .context("No valid checkpoint for rollback")?;
|
|
|
| self.rollback_mgr.rollback(&checkpoint)?;
|
|
|
|
|
| self.emit_audit(ProofEvent::RolledBack {
|
| checkpoint: checkpoint.id,
|
| violation_id: self.violation_count,
|
| });
|
|
|
| Err(anyhow::anyhow!("Invariant violated, rolled back: {}", e))
|
| }
|
| }
|
| }
|
|
|
|
|
| pub fn add_checkpoint(&mut self, checkpoint: WormCheckpoint) {
|
| self.rollback_mgr.add_checkpoint(checkpoint);
|
| }
|
|
|
| fn emit_audit(&self, event: ProofEvent) {
|
|
|
| info!("Proof audit: {:?}", event);
|
| }
|
|
|
| pub fn violation_count(&self) -> u64 {
|
| self.violation_count
|
| }
|
| }
|
|
|
| impl Default for ProofValidator {
|
| fn default() -> Self {
|
| Self::new(RollbackManager::default())
|
| }
|
| }
|
|
|
| #[cfg(test)]
|
| mod tests {
|
| use super::*;
|
|
|
| #[test]
|
| fn test_validator_creation() {
|
| let validator = ProofValidator::default();
|
| assert_eq!(validator.violation_count(), 0);
|
| }
|
|
|
| #[test]
|
| fn test_validate_mutation() {
|
| let mut validator = ProofValidator::default();
|
| let result = validator.validate_mutation("M[0] ← 42", &["M[0] > 0".into()]);
|
|
|
| let _ = result;
|
| }
|
|
|
| #[test]
|
| fn test_add_checkpoint() {
|
| let mut validator = ProofValidator::default();
|
| let cp = WormCheckpoint {
|
| id: "cp1".into(),
|
| ip: 0,
|
| step_count: 0,
|
| mutation_log_len: 0,
|
| timestamp: 0,
|
| memory_snapshot: vec![],
|
| };
|
| validator.add_checkpoint(cp);
|
| assert!(validator.rollback_mgr.last_valid_checkpoint().is_some());
|
| }
|
| }
|
|
|