This commit is contained in:
Leonardo Alt 2020-07-14 17:41:19 +02:00
parent 003c9b9a5b
commit 5bb4e73693
10 changed files with 40 additions and 86 deletions

View File

@ -36,7 +36,7 @@ Z3CHCInterface::Z3CHCInterface():
z3::set_param("rewriter.pull_cheap_ite", true);
z3::set_param("rlimit", Z3Interface::resourceLimit);
enablePreProcessing();
setSpacerOptions();
}
void Z3CHCInterface::declareVariable(string const& _name, SortPointer const& _sort)
@ -104,7 +104,7 @@ pair<CheckResult, CHCSolverInterface::CexGraph> Z3CHCInterface::query(Expression
return {result, cex};
}
void Z3CHCInterface::enablePreProcessing()
void Z3CHCInterface::setSpacerOptions(bool _preProcessing)
{
// Spacer options.
// These needs to be set in the solver.
@ -117,31 +117,12 @@ void Z3CHCInterface::enablePreProcessing()
// Ground pobs by using values from a model.
p.set("fp.spacer.ground_pobs", false);
// Enable Spacer optimization for better solving.
p.set("fp.xform.slice", true);
p.set("fp.xform.inline_linear", true);
p.set("fp.xform.inline_eager", true);
m_solver.set(p);
}
void Z3CHCInterface::disablePreProcessing()
{
// Spacer options.
// These needs to be set in the solver.
// https://github.com/Z3Prover/z3/blob/master/src/muz/base/fp_params.pyg
z3::params p(*m_context);
// These are useful for solving problems with arrays and loops.
// Use quantified lemma generalizer.
p.set("fp.spacer.q3.use_qgen", true);
p.set("fp.spacer.mbqi", false);
// Ground pobs by using values from a model.
p.set("fp.spacer.ground_pobs", false);
// Disable Spacer optimization for counterexample generation.
p.set("fp.xform.slice", false);
p.set("fp.xform.inline_linear", false);
p.set("fp.xform.inline_eager", false);
// Spacer optimization should be
// - enabled for better solving (default)
// - disable for counterexample generation
p.set("fp.xform.slice", _preProcessing);
p.set("fp.xform.inline_linear", _preProcessing);
p.set("fp.xform.inline_eager", _preProcessing);
m_solver.set(p);
}
@ -151,6 +132,13 @@ Convert a ground refutation into a linear or nonlinear counterexample.
The counterexample is given as an implication graph of the form
`premises => conclusion` where `premises` are the predicates
from the body of nonlinear clauses, representing the proof graph.
This function is based on and similar to
https://github.com/Z3Prover/z3/blob/z3-4.8.8/src/muz/spacer/spacer_context.cpp#L2919
(spacer::context::get_ground_sat_answer)
which generates linear counterexamples.
It is modified here to accept nonlinear CHCs as well, generating a DAG
instead of a path.
*/
CHCSolverInterface::CexGraph Z3CHCInterface::cexGraph(z3::expr const& _proof)
{

View File

@ -46,8 +46,7 @@ public:
Z3Interface* z3Interface() const { return m_z3Interface.get(); }
void enablePreProcessing();
void disablePreProcessing();
void setSpacerOptions(bool _preProcessing = true);
private:
/// Constructs a nonlinear counterexample graph from the refutation.

View File

@ -32,6 +32,8 @@
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <queue>
using namespace std;
using namespace solidity;
using namespace solidity::util;
@ -569,6 +571,7 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
m_context.variable(*var)->increaseIndex();
auto nondet = (*m_nondetInterfaces.at(m_currentContract))(preCallState + currentStateVariables());
m_symbolFunction[nondet.name] = &_funCall;
m_context.addAssertion(nondet);
m_context.addAssertion(m_error.currentValue() == 0);
@ -1134,7 +1137,7 @@ pair<smtutil::CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Exp
// We now disable those optimizations and check whether we can still solve the problem.
auto* spacer = dynamic_cast<Z3CHCInterface*>(m_interface.get());
solAssert(spacer, "");
spacer->disablePreProcessing();
spacer->setSpacerOptions(false);
smtutil::CheckResult resultNoOpt;
CHCSolverInterface::CexGraph cexNoOpt;
@ -1143,7 +1146,7 @@ pair<smtutil::CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Exp
if (resultNoOpt == smtutil::CheckResult::SATISFIABLE)
cex = move(cexNoOpt);
spacer->enablePreProcessing();
spacer->setSpacerOptions(true);
#endif
break;
}
@ -1312,7 +1315,8 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
string localState;
unsigned node = *rootId;
optional<string> lastTxSeen;
/// The first summary node seen in this loop represents the last transaction.
bool lastTxSeen = false;
while (_graph.edges.at(node).size() >= 1)
{
auto const& edges = _graph.edges.at(node);
@ -1328,6 +1332,8 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
swap(summaryId, *interfaceId);
solAssert(_graph.nodes.at(*interfaceId).first.rfind("interface", 0) == 0, "");
}
/// The children are unordered, so we need to check which is the summary and
/// which is the interface.
solAssert(_graph.nodes.at(summaryId).first.rfind("summary", 0) == 0, "");
/// At this point property 2 from the function description is verified for this node.
@ -1360,9 +1366,9 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
/// but not necessarily the summary of the function that contains the error.
if (!lastTxSeen)
{
lastTxSeen = summaryNode.first;
lastTxSeen = true;
/// Generate counterexample message local to the failed target.
localState = generatePostStateCounterexample(stateVars, calledFun, summaryNode.second) + "\n";
localState = formatStateCounterexample(stateVars, calledFun, summaryNode.second) + "\n";
if (calledFun)
{
/// The signature of a summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars).
@ -1389,9 +1395,9 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
else
/// We report the state after every tx in the trace except for the last, which is reported
/// first in the code above.
path.emplace_back("State: " + generatePostStateCounterexample(stateVars, calledFun, summaryNode.second));
path.emplace_back("State: " + formatStateCounterexample(stateVars, calledFun, summaryNode.second));
string txCex = calledContract ? "constructor()" : generatePreTxCounterexample(stateVars, *calledFun, summaryNode.second);
string txCex = calledContract ? "constructor()" : formatFunctionCallCounterexample(stateVars, *calledFun, summaryNode.second);
path.emplace_back(txCex);
/// Recurse on the next interface node which represents the previous transaction
@ -1405,7 +1411,7 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
return localState + "\nTransaction trace:\n" + boost::algorithm::join(boost::adaptors::reverse(path), "\n");
}
string CHC::generatePostStateCounterexample(vector<VariableDeclaration const*> const& _stateVars, FunctionDefinition const* _function, vector<string> const& _summaryValues)
string CHC::formatStateCounterexample(vector<VariableDeclaration const*> const& _stateVars, FunctionDefinition const* _function, vector<string> const& _summaryValues)
{
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars).
/// The signature of an implicit constructor summary predicate is: summary(error, postStateVars).
@ -1423,6 +1429,8 @@ string CHC::generatePostStateCounterexample(vector<VariableDeclaration const*> c
stateLast = stateFirst + static_cast<int>(_stateVars.size());
}
solAssert(stateFirst >= _summaryValues.begin() && stateFirst <= _summaryValues.end(), "");
solAssert(stateLast >= _summaryValues.begin() && stateLast <= _summaryValues.end(), "");
vector<string> stateArgs(stateFirst, stateLast);
solAssert(stateArgs.size() == _stateVars.size(), "");
@ -1437,12 +1445,14 @@ string CHC::generatePostStateCounterexample(vector<VariableDeclaration const*> c
return boost::algorithm::join(stateCex, ", ");
}
string CHC::generatePreTxCounterexample(vector<VariableDeclaration const*> const& _stateVars, FunctionDefinition const& _function, vector<string> const& _summaryValues)
string CHC::formatFunctionCallCounterexample(vector<VariableDeclaration const*> const& _stateVars, FunctionDefinition const& _function, vector<string> const& _summaryValues)
{
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars).
/// Here we are interested in preInputVars.
vector<string>::const_iterator first = _summaryValues.begin() + static_cast<int>(_stateVars.size()) + 1;
vector<string>::const_iterator last = first + static_cast<int>(_function.parameters().size());
solAssert(first >= _summaryValues.begin() && first <= _summaryValues.end(), "");
solAssert(last >= _summaryValues.begin() && last <= _summaryValues.end(), "");
vector<string> functionArgsCex(first, last);
vector<string> functionArgs;

View File

@ -218,11 +218,11 @@ private:
/// _function was executed.
/// _function = nullptr means the transaction was the deployment of a
/// contract without an explicit constructor.
std::string generatePostStateCounterexample(std::vector<VariableDeclaration const*> const& _stateVariables, FunctionDefinition const* _function, std::vector<std::string> const& _summaryValues);
std::string formatStateCounterexample(std::vector<VariableDeclaration const*> const& _stateVariables, FunctionDefinition const* _function, std::vector<std::string> const& _summaryValues);
/// @returns a formatted text representing a call to _function
/// with the concrete values for value type parameters and
/// the parameter name for reference types.
std::string generatePreTxCounterexample(std::vector<VariableDeclaration const*> const& _stateVariables, FunctionDefinition const& _function, std::vector<std::string> const& _summaryValues);
std::string formatFunctionCallCounterexample(std::vector<VariableDeclaration const*> const& _stateVariables, FunctionDefinition const& _function, std::vector<std::string> const& _summaryValues);
//@}
/// Misc.

View File

@ -67,7 +67,6 @@ Testsuite const g_interactiveTestsuites[] = {
{"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create},
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create, {"nooptions"}},
{"SMT Checker JSON", "libsolidity", "smtCheckerTestsJSON", true, false, &SMTCheckerJSONTest::create, {"nooptions"}},
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create}
};

View File

@ -37,6 +37,8 @@ contract C {
b[x][y] = z;
}
}
// ====
// SMTSolvers: cvc4
// ----
// Warning 6328: (617-637): Assertion violation happens here.
// Warning 4661: (372-392): Assertion violation happens here
// Warning 4661: (617-637): Assertion violation happens here

View File

@ -1,10 +0,0 @@
{
"auxiliaryInput":
{
"smtlib2responses":
{
"0x45598870c7c0bc4c4f61acad7e0dd9399fb28aa3df198379dd36a95d70814ef8": "sat\n((|EVALEXPR_0| 1))\n",
"0xee335f8104fdb81b6e5fb418725923b81f7d78ffbd6bf95fb82e5593a1ac366a": "sat\n((|EVALEXPR_0| 0))\n"
}
}
}

View File

@ -1,14 +0,0 @@
==== Source: A ====
pragma experimental SMTChecker;
contract C
{
function f(uint x) public pure {
assert(x > 0);
assert(x > 100);
assert(x >= 0);
}
}
// ----
// Warning: (82-95): Assertion violation happens here
// Warning: (99-114): Assertion violation happens here

View File

@ -1,9 +0,0 @@
{
"auxiliaryInput":
{
"smtlib2responses":
{
"0x535c76d6b87d14bd4b4bf1014d14b8e91b648f073a68f9f267c4fe1df570bc14": "sat\n((|EVALEXPR_0| 0))\n"
}
}
}

View File

@ -1,11 +0,0 @@
==== Source: A ====
pragma experimental SMTChecker;
contract C
{
function f(uint x) public pure {
assert(x > 0);
}
}
// ----
// Warning: (82-95): Assertion violation happens here