diff --git a/libsolidity/formal/Invariants.cpp b/libsolidity/formal/Invariants.cpp new file mode 100644 index 000000000..9dad0722c --- /dev/null +++ b/libsolidity/formal/Invariants.cpp @@ -0,0 +1,86 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include + +#include + +#include + +using namespace std; +using boost::algorithm::starts_with; +using namespace solidity; +using namespace solidity::smtutil; +using namespace solidity::frontend::smt; + +namespace solidity::frontend::smt +{ + +map> collectInvariants( + smtutil::Expression const& _proof, + set const& _predicates, + ModelCheckerInvariants const& _invariantsSetting +) +{ + set targets; + if (_invariantsSetting.has(InvariantType::Contract)) + targets.insert("interface_"); + if (_invariantsSetting.has(InvariantType::Reentrancy)) + targets.insert("nondet_interface_"); + + map> equalities; + // Collect equalities where one of the sides is a predicate we're interested in. + BreadthFirstSearch{{&_proof}}.run([&](auto&& _expr, auto&& _addChild) { + if (_expr->name == "=") + for (auto const& t: targets) + { + auto arg0 = _expr->arguments.at(0); + auto arg1 = _expr->arguments.at(1); + if (starts_with(arg0.name, t)) + equalities.insert({arg0.name, {arg0, move(arg1)}}); + else if (starts_with(arg1.name, t)) + equalities.insert({arg1.name, {arg1, move(arg0)}}); + } + for (auto const& arg: _expr->arguments) + _addChild(&arg); + }); + + map> invariants; + for (auto pred: _predicates) + { + auto predName = pred->functor().name; + if (!equalities.count(predName)) + continue; + + solAssert(pred->contextContract(), ""); + + auto const& [predExpr, invExpr] = equalities.at(predName); + + static set const ignore{"true", "false"}; + auto r = substitute(invExpr, pred->expressionSubstitution(predExpr)); + // No point in reporting true/false as invariants. + if (!ignore.count(r.name)) + invariants[pred].insert(toSolidityStr(r)); + } + return invariants; +} + +} diff --git a/libsolidity/formal/Invariants.h b/libsolidity/formal/Invariants.h new file mode 100644 index 000000000..b6459fccd --- /dev/null +++ b/libsolidity/formal/Invariants.h @@ -0,0 +1,37 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include +#include +#include + +namespace solidity::frontend::smt +{ + +std::map> collectInvariants( + smtutil::Expression const& _proof, + std::set const& _predicates, + ModelCheckerInvariants const& _invariantsSettings +); + +}