@@ -17,6 +17,8 @@ use std::io::{BufRead, BufReader, Write};
1717use std:: path:: PathBuf ;
1818use std:: process:: { Child , Command , Stdio } ;
1919use std:: sync:: { Arc , Mutex } ;
20+ use tokio:: fs;
21+ use uuid:: Uuid ;
2022
2123use crate :: core:: { Goal , ProofState , Tactic , TacticResult , Term } ;
2224use 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