Move post input and post output filtering from CHC to Predicate

This commit is contained in:
Leonardo Alt 2020-08-20 10:43:50 +02:00
parent 2e2e96cc93
commit a3b6019131
4 changed files with 85 additions and 50 deletions

View File

@ -29,7 +29,6 @@
#include <libsmtutil/CHCSmtLib2Interface.h>
#include <libsolutil/Algorithms.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <queue>
@ -1404,24 +1403,22 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
solAssert(edges.size() <= 2, "");
unsigned summaryId = edges.at(0);
optional<unsigned> interfaceId;
if (edges.size() == 2)
{
interfaceId = edges.at(1);
if (_graph.nodes.at(summaryId).first.rfind("summary", 0) != 0)
if (!Predicate::predicate(_graph.nodes.at(summaryId).first)->isSummary())
swap(summaryId, *interfaceId);
solAssert(_graph.nodes.at(*interfaceId).first.rfind("interface", 0) == 0, "");
auto interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).first);
solAssert(interfacePredicate && interfacePredicate->isInterface(), "");
}
/// 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, "");
Predicate const* summaryPredicate = Predicate::predicate(_graph.nodes.at(summaryId).first);
solAssert(summaryPredicate && summaryPredicate->isSummary(), "");
/// At this point property 2 from the function description is verified for this node.
auto const& summaryNode = _graph.nodes.at(summaryId);
Predicate const* summaryPredicate = Predicate::predicate(summaryNode.first);
solAssert(summaryPredicate, "");
auto summaryArgs = _graph.nodes.at(summaryId).second;
FunctionDefinition const* calledFun = summaryPredicate->programFunction();
ContractDefinition const* calledContract = summaryPredicate->programContract();
@ -1429,7 +1426,7 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
solAssert((calledFun && !calledContract) || (!calledFun && calledContract), "");
auto stateVars = summaryPredicate->stateVariables();
solAssert(stateVars.has_value(), "");
auto stateValues = summaryPredicate->summaryStateValues(summaryNode.second);
auto stateValues = summaryPredicate->summaryStateValues(summaryArgs);
solAssert(stateValues.size() == stateVars->size(), "");
/// This summary node is the end of a tx.
@ -1440,36 +1437,23 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
{
lastTxSeen = true;
/// Generate counterexample message local to the failed target.
localState = formatStateCounterexample(*stateVars, stateValues) + "\n";
localState = formatVariableModel(*stateVars, stateValues, ", ") + "\n";
if (calledFun)
{
/// The signature of a summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars).
auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs);
auto const& inParams = calledFun->parameters();
unsigned initLocals = stateVars->size() * 2 + 1 + inParams.size();
/// In this loop we are interested in postInputVars.
for (unsigned i = initLocals; i < initLocals + inParams.size(); ++i)
{
auto param = inParams.at(i - initLocals);
if (param->type()->isValueType())
localState += param->name() + " = " + summaryNode.second.at(i) + "\n";
}
localState += formatVariableModel(inParams, inValues, "\n") + "\n";
auto outValues = summaryPredicate->summaryPostOutputValues(summaryArgs);
auto const& outParams = calledFun->returnParameters();
initLocals += inParams.size();
/// In this loop we are interested in outputVars.
for (unsigned i = initLocals; i < initLocals + outParams.size(); ++i)
{
auto param = outParams.at(i - initLocals);
if (param->type()->isValueType())
localState += param->name() + " = " + summaryNode.second.at(i) + "\n";
}
localState += formatVariableModel(outParams, outValues, "\n") + "\n";
}
}
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: " + formatStateCounterexample(*stateVars, stateValues));
path.emplace_back("State: " + formatVariableModel(*stateVars, stateValues, ", "));
string txCex = summaryPredicate->formatSummaryCall(summaryNode.second);
string txCex = summaryPredicate->formatSummaryCall(summaryArgs);
path.emplace_back(txCex);
/// Recurse on the next interface node which represents the previous transaction
@ -1483,21 +1467,6 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
return localState + "\nTransaction trace:\n" + boost::algorithm::join(boost::adaptors::reverse(path), "\n");
}
string CHC::formatStateCounterexample(vector<VariableDeclaration const*> const& _stateVars, vector<string> const& _values)
{
solAssert(_stateVars.size() == _values.size(), "");
vector<string> stateCex;
for (unsigned i = 0; i < _values.size(); ++i)
{
auto var = _stateVars.at(i);
if (var->type()->isValueType())
stateCex.emplace_back(var->name() + " = " + _values.at(i));
}
return boost::algorithm::join(stateCex, ", ");
}
string CHC::cex2dot(smtutil::CHCSolverInterface::CexGraph const& _cex)
{
string dot = "digraph {\n";

View File

@ -38,6 +38,8 @@
#include <libsmtutil/CHCSolverInterface.h>
#include <boost/algorithm/string/join.hpp>
#include <map>
#include <optional>
#include <set>
@ -221,11 +223,23 @@ private:
);
std::optional<std::string> generateCounterexample(smtutil::CHCSolverInterface::CexGraph const& _graph, std::string const& _root);
/// @returns values for the _stateVariables after a transaction calling
/// _function was executed.
/// _function = nullptr means the transaction was the deployment of a
/// contract without an explicit constructor.
std::string formatStateCounterexample(std::vector<VariableDeclaration const*> const& _stateVariables, std::vector<std::string> const& _values);
/// @returns a set of pairs _var = _value separated by _separator.
template <typename T>
std::string formatVariableModel(std::vector<T> const& _variables, std::vector<std::string> const& _values, std::string const& _separator) const
{
solAssert(_variables.size() == _values.size(), "");
std::vector<std::string> assignments;
for (unsigned i = 0; i < _values.size(); ++i)
{
auto var = _variables.at(i);
if (var && var->type()->isValueType())
assignments.emplace_back(var->name() + " = " + _values.at(i));
}
return boost::algorithm::join(assignments, _separator);
}
/// @returns a DAG in the dot format.
/// Used for debugging purposes.

View File

@ -214,3 +214,47 @@ vector<string> Predicate::summaryStateValues(vector<string> const& _args) const
solAssert(stateArgs.size() == stateVars->size(), "");
return stateArgs;
}
vector<string> Predicate::summaryPostInputValues(vector<string> const& _args) const
{
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
/// Here we are interested in postInputVars.
auto const* function = programFunction();
solAssert(function, "");
auto stateVars = stateVariables();
solAssert(stateVars.has_value(), "");
auto const& inParams = function->parameters();
vector<string>::const_iterator first = _args.begin() + 1 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size());
vector<string>::const_iterator last = first + static_cast<int>(inParams.size());
solAssert(first >= _args.begin() && first <= _args.end(), "");
solAssert(last >= _args.begin() && last <= _args.end(), "");
vector<string> inValues(first, last);
solAssert(inValues.size() == inParams.size(), "");
return inValues;
}
vector<string> Predicate::summaryPostOutputValues(vector<string> const& _args) const
{
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
/// Here we are interested in outputVars.
auto const* function = programFunction();
solAssert(function, "");
auto stateVars = stateVariables();
solAssert(stateVars.has_value(), "");
auto const& inParams = function->parameters();
vector<string>::const_iterator first = _args.begin() + 1 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size()) * 2;
solAssert(first >= _args.begin() && first <= _args.end(), "");
vector<string> outValues(first, _args.end());
solAssert(outValues.size() == function->returnParameters().size(), "");
return outValues;
}

View File

@ -96,6 +96,14 @@ public:
/// where this summary was reached.
std::vector<std::string> summaryStateValues(std::vector<std::string> const& _args) const;
/// @returns the values of the function input variables from _args at the point
/// where this summary was reached.
std::vector<std::string> summaryPostInputValues(std::vector<std::string> const& _args) const;
/// @returns the values of the function output variables from _args at the point
/// where this summary was reached.
std::vector<std::string> summaryPostOutputValues(std::vector<std::string> const& _args) const;
private:
/// The actual SMT expression.
smt::SymbolicFunctionVariable m_predicate;