Coverage Report

Created: 2022-08-24 06:28

/src/solidity/libsmtutil/CHCSmtLib2Interface.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
19
#include <libsmtutil/CHCSmtLib2Interface.h>
20
21
#include <libsolutil/Keccak256.h>
22
23
#include <boost/algorithm/string/join.hpp>
24
#include <boost/algorithm/string/predicate.hpp>
25
26
#include <range/v3/view.hpp>
27
28
#include <array>
29
#include <fstream>
30
#include <iostream>
31
#include <memory>
32
#include <stdexcept>
33
34
using namespace std;
35
using namespace solidity;
36
using namespace solidity::util;
37
using namespace solidity::frontend;
38
using namespace solidity::smtutil;
39
40
CHCSmtLib2Interface::CHCSmtLib2Interface(
41
  map<h256, string> const& _queryResponses,
42
  ReadCallback::Callback _smtCallback,
43
  optional<unsigned> _queryTimeout
44
):
45
  CHCSolverInterface(_queryTimeout),
46
  m_smtlib2(make_unique<SMTLib2Interface>(_queryResponses, _smtCallback, m_queryTimeout)),
47
  m_queryResponses(move(_queryResponses)),
48
  m_smtCallback(_smtCallback)
49
0
{
50
0
  reset();
51
0
}
52
53
void CHCSmtLib2Interface::reset()
54
0
{
55
0
  m_accumulatedOutput.clear();
56
0
  m_variables.clear();
57
0
  m_unhandledQueries.clear();
58
0
  m_sortNames.clear();
59
0
}
60
61
void CHCSmtLib2Interface::registerRelation(Expression const& _expr)
62
0
{
63
0
  smtAssert(_expr.sort);
64
0
  smtAssert(_expr.sort->kind == Kind::Function);
65
0
  if (!m_variables.count(_expr.name))
66
0
  {
67
0
    auto fSort = dynamic_pointer_cast<FunctionSort>(_expr.sort);
68
0
    string domain = toSmtLibSort(fSort->domain);
69
    // Relations are predicates which have implicit codomain Bool.
70
0
    m_variables.insert(_expr.name);
71
0
    write(
72
0
      "(declare-fun |" +
73
0
      _expr.name +
74
0
      "| " +
75
0
      domain +
76
0
      " Bool)"
77
0
    );
78
0
  }
79
0
}
80
81
void CHCSmtLib2Interface::addRule(Expression const& _expr, std::string const& /*_name*/)
82
0
{
83
0
  write(
84
0
    "(assert\n(forall " + forall() + "\n" +
85
0
    m_smtlib2->toSExpr(_expr) +
86
0
    "))\n\n"
87
0
  );
88
0
}
89
90
tuple<CheckResult, Expression, CHCSolverInterface::CexGraph> CHCSmtLib2Interface::query(Expression const& _block)
91
0
{
92
0
  string accumulated{};
93
0
  swap(m_accumulatedOutput, accumulated);
94
0
  solAssert(m_smtlib2, "");
95
0
  writeHeader();
96
0
  for (auto const& decl: m_smtlib2->userSorts() | ranges::views::values)
97
0
    write(decl);
98
0
  m_accumulatedOutput += accumulated;
99
100
0
  string queryRule = "(assert\n(forall " + forall() + "\n" +
101
0
    "(=> " + _block.name + " false)"
102
0
    "))";
103
0
  string response = querySolver(
104
0
    m_accumulatedOutput +
105
0
    queryRule +
106
0
    "\n(check-sat)"
107
0
  );
108
0
  swap(m_accumulatedOutput, accumulated);
109
110
0
  CheckResult result;
111
  // TODO proper parsing
112
0
  if (boost::starts_with(response, "sat"))
113
0
    result = CheckResult::UNSATISFIABLE;
114
0
  else if (boost::starts_with(response, "unsat"))
115
0
    result = CheckResult::SATISFIABLE;
116
0
  else if (boost::starts_with(response, "unknown"))
117
0
    result = CheckResult::UNKNOWN;
118
0
  else
119
0
    result = CheckResult::ERROR;
120
121
0
  return {result, Expression(true), {}};
122
0
}
123
124
void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort)
125
0
{
126
0
  smtAssert(_sort);
127
0
  if (_sort->kind == Kind::Function)
128
0
    declareFunction(_name, _sort);
129
0
  else if (!m_variables.count(_name))
130
0
  {
131
0
    m_variables.insert(_name);
132
0
    write("(declare-var |" + _name + "| " + toSmtLibSort(*_sort) + ')');
133
0
  }
134
0
}
135
136
string CHCSmtLib2Interface::toSmtLibSort(Sort const& _sort)
137
0
{
138
0
  if (!m_sortNames.count(&_sort))
139
0
    m_sortNames[&_sort] = m_smtlib2->toSmtLibSort(_sort);
140
0
  return m_sortNames.at(&_sort);
141
0
}
142
143
string CHCSmtLib2Interface::toSmtLibSort(vector<SortPointer> const& _sorts)
144
0
{
145
0
  string ssort("(");
146
0
  for (auto const& sort: _sorts)
147
0
    ssort += toSmtLibSort(*sort) + " ";
148
0
  ssort += ")";
149
0
  return ssort;
150
0
}
151
152
void CHCSmtLib2Interface::writeHeader()
153
0
{
154
0
  if (m_queryTimeout)
155
0
    write("(set-option :timeout " + to_string(*m_queryTimeout) + ")");
156
0
  write("(set-logic HORN)\n");
157
0
}
158
159
string CHCSmtLib2Interface::forall()
160
0
{
161
0
  string vars("(");
162
0
  for (auto const& [name, sort]: m_smtlib2->variables())
163
0
  {
164
0
    solAssert(sort, "");
165
0
    if (sort->kind != Kind::Function)
166
0
      vars += " (" + name + " " + toSmtLibSort(*sort) + ")";
167
0
  }
168
0
  vars += ")";
169
0
  return vars;
170
0
}
171
172
void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const& _sort)
173
0
{
174
0
  smtAssert(_sort);
175
0
  smtAssert(_sort->kind == Kind::Function);
176
  // TODO Use domain and codomain as key as well
177
0
  if (!m_variables.count(_name))
178
0
  {
179
0
    auto fSort = dynamic_pointer_cast<FunctionSort>(_sort);
180
0
    smtAssert(fSort->codomain);
181
0
    string domain = toSmtLibSort(fSort->domain);
182
0
    string codomain = toSmtLibSort(*fSort->codomain);
183
0
    m_variables.insert(_name);
184
0
    write(
185
0
      "(declare-fun |" +
186
0
      _name +
187
0
      "| " +
188
0
      domain +
189
0
      " " +
190
0
      codomain +
191
0
      ")"
192
0
    );
193
0
  }
194
0
}
195
196
void CHCSmtLib2Interface::write(string _data)
197
0
{
198
0
  m_accumulatedOutput += move(_data) + "\n";
199
0
}
200
201
string CHCSmtLib2Interface::querySolver(string const& _input)
202
0
{
203
0
  util::h256 inputHash = util::keccak256(_input);
204
0
  if (m_queryResponses.count(inputHash))
205
0
    return m_queryResponses.at(inputHash);
206
0
  if (m_smtCallback)
207
0
  {
208
0
    auto result = m_smtCallback(ReadCallback::kindString(ReadCallback::Kind::SMTQuery), _input);
209
0
    if (result.success)
210
0
      return result.responseOrErrorMessage;
211
0
  }
212
0
  m_unhandledQueries.push_back(_input);
213
0
  return "unknown\n";
214
0
}