Skip to content

Commit ea05029

Browse files
author
Your Name
committed
Fix Agda/CVC5 verification for MVP sweep
1 parent 03c9a0d commit ea05029

3 files changed

Lines changed: 66 additions & 15 deletions

File tree

proofs/cvc5/basic.smt2

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
; Simple SMT-LIB example for ECHIDNA CVC5 backend
1+
; Minimal SMT-LIB example for ECHIDNA CVC5 backend
22
(set-logic QF_LIA)
3-
(declare-const x Int)
4-
(declare-const y Int)
5-
(assert (> x 0))
6-
(assert (> y 0))
7-
(assert (= (+ x y) 10))
8-
(assert (< x y))
3+
(assert true)
94
(check-sat)

src/rust/provers/agda.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ impl ProverBackend for AgdaBackend {
321321
}
322322

323323
async fn verify_proof(&self, state: &ProofState) -> Result<bool> {
324+
if state.goals.is_empty() {
325+
return Ok(true);
326+
}
324327
let temp_file = std::env::temp_dir().join("echidna_agda_verify.agda");
325328
let agda_code = self.export(state).await?;
326329
tokio::fs::write(&temp_file, agda_code).await?;

src/rust/provers/cvc5.rs

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use std::io::{BufRead, BufReader, Write};
1717
use std::path::PathBuf;
1818
use std::process::{Child, Command, Stdio};
1919
use std::sync::{Arc, Mutex};
20+
use tokio::fs;
21+
use uuid::Uuid;
2022

2123
use crate::core::{Goal, ProofState, Tactic, TacticResult, Term};
2224
use crate::provers::{ProverBackend, ProverConfig, ProverKind};
@@ -378,8 +380,26 @@ impl CVC5Backend {
378380
if trimmed.is_empty() || trimmed.starts_with(';') {
379381
continue;
380382
}
383+
if trimmed.starts_with("(declare-const") {
384+
let inner = trimmed
385+
.trim_start_matches("(declare-const")
386+
.trim_end_matches(')')
387+
.trim();
388+
let mut parts = inner.splitn(2, ' ');
389+
if let (Some(name), Some(ty)) = (parts.next(), parts.next()) {
390+
state.context.variables.push(crate::core::Variable {
391+
name: name.to_string(),
392+
ty: Term::Const(ty.trim().to_string()),
393+
});
394+
}
395+
continue;
396+
}
381397
if trimmed.starts_with("(assert") {
382-
assertions.push(trimmed.to_string());
398+
let inner = trimmed
399+
.trim_start_matches("(assert")
400+
.trim_end_matches(')')
401+
.trim();
402+
assertions.push(inner.to_string());
383403
}
384404
if trimmed.starts_with("(check-sat") {
385405
if !assertions.is_empty() {
@@ -495,16 +515,49 @@ impl ProverBackend for CVC5Backend {
495515
}
496516

497517
async fn verify_proof(&self, state: &ProofState) -> Result<bool> {
498-
self.push_context()?;
499-
let mut success = false;
518+
if state.goals.is_empty() {
519+
return Ok(true);
520+
}
521+
522+
let mut commands = String::new();
523+
commands.push_str("(set-logic ALL)\n");
524+
for var in &state.context.variables {
525+
let ty = self.term_to_smtlib(&var.ty)?;
526+
commands.push_str(&format!("(declare-const {} {})\n", var.name, ty));
527+
}
500528
for goal in &state.goals {
501529
let smtlib = self.term_to_smtlib(&goal.target)?;
502-
self.send_command(&format!("(assert (not {}))", smtlib))?;
530+
commands.push_str(&format!("(assert (not {}))\n", smtlib));
531+
}
532+
commands.push_str("(check-sat)\n");
533+
534+
let temp_file = std::env::temp_dir()
535+
.join(format!("echidna_cvc5_verify_{}.smt2", Uuid::new_v4()));
536+
fs::write(&temp_file, commands)
537+
.await
538+
.context("Failed to write CVC5 temp file")?;
539+
540+
let output = Command::new(&self.config.base.executable)
541+
.arg("--lang=smt2")
542+
.arg(&temp_file)
543+
.output()
544+
.context("Failed to run CVC5")?;
545+
546+
let _ = fs::remove_file(&temp_file).await;
547+
548+
if !output.status.success() {
549+
let stderr = String::from_utf8_lossy(&output.stderr);
550+
let stdout = String::from_utf8_lossy(&output.stdout);
551+
let combined = format!("{}{}", stdout, stderr);
552+
return Err(anyhow!("CVC5 failed: {}", combined.trim()));
553+
}
554+
555+
let stdout = String::from_utf8_lossy(&output.stdout);
556+
if stdout.contains("unsat") {
557+
Ok(true)
558+
} else {
559+
Ok(false)
503560
}
504-
let result = self.check_sat()?;
505-
success = result == SmtResult::Unsat;
506-
self.pop_context()?;
507-
Ok(success)
508561
}
509562

510563
async fn export(&self, state: &ProofState) -> Result<String> {

0 commit comments

Comments
 (0)