Merge pull request #3840 from ethereum/smt_cvc4

[SMTChecker] Integration with CVC4
This commit is contained in:
Alex Beregszaszi 2018-04-17 14:35:47 +01:00 committed by GitHub
commit f925747050
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 318 additions and 25 deletions

View File

@ -1,12 +1,11 @@
### 0.4.23 (unreleased) ### 0.4.23 (unreleased)
Features: Features:
* SMTChecker: Integration with CVC4 SMT solver
Bugfixes: Bugfixes:
### 0.4.22 (2018-04-16) ### 0.4.22 (2018-04-16)
Features: Features:
@ -34,7 +33,6 @@ Features:
* Syntax Tests: Add source locations to syntax test expectations. * Syntax Tests: Add source locations to syntax test expectations.
* Type Checker: Improve documentation and warnings for accessing contract members inherited from ``address``. * Type Checker: Improve documentation and warnings for accessing contract members inherited from ``address``.
Bugfixes: Bugfixes:
* Code Generator: Allow ``block.blockhash`` without being called. * Code Generator: Allow ``block.blockhash`` without being called.
* Code Generator: Do not include internal functions in the runtime bytecode which are only referenced in the constructor. * Code Generator: Do not include internal functions in the runtime bytecode which are only referenced in the constructor.

4
cmake/FindCVC4.cmake Normal file
View File

@ -0,0 +1,4 @@
find_path(CVC4_INCLUDE_DIR cvc4/cvc4.h)
find_library(CVC4_LIBRARY NAMES cvc4 )
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CVC4 DEFAULT_MSG CVC4_LIBRARY CVC4_INCLUDE_DIR)

3
cmake/FindGMP.cmake Normal file
View File

@ -0,0 +1,3 @@
find_library(GMP_LIBRARY NAMES gmp )
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARY)

View File

@ -6,10 +6,25 @@ find_package(Z3 QUIET)
if (${Z3_FOUND}) if (${Z3_FOUND})
include_directories(${Z3_INCLUDE_DIR}) include_directories(${Z3_INCLUDE_DIR})
add_definitions(-DHAVE_Z3) add_definitions(-DHAVE_Z3)
message("Z3 SMT solver found. This enables optional SMT checking.") message("Z3 SMT solver found. This enables optional SMT checking with Z3.")
list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
else() else()
message("Z3 SMT solver NOT found. Optional SMT checking will not be available. Please install Z3 if it is desired.")
list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp") list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp")
find_package(GMP QUIET)
find_package(CVC4 QUIET)
if (${CVC4_FOUND})
if (${GMP_FOUND})
include_directories(${CVC4_INCLUDE_DIR})
add_definitions(-DHAVE_CVC4)
message("CVC4 SMT solver and GMP found. This enables optional SMT checking with CVC4.")
else()
message("CVC4 SMT solver found but its dependency GMP was NOT found. Optional SMT checking with CVC4 will not be available. Please install GMP if it is desired.")
list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
endif()
else()
message("No SMT solver found (Z3 or CVC4). Optional SMT checking will not be available. Please install Z3 or CVC4 if it is desired.")
list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
endif()
endif() endif()
add_library(solidity ${sources} ${headers}) add_library(solidity ${sources} ${headers})
@ -18,3 +33,8 @@ target_link_libraries(solidity PUBLIC evmasm devcore)
if (${Z3_FOUND}) if (${Z3_FOUND})
target_link_libraries(solidity PUBLIC ${Z3_LIBRARY}) target_link_libraries(solidity PUBLIC ${Z3_LIBRARY})
endif() endif()
if (${CVC4_FOUND} AND ${GMP_FOUND})
target_link_libraries(solidity PUBLIC ${CVC4_LIBRARY})
target_link_libraries(solidity PUBLIC ${GMP_LIBRARY})
endif()

View File

@ -0,0 +1,200 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libsolidity/formal/CVC4Interface.h>
#include <libsolidity/interface/Exceptions.h>
#include <libdevcore/CommonIO.h>
using namespace std;
using namespace dev;
using namespace dev::solidity::smt;
CVC4Interface::CVC4Interface():
m_solver(&m_context)
{
reset();
}
void CVC4Interface::reset()
{
m_constants.clear();
m_functions.clear();
m_solver.reset();
m_solver.setOption("produce-models", true);
}
void CVC4Interface::push()
{
m_solver.push();
}
void CVC4Interface::pop()
{
m_solver.pop();
}
Expression CVC4Interface::newFunction(string _name, Sort _domain, Sort _codomain)
{
CVC4::Type fType = m_context.mkFunctionType(cvc4Sort(_domain), cvc4Sort(_codomain));
m_functions.insert({_name, m_context.mkVar(_name.c_str(), fType)});
return SolverInterface::newFunction(move(_name), _domain, _codomain);
}
Expression CVC4Interface::newInteger(string _name)
{
m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.integerType())});
return SolverInterface::newInteger(move(_name));
}
Expression CVC4Interface::newBool(string _name)
{
m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.booleanType())});
return SolverInterface::newBool(std::move(_name));
}
void CVC4Interface::addAssertion(Expression const& _expr)
{
try
{
m_solver.assertFormula(toCVC4Expr(_expr));
}
catch (CVC4::TypeCheckingException const&)
{
solAssert(false, "");
}
catch (CVC4::LogicException const&)
{
solAssert(false, "");
}
catch (CVC4::UnsafeInterruptException const&)
{
solAssert(false, "");
}
}
pair<CheckResult, vector<string>> CVC4Interface::check(vector<Expression> const& _expressionsToEvaluate)
{
CheckResult result;
vector<string> values;
try
{
switch (m_solver.checkSat().isSat())
{
case CVC4::Result::SAT:
result = CheckResult::SATISFIABLE;
break;
case CVC4::Result::UNSAT:
result = CheckResult::UNSATISFIABLE;
break;
case CVC4::Result::SAT_UNKNOWN:
result = CheckResult::UNKNOWN;
break;
default:
solAssert(false, "");
}
if (result != CheckResult::UNSATISFIABLE && !_expressionsToEvaluate.empty())
{
for (Expression const& e: _expressionsToEvaluate)
values.push_back(toString(m_solver.getValue(toCVC4Expr(e))));
}
}
catch (CVC4::Exception & e)
{
result = CheckResult::ERROR;
values.clear();
}
return make_pair(result, values);
}
CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
{
if (_expr.arguments.empty() && m_constants.count(_expr.name))
return m_constants.at(_expr.name);
vector<CVC4::Expr> arguments;
for (auto const& arg: _expr.arguments)
arguments.push_back(toCVC4Expr(arg));
string const& n = _expr.name;
if (m_functions.count(n))
return m_context.mkExpr(CVC4::kind::APPLY_UF, m_functions[n], arguments);
else if (m_constants.count(n))
{
solAssert(arguments.empty(), "");
return m_constants.at(n);
}
else if (arguments.empty())
{
if (n == "true")
return m_context.mkConst(true);
else if (n == "false")
return m_context.mkConst(false);
else
// We assume it is an integer...
return m_context.mkConst(CVC4::Rational(n));
}
solAssert(_expr.hasCorrectArity(), "");
if (n == "ite")
return arguments[0].iteExpr(arguments[1], arguments[2]);
else if (n == "not")
return arguments[0].notExpr();
else if (n == "and")
return arguments[0].andExpr(arguments[1]);
else if (n == "or")
return arguments[0].orExpr(arguments[1]);
else if (n == "=")
return m_context.mkExpr(CVC4::kind::EQUAL, arguments[0], arguments[1]);
else if (n == "<")
return m_context.mkExpr(CVC4::kind::LT, arguments[0], arguments[1]);
else if (n == "<=")
return m_context.mkExpr(CVC4::kind::LEQ, arguments[0], arguments[1]);
else if (n == ">")
return m_context.mkExpr(CVC4::kind::GT, arguments[0], arguments[1]);
else if (n == ">=")
return m_context.mkExpr(CVC4::kind::GEQ, arguments[0], arguments[1]);
else if (n == "+")
return m_context.mkExpr(CVC4::kind::PLUS, arguments[0], arguments[1]);
else if (n == "-")
return m_context.mkExpr(CVC4::kind::MINUS, arguments[0], arguments[1]);
else if (n == "*")
return m_context.mkExpr(CVC4::kind::MULT, arguments[0], arguments[1]);
else if (n == "/")
return m_context.mkExpr(CVC4::kind::INTS_DIVISION_TOTAL, arguments[0], arguments[1]);
// Cannot reach here.
solAssert(false, "");
return arguments[0];
}
CVC4::Type CVC4Interface::cvc4Sort(Sort _sort)
{
switch (_sort)
{
case Sort::Bool:
return m_context.booleanType();
case Sort::Int:
return m_context.integerType();
default:
break;
}
solAssert(false, "");
// Cannot be reached.
return m_context.integerType();
}

View File

@ -0,0 +1,62 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <libsolidity/formal/SolverInterface.h>
#include <boost/noncopyable.hpp>
#include <cvc4/cvc4.h>
namespace dev
{
namespace solidity
{
namespace smt
{
class CVC4Interface: public SolverInterface, public boost::noncopyable
{
public:
CVC4Interface();
void reset() override;
void push() override;
void pop() override;
Expression newFunction(std::string _name, Sort _domain, Sort _codomain) override;
Expression newInteger(std::string _name) override;
Expression newBool(std::string _name) override;
void addAssertion(Expression const& _expr) override;
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
private:
CVC4::Expr toCVC4Expr(Expression const& _expr);
CVC4::Type cvc4Sort(smt::Sort _sort);
CVC4::ExprManager m_context;
CVC4::SmtEngine m_solver;
std::map<std::string, CVC4::Expr> m_constants;
std::map<std::string, CVC4::Expr> m_functions;
};
}
}
}

View File

@ -19,6 +19,8 @@
#ifdef HAVE_Z3 #ifdef HAVE_Z3
#include <libsolidity/formal/Z3Interface.h> #include <libsolidity/formal/Z3Interface.h>
#elif HAVE_CVC4
#include <libsolidity/formal/CVC4Interface.h>
#else #else
#include <libsolidity/formal/SMTLib2Interface.h> #include <libsolidity/formal/SMTLib2Interface.h>
#endif #endif
@ -39,6 +41,8 @@ using namespace dev::solidity;
SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readFileCallback): SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readFileCallback):
#ifdef HAVE_Z3 #ifdef HAVE_Z3
m_interface(make_shared<smt::Z3Interface>()), m_interface(make_shared<smt::Z3Interface>()),
#elif HAVE_CVC4
m_interface(make_shared<smt::CVC4Interface>()),
#else #else
m_interface(make_shared<smt::SMTLib2Interface>(_readFileCallback)), m_interface(make_shared<smt::SMTLib2Interface>(_readFileCallback)),
#endif #endif

View File

@ -65,6 +65,26 @@ public:
Expression& operator=(Expression const&) = default; Expression& operator=(Expression const&) = default;
Expression& operator=(Expression&&) = default; Expression& operator=(Expression&&) = default;
bool hasCorrectArity() const
{
static std::map<std::string, unsigned> const operatorsArity{
{"ite", 3},
{"not", 1},
{"and", 2},
{"or", 2},
{"=", 2},
{"<", 2},
{"<=", 2},
{">", 2},
{">=", 2},
{"+", 2},
{"-", 2},
{"*", 2},
{"/", 2}
};
return operatorsArity.count(name) && operatorsArity.at(name) == arguments.size();
}
static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue) static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue)
{ {
solAssert(_trueValue.sort == _falseValue.sort, ""); solAssert(_trueValue.sort == _falseValue.sort, "");

View File

@ -116,21 +116,6 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
for (auto const& arg: _expr.arguments) for (auto const& arg: _expr.arguments)
arguments.push_back(toZ3Expr(arg)); arguments.push_back(toZ3Expr(arg));
static map<string, unsigned> arity{
{"ite", 3},
{"not", 1},
{"and", 2},
{"or", 2},
{"=", 2},
{"<", 2},
{"<=", 2},
{">", 2},
{">=", 2},
{"+", 2},
{"-", 2},
{"*", 2},
{"/", 2}
};
string const& n = _expr.name; string const& n = _expr.name;
if (m_functions.count(n)) if (m_functions.count(n))
return m_functions.at(n)(arguments); return m_functions.at(n)(arguments);
@ -150,7 +135,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
return m_context.int_val(n.c_str()); return m_context.int_val(n.c_str());
} }
solAssert(arity.count(n) && arity.at(n) == arguments.size(), ""); solAssert(_expr.hasCorrectArity(), "");
if (n == "ite") if (n == "ite")
return z3::ite(arguments[0], arguments[1], arguments[2]); return z3::ite(arguments[0], arguments[1], arguments[2]);
else if (n == "not") else if (n == "not")

View File

@ -51,9 +51,6 @@ private:
z3::expr toZ3Expr(Expression const& _expr); z3::expr toZ3Expr(Expression const& _expr);
z3::sort z3Sort(smt::Sort _sort); z3::sort z3Sort(smt::Sort _sort);
std::string checkSatAndGetValuesCommand(std::vector<Expression> const& _expressionsToEvaluate);
std::vector<std::string> parseValues(std::string::const_iterator _start, std::string::const_iterator _end);
z3::context m_context; z3::context m_context;
z3::solver m_solver; z3::solver m_solver;
std::map<std::string, z3::expr> m_constants; std::map<std::string, z3::expr> m_constants;