mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #3840 from ethereum/smt_cvc4
[SMTChecker] Integration with CVC4
This commit is contained in:
commit
f925747050
@ -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
4
cmake/FindCVC4.cmake
Normal 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
3
cmake/FindGMP.cmake
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
find_library(GMP_LIBRARY NAMES gmp )
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARY)
|
@ -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})
|
||||||
@ -17,4 +32,9 @@ 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()
|
||||||
|
200
libsolidity/formal/CVC4Interface.cpp
Normal file
200
libsolidity/formal/CVC4Interface.cpp
Normal 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();
|
||||||
|
}
|
62
libsolidity/formal/CVC4Interface.h
Normal file
62
libsolidity/formal/CVC4Interface.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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, "");
|
||||||
|
@ -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")
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user