Skip to content

Commit 401dd43

Browse files
authored
Merge pull request #11451 from ethereum/smt_report_invariants
[SMTChecker] Report contract invariants
2 parents 719b708 + 902a2e2 commit 401dd43

File tree

200 files changed

+1253
-45
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

200 files changed

+1253
-45
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Compiler Features:
99
* Commandline Interface: Add ``--debug-info`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
1010
* Commandline Interface: Use different colors when printing errors, warnings and infos.
1111
* SMTChecker: Output values for ``block.*``, ``msg.*`` and ``tx.*`` variables that are present in the called functions.
12+
* SMTChecker: Report contract invariants and reentrancy properties. This can be enabled via the CLI option ``--model-checker-invariants`` or the Standard JSON option ``settings.modelChecker.invariants``.
1213
* Standard JSON: Accept nested brackets in step sequences passed to ``settings.optimizer.details.yulDetails.optimizerSteps``.
1314
* Standard JSON: Add ``settings.debug.debugInfo`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
1415

docs/smtchecker.rst

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ is already "locked", so it would not be possible to change the value of ``x``,
412412
regardless of what the unknown called code does.
413413

414414
If we "forget" to use the ``mutex`` modifier on function ``set``, the
415-
SMTChecker is able to synthesize the behavior of the externally called code so
415+
SMTChecker is able to synthesize the behaviour of the externally called code so
416416
that the assertion fails:
417417

418418
.. code-block:: text
@@ -518,6 +518,23 @@ which has the following form:
518518
"source2.sol": ["contract2", "contract3"]
519519
}
520520
521+
Reported Inferred Inductive Invariants
522+
======================================
523+
524+
For properties that were proved safe with the CHC engine,
525+
the SMTChecker can retrieve inductive invariants that were inferred by the Horn
526+
solver as part of the proof.
527+
Currently two types of invariants can be reported to the user:
528+
529+
- Contract Invariants: these are properties over the contract's state variables
530+
that are true before and after every possible transaction that the contract may ever run. For example, ``x >= y``, where ``x`` and ``y`` are a contract's state variables.
531+
- Reentrancy Properties: they represent the behavior of the contract
532+
in the presence of external calls to unknown code. These properties can express a relation
533+
between the value of the state variables before and after the external call, where the external call is free to do anything, including making reentrant calls to the analyzed contract. Primed variables represent the state variables' values after said external call. Example: ``lock -> x = x'``.
534+
535+
The user can choose the type of invariants to be reported using the CLI option ``--model-checker-invariants "contract,reentrancy"`` or as an array in the field ``settings.modelChecker.invariants`` in the :ref:`JSON input<compiler-api>`.
536+
By default the SMTChecker does not report invariants.
537+
521538
Division and Modulo With Slack Variables
522539
========================================
523540

docs/using-the-compiler.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,8 @@ Input Description
418418
"divModWithSlacks": true,
419419
// Choose which model checker engine to use: all (default), bmc, chc, none.
420420
"engine": "chc",
421+
// Choose which types of invariants should be reported to the user: contract, reentrancy.
422+
"invariants": ["contract", "reentrancy"],
421423
// Choose whether to output all unproved targets. The default is `false`.
422424
"showUnproved": true,
423425
// Choose which solvers should be used, if available.

liblangutil/ErrorReporter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,8 @@ void ErrorReporter::info(
261261
{
262262
error(_error, Error::Type::Info, _location, _description);
263263
}
264+
265+
void ErrorReporter::info(ErrorId _error, string const& _description)
266+
{
267+
error(_error, Error::Type::Info, SourceLocation(), _description);
268+
}

liblangutil/ErrorReporter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class ErrorReporter
7272
std::string const& _description
7373
);
7474

75+
void info(ErrorId _error, std::string const& _description);
76+
7577
void declarationError(
7678
ErrorId _error,
7779
SourceLocation const& _location,

liblangutil/UniqueErrorReporter.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@ class UniqueErrorReporter
6161
m_errorReporter.warning(_error, _description);
6262
}
6363

64+
void info(ErrorId _error, SourceLocation const& _location, std::string const& _description)
65+
{
66+
if (!seen(_error, _location, _description))
67+
{
68+
m_errorReporter.info(_error, _location, _description);
69+
markAsSeen(_error, _location, _description);
70+
}
71+
}
72+
73+
void info(ErrorId _error, std::string const& _description)
74+
{
75+
m_errorReporter.info(_error, _description);
76+
}
77+
6478
bool seen(ErrorId _error, SourceLocation const& _location, std::string const& _description) const
6579
{
6680
if (m_seenErrors.count({_error, _location}))

libsmtutil/CHCSmtLib2Interface.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ void CHCSmtLib2Interface::addRule(Expression const& _expr, std::string const& /*
8787
);
8888
}
8989

90-
pair<CheckResult, CHCSolverInterface::CexGraph> CHCSmtLib2Interface::query(Expression const& _block)
90+
tuple<CheckResult, Expression, CHCSolverInterface::CexGraph> CHCSmtLib2Interface::query(Expression const& _block)
9191
{
9292
string accumulated{};
9393
swap(m_accumulatedOutput, accumulated);
@@ -118,8 +118,7 @@ pair<CheckResult, CHCSolverInterface::CexGraph> CHCSmtLib2Interface::query(Expre
118118
else
119119
result = CheckResult::ERROR;
120120

121-
// TODO collect invariants or counterexamples.
122-
return {result, {}};
121+
return {result, Expression(true), {}};
123122
}
124123

125124
void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort)

libsmtutil/CHCSmtLib2Interface.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ class CHCSmtLib2Interface: public CHCSolverInterface
4444

4545
void addRule(Expression const& _expr, std::string const& _name) override;
4646

47-
std::pair<CheckResult, CexGraph> query(Expression const& _expr) override;
47+
/// Takes a function application _expr and checks for reachability.
48+
/// @returns solving result, an invariant, and counterexample graph, if possible.
49+
std::tuple<CheckResult, Expression, CexGraph> query(Expression const& _expr) override;
4850

4951
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
5052

libsmtutil/CHCSolverInterface.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ class CHCSolverInterface
5454
};
5555

5656
/// Takes a function application _expr and checks for reachability.
57-
/// @returns solving result and a counterexample graph, if possible.
58-
virtual std::pair<CheckResult, CexGraph> query(
57+
/// @returns solving result, an invariant, and counterexample graph, if possible.
58+
virtual std::tuple<CheckResult, Expression, CexGraph> query(
5959
Expression const& _expr
6060
) = 0;
6161

libsmtutil/SolverInterface.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
#include <libsolutil/Common.h>
2525
#include <libsolutil/Numeric.h>
26+
#include <libsolutil/CommonData.h>
2627

28+
#include <range/v3/algorithm/all_of.hpp>
2729
#include <range/v3/view.hpp>
2830

2931
#include <cstdio>
@@ -305,6 +307,64 @@ class Expression
305307
);
306308
}
307309

310+
static bool sameSort(std::vector<Expression> const& _args)
311+
{
312+
if (_args.empty())
313+
return true;
314+
315+
auto sort = _args.front().sort;
316+
return ranges::all_of(
317+
_args,
318+
[&](auto const& _expr){ return _expr.sort->kind == sort->kind; }
319+
);
320+
}
321+
322+
static Expression mkAnd(std::vector<Expression> _args)
323+
{
324+
smtAssert(!_args.empty(), "");
325+
smtAssert(sameSort(_args), "");
326+
327+
auto sort = _args.front().sort;
328+
if (sort->kind == Kind::BitVector)
329+
return Expression("bvand", std::move(_args), sort);
330+
331+
smtAssert(sort->kind == Kind::Bool, "");
332+
return Expression("and", std::move(_args), Kind::Bool);
333+
}
334+
335+
static Expression mkOr(std::vector<Expression> _args)
336+
{
337+
smtAssert(!_args.empty(), "");
338+
smtAssert(sameSort(_args), "");
339+
340+
auto sort = _args.front().sort;
341+
if (sort->kind == Kind::BitVector)
342+
return Expression("bvor", std::move(_args), sort);
343+
344+
smtAssert(sort->kind == Kind::Bool, "");
345+
return Expression("or", std::move(_args), Kind::Bool);
346+
}
347+
348+
static Expression mkPlus(std::vector<Expression> _args)
349+
{
350+
smtAssert(!_args.empty(), "");
351+
smtAssert(sameSort(_args), "");
352+
353+
auto sort = _args.front().sort;
354+
smtAssert(sort->kind == Kind::BitVector || sort->kind == Kind::Int, "");
355+
return Expression("+", std::move(_args), sort);
356+
}
357+
358+
static Expression mkMul(std::vector<Expression> _args)
359+
{
360+
smtAssert(!_args.empty(), "");
361+
smtAssert(sameSort(_args), "");
362+
363+
auto sort = _args.front().sort;
364+
smtAssert(sort->kind == Kind::BitVector || sort->kind == Kind::Int, "");
365+
return Expression("*", std::move(_args), sort);
366+
}
367+
308368
friend Expression operator!(Expression _a)
309369
{
310370
if (_a.sort->kind == Kind::BitVector)

0 commit comments

Comments
 (0)