Skip to content

Commit 7a4ddb2

Browse files
authored
Merge pull request #15972 from ethereum/smt-refactor
SMTChecker: Refactor processing invariants from the solver
2 parents a04f946 + c608428 commit 7a4ddb2

File tree

10 files changed

+92
-176
lines changed

10 files changed

+92
-176
lines changed

libsmtutil/CHCSmtLib2Interface.cpp

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,21 +102,18 @@ CHCSolverInterface::QueryResult CHCSmtLib2Interface::query(Expression const& _bl
102102
// However, with CHC solvers, the meaning is flipped, UNSAT -> UNSAFE and SAT -> SAFE.
103103
// So we have to flip the answer.
104104
if (boost::starts_with(response, "sat"))
105-
{
106-
auto maybeInvariants = invariantsFromSolverResponse(response);
107-
return {CheckResult::UNSATISFIABLE, maybeInvariants.value_or(Expression(true)), {}};
108-
}
109-
else if (boost::starts_with(response, "unsat"))
105+
return {CheckResult::UNSATISFIABLE, invariantsFromSolverResponse(response), {}};
106+
if (boost::starts_with(response, "unsat"))
110107
result = CheckResult::SATISFIABLE;
111108
else if (boost::starts_with(response, "unknown"))
112109
result = CheckResult::UNKNOWN;
113110
else
114111
result = CheckResult::ERROR;
115-
return {result, Expression(true), {}};
112+
return {result, {}, {}};
116113
}
117114
catch(smtutil::SMTSolverInteractionError const&)
118115
{
119-
return {CheckResult::ERROR, Expression(true), {}};
116+
return {CheckResult::ERROR, {}, {}};
120117
}
121118

122119
}
@@ -416,7 +413,7 @@ smtutil::Expression CHCSmtLib2Interface::ScopedParser::toSMTUtilExpression(SMTLi
416413
}
417414

418415

419-
std::optional<smtutil::Expression> CHCSmtLib2Interface::invariantsFromSolverResponse(std::string const& _response) const
416+
CHCSmtLib2Interface::Invariants CHCSmtLib2Interface::invariantsFromSolverResponse(std::string const& _response) const
420417
{
421418
std::stringstream ss(_response);
422419
std::string answer;
@@ -438,7 +435,7 @@ std::optional<smtutil::Expression> CHCSmtLib2Interface::invariantsFromSolverResp
438435
smtSolverInteractionRequire(parser.isEOF(), "Error during parsing CHC model");
439436
smtSolverInteractionRequire(!parsedOutput.empty(), "Error during parsing CHC model");
440437
auto& commands = parsedOutput.size() == 1 ? asSubExpressions(parsedOutput[0]) : parsedOutput;
441-
std::vector<Expression> definitions;
438+
Invariants definitions;
442439
for (auto& command: commands)
443440
{
444441
auto& args = asSubExpressions(command);
@@ -456,7 +453,7 @@ std::optional<smtutil::Expression> CHCSmtLib2Interface::invariantsFromSolverResp
456453
inlineLetExpressions(interpretation);
457454
ScopedParser scopedParser(m_context);
458455
auto const& formalArguments = asSubExpressions(args[2]);
459-
std::vector<Expression> predicateArgs;
456+
std::vector<std::string> predicateArguments;
460457
for (auto const& formalArgument: formalArguments)
461458
{
462459
smtSolverInteractionRequire(!isAtom(formalArgument), "Invalid format of CHC model");
@@ -465,7 +462,7 @@ std::optional<smtutil::Expression> CHCSmtLib2Interface::invariantsFromSolverResp
465462
smtSolverInteractionRequire(isAtom(nameSortPair[0]), "Invalid format of CHC model");
466463
SortPointer varSort = scopedParser.toSort(nameSortPair[1]);
467464
scopedParser.addVariableDeclaration(asAtom(nameSortPair[0]), varSort);
468-
predicateArgs.push_back(scopedParser.toSMTUtilExpression(nameSortPair[0]));
465+
predicateArguments.push_back(asAtom(nameSortPair[0]));
469466
}
470467

471468
auto parsedInterpretation = scopedParser.toSMTUtilExpression(interpretation);
@@ -476,11 +473,11 @@ std::optional<smtutil::Expression> CHCSmtLib2Interface::invariantsFromSolverResp
476473
return first.name < second.name;
477474
});
478475

479-
Expression predicate(asAtom(args[1]), predicateArgs, SortProvider::boolSort);
480-
// FIXME: Why do we need to represent the predicate as Expression?
481-
definitions.push_back(predicate == parsedInterpretation);
476+
std::string const& predicateName = asAtom(args[1]);
477+
smtSolverInteractionRequire(!definitions.contains(predicateName), "Predicates must be unique");
478+
definitions.emplace(predicateName, InvariantInfo{parsedInterpretation, predicateArguments});
482479
}
483-
return Expression::mkAnd(std::move(definitions));
480+
return definitions;
484481
}
485482

486483
namespace

libsmtutil/CHCSmtLib2Interface.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ class CHCSmtLib2Interface: public CHCSolverInterface
9494
/// Communicates with the solver via the callback. Throws SMTSolverError on error.
9595
virtual std::string querySolver(std::string const& _input);
9696

97-
/// Translates CHC solver response with a model to our representation of invariants. Returns None on error.
98-
std::optional<smtutil::Expression> invariantsFromSolverResponse(std::string const& _response) const;
97+
/// Translates CHC solver response with a model to our representation of invariants.
98+
Invariants invariantsFromSolverResponse(std::string const& _response) const;
9999

100100
std::set<std::string> collectVariableNames(Expression const& _expr) const;
101101

libsmtutil/CHCSolverInterface.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <libsmtutil/SolverInterface.h>
2626

2727
#include <map>
28+
#include <unordered_map>
2829
#include <vector>
2930

3031
namespace solidity::smtutil
@@ -49,10 +50,19 @@ class CHCSolverInterface : public SolverInterface
4950
std::map<unsigned, std::vector<unsigned>> edges;
5051
};
5152

53+
struct InvariantInfo
54+
{
55+
/// Predicate definition as SMT expression
56+
Expression expression;
57+
/// Names of the formal arguments of the predicate definition
58+
std::vector<std::string> variableNames;
59+
};
60+
/// Maps predicate to its definition as given by the solver and the formal arguments of the predicate
61+
using Invariants = std::unordered_map<std::string, InvariantInfo>;
5262
struct QueryResult
5363
{
5464
CheckResult answer;
55-
Expression invariant;
65+
Invariants invariants;
5666
CexGraph cex;
5767
};
5868
/// Takes a function application _expr and checks for reachability.

libsolidity/CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,6 @@ set(sources
117117
formal/EncodingContext.h
118118
formal/ExpressionFormatter.cpp
119119
formal/ExpressionFormatter.h
120-
formal/Invariants.cpp
121-
formal/Invariants.h
122120
formal/ModelChecker.cpp
123121
formal/ModelChecker.h
124122
formal/ModelCheckerSettings.cpp

libsolidity/formal/CHC.cpp

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
#include <libsolidity/formal/CHC.h>
2020

2121
#include <libsolidity/formal/ArraySlicePredicate.h>
22+
#include <libsolidity/formal/ExpressionFormatter.h>
2223
#include <libsolidity/formal/EldaricaCHCSmtLib2Interface.h>
23-
#include <libsolidity/formal/Invariants.h>
2424
#include <libsolidity/formal/ModelChecker.h>
2525
#include <libsolidity/formal/PredicateInstance.h>
2626
#include <libsolidity/formal/PredicateSort.h>
@@ -34,9 +34,8 @@
3434
#include <libsolutil/Algorithms.h>
3535
#include <libsolutil/StringUtils.h>
3636

37-
#include <boost/algorithm/string.hpp>
38-
3937
#include <range/v3/algorithm/for_each.hpp>
38+
#include <range/v3/algorithm/none_of.hpp>
4039
#include <range/v3/view.hpp>
4140
#include <range/v3/view/enumerate.hpp>
4241
#include <range/v3/view/reverse.hpp>
@@ -1895,7 +1894,7 @@ CHCSolverInterface::QueryResult CHC::query(smtutil::Expression const& _query, la
18951894
2339_error,
18961895
"CHC: Requested query:\n" + smtLibCode
18971896
);
1898-
return {.answer = CheckResult::UNKNOWN, .invariant = smtutil::Expression(true), .cex = {}};
1897+
return {.answer = CheckResult::UNKNOWN, .invariants = {}, .cex = {}};
18991898
}
19001899
auto result = m_interface->query(_query);
19011900
switch (result.answer)
@@ -2134,6 +2133,43 @@ void CHC::checkVerificationTargets()
21342133
m_safeTargets[m_verificationTargets.at(id).errorNode].insert(m_verificationTargets.at(id));
21352134
}
21362135

2136+
namespace
2137+
{
2138+
std::map<Predicate const*, std::set<std::string>> collectInvariants(
2139+
CHCSmtLib2Interface::Invariants const& _invariants,
2140+
std::set<Predicate const*> const& _predicates,
2141+
ModelCheckerInvariants const& _invariantsSetting
2142+
)
2143+
{
2144+
std::set<std::string> targets;
2145+
if (_invariantsSetting.has(InvariantType::Contract))
2146+
targets.insert("interface_");
2147+
if (_invariantsSetting.has(InvariantType::Reentrancy))
2148+
targets.insert("nondet_interface_");
2149+
2150+
std::map<Predicate const*, std::set<std::string>> invariants;
2151+
for (auto const* pred: _predicates)
2152+
{
2153+
smtAssert(pred);
2154+
auto const& predName = pred->functor().name;
2155+
if (!_invariants.contains(predName))
2156+
continue;
2157+
if (ranges::none_of(targets, [&](auto const& _target) { return predName.starts_with(_target); }))
2158+
continue;
2159+
2160+
smtAssert(pred->contextContract());
2161+
2162+
auto const& [definition, formalArguments] = _invariants.at(predName);
2163+
2164+
auto r = substitute(definition, pred->expressionSubstitution(formalArguments));
2165+
// No point in reporting true/false as invariants.
2166+
if (r.name != "true" && r.name != "false")
2167+
invariants[pred].insert(toSolidityStr(r));
2168+
}
2169+
return invariants;
2170+
}
2171+
} // namespace
2172+
21372173
void CHC::checkAndReportTarget(
21382174
CHCVerificationTarget const& _target,
21392175
std::vector<CHCQueryPlaceholder> const& _placeholders,
@@ -2153,7 +2189,7 @@ void CHC::checkAndReportTarget(
21532189
placeholder.constraints && placeholder.errorExpression == _target.errorId
21542190
);
21552191
auto const& location = _target.errorNode->location();
2156-
auto [result, invariant, model] = query(error(), location);
2192+
auto [result, invariants, model] = query(error(), location);
21572193
if (result == CheckResult::UNSATISFIABLE)
21582194
{
21592195
m_safeTargets[_target.errorNode].insert(_target);
@@ -2162,9 +2198,9 @@ void CHC::checkAndReportTarget(
21622198
predicates.insert(pred);
21632199
for (auto const* pred: m_nondetInterfaces | ranges::views::values)
21642200
predicates.insert(pred);
2165-
std::map<Predicate const*, std::set<std::string>> invariants = collectInvariants(invariant, predicates, m_settings.invariants);
2166-
for (auto pred: invariants | ranges::views::keys)
2167-
m_invariants[pred] += std::move(invariants.at(pred));
2201+
std::map<Predicate const*, std::set<std::string>> invariantStrings = collectInvariants(invariants, predicates, m_settings.invariants);
2202+
for (auto pred: invariantStrings | ranges::views::keys)
2203+
m_invariants[pred] += std::move(invariantStrings.at(pred));
21682204
}
21692205
else if (result == CheckResult::SATISFIABLE)
21702206
{

libsolidity/formal/Invariants.cpp

Lines changed: 0 additions & 85 deletions
This file was deleted.

libsolidity/formal/Invariants.h

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)