Coverage Report

Created: 2026-08-14 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libsolidity/formal/SymbolicState.cpp
Line
Count
Source
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 <libsolidity/formal/SymbolicState.h>
20
21
#include <libsolidity/formal/SymbolicTypes.h>
22
#include <libsolidity/formal/EncodingContext.h>
23
#include <libsolidity/formal/SMTEncoder.h>
24
25
#include <libsmtutil/Sorts.h>
26
27
#include <range/v3/view.hpp>
28
29
using namespace solidity;
30
using namespace solidity::util;
31
using namespace solidity::smtutil;
32
using namespace solidity::frontend::smt;
33
34
BlockchainVariable::BlockchainVariable(
35
  std::string _name,
36
  std::map<std::string, smtutil::SortPointer> _members,
37
  EncodingContext& _context
38
):
39
131k
  m_name(std::move(_name)),
40
131k
  m_members(std::move(_members)),
41
131k
  m_context(_context)
42
131k
{
43
131k
  std::vector<std::string> members;
44
131k
  std::vector<SortPointer> sorts;
45
131k
  for (auto const& [component, sort]: m_members)
46
365k
  {
47
365k
    members.emplace_back(component);
48
365k
    sorts.emplace_back(sort);
49
365k
    m_componentIndices[component] = static_cast<unsigned>(members.size() - 1);
50
365k
  }
51
131k
  m_tuple = std::make_unique<SymbolicTupleVariable>(
52
131k
    std::make_shared<smtutil::TupleSort>(m_name + "_type", members, sorts),
53
131k
    m_name,
54
131k
    m_context
55
131k
  );
56
131k
}
57
58
smtutil::Expression BlockchainVariable::member(std::string const& _member) const
59
890k
{
60
890k
  return m_tuple->component(m_componentIndices.at(_member));
61
890k
}
62
63
smtutil::Expression BlockchainVariable::assignMember(std::string const& _member, smtutil::Expression const& _value)
64
15.6k
{
65
15.6k
  smtutil::Expression newTuple = smt::assignMember(m_tuple->currentValue(), {{_member, _value}});
66
15.6k
  m_context.addAssertion(m_tuple->increaseIndex() == newTuple);
67
15.6k
  return m_tuple->currentValue();
68
15.6k
}
69
70
void SymbolicState::reset()
71
319k
{
72
319k
  m_error.resetIndex();
73
319k
  m_thisAddress.resetIndex();
74
319k
  m_tx.reset();
75
319k
  m_crypto.reset();
76
319k
  if (m_abi)
77
304k
    m_abi->reset();
78
  /// We don't reset nor clear these pointers on purpose,
79
  /// since it only helps to keep the already generated types.
80
319k
  if (m_state)
81
304k
    m_state->reset();
82
319k
}
83
84
smtutil::Expression SymbolicState::balances() const
85
59.4k
{
86
59.4k
  return m_state->member("balances");
87
59.4k
}
88
89
smtutil::Expression SymbolicState::balance() const
90
396
{
91
396
  return balance(thisAddress());
92
396
}
93
94
smtutil::Expression SymbolicState::balance(smtutil::Expression _address) const
95
45.5k
{
96
45.5k
  return smtutil::Expression::select(balances(), std::move(_address));
97
45.5k
}
98
99
smtutil::Expression SymbolicState::blobhash(smtutil::Expression _blobIndex) const
100
2
{
101
2
  return smtutil::Expression::select(m_tx.member("blobhash"), std::move(_blobIndex));
102
2
}
103
104
smtutil::Expression SymbolicState::blockhash(smtutil::Expression _blockNumber) const
105
40
{
106
40
  return smtutil::Expression::select(m_tx.member("blockhash"), std::move(_blockNumber));
107
40
}
108
109
void SymbolicState::newBalances()
110
1.78k
{
111
1.78k
  auto tupleSort = std::dynamic_pointer_cast<TupleSort>(stateSort());
112
1.78k
  auto balanceSort = tupleSort->components.at(tupleSort->memberToIndex.at("balances"));
113
1.78k
  SymbolicVariable newBalances(balanceSort, "fresh_balances_" + std::to_string(m_context.newUniqueId()), m_context);
114
1.78k
  m_state->assignMember("balances", newBalances.currentValue());
115
1.78k
}
116
117
void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value)
118
198
{
119
198
  unsigned indexBefore = m_state->index();
120
198
  addBalance(_from, 0 - _value);
121
198
  addBalance(_to, std::move(_value));
122
198
  unsigned indexAfter = m_state->index();
123
198
  solAssert(indexAfter > indexBefore, "");
124
198
  m_state->newVar();
125
  /// Do not apply the transfer operation if _from == _to.
126
198
  auto newState = smtutil::Expression::ite(
127
198
    std::move(_from) == std::move(_to),
128
198
    m_state->value(indexBefore),
129
198
    m_state->value(indexAfter)
130
198
  );
131
198
  m_context.addAssertion(m_state->value() == newState);
132
198
}
133
134
smtutil::Expression SymbolicState::storage(ContractDefinition const& _contract) const
135
0
{
136
0
  return smt::member(m_state->member("storage"), contractStorageKey(_contract));
137
0
}
138
139
smtutil::Expression SymbolicState::storage(ContractDefinition const& _contract, smtutil::Expression _address) const
140
0
{
141
0
  return smtutil::Expression::select(storage(_contract), std::move(_address));
142
0
}
143
144
smtutil::Expression SymbolicState::addressActive(smtutil::Expression _address) const
145
0
{
146
0
  return smtutil::Expression::select(m_state->member("isActive"), std::move(_address));
147
0
}
148
149
void SymbolicState::setAddressActive(
150
  smtutil::Expression _address,
151
  bool _active
152
)
153
0
{
154
0
  m_state->assignMember("isActive", smtutil::Expression::store(
155
0
    m_state->member("isActive"),
156
0
    std::move(_address),
157
0
    smtutil::Expression(_active))
158
0
  );
159
0
}
160
161
void SymbolicState::newStorage()
162
0
{
163
0
  auto newStorageVar = SymbolicTupleVariable(
164
0
    m_state->member("storage").sort,
165
0
    "havoc_storage_" + std::to_string(m_context.newUniqueId()),
166
0
    m_context
167
0
  );
168
0
  m_state->assignMember("storage", newStorageVar.currentValue());
169
0
}
170
171
void SymbolicState::writeStateVars(ContractDefinition const& _contract, smtutil::Expression _address)
172
0
{
173
0
  auto stateVars = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract);
174
0
  if (stateVars.empty())
175
0
    return;
176
177
0
  std::map<std::string, smtutil::Expression> values;
178
0
  for (auto var: stateVars)
179
0
    values.emplace(stateVarStorageKey(*var, _contract), m_context.variable(*var)->currentValue());
180
181
0
  smtutil::Expression thisStorage = storage(_contract, _address);
182
0
  smtutil::Expression newStorage = smt::assignMember(thisStorage, values);
183
0
  auto newContractStorage = smtutil::Expression::store(
184
0
    storage(_contract), std::move(_address), newStorage
185
0
  );
186
0
  smtutil::Expression newAllStorage = smt::assignMember(m_state->member("storage"), {{contractStorageKey(_contract), newContractStorage}});
187
0
  m_state->assignMember("storage", newAllStorage);
188
0
}
189
190
void SymbolicState::readStateVars(ContractDefinition const& _contract, smtutil::Expression _address)
191
0
{
192
0
  auto stateVars = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract);
193
0
  if (stateVars.empty())
194
0
    return;
195
196
0
  auto contractStorage = storage(_contract, std::move(_address));
197
0
  for (auto var: stateVars)
198
0
    m_context.addAssertion(
199
0
      m_context.variable(*var)->increaseIndex() ==
200
0
      smt::member(contractStorage, stateVarStorageKey(*var, _contract))
201
0
    );
202
0
}
203
204
void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _value)
205
13.8k
{
206
13.8k
  auto newBalances = smtutil::Expression::store(
207
13.8k
    balances(),
208
13.8k
    _address,
209
13.8k
    balance(_address) + std::move(_value)
210
13.8k
  );
211
13.8k
  m_state->assignMember("balances", newBalances);
212
13.8k
}
213
214
smtutil::Expression SymbolicState::txMember(std::string const& _member) const
215
80.8k
{
216
80.8k
  return m_tx.member(_member);
217
80.8k
}
218
219
smtutil::Expression SymbolicState::evmParisConstraints() const
220
47.6k
{
221
  // Ensure prevrandao range as defined by EIP-4399.
222
47.6k
  return txMember("block.prevrandao") > (u256(1) << 64);
223
47.6k
}
224
225
smtutil::Expression SymbolicState::txTypeConstraints() const
226
47.6k
{
227
47.6k
  return
228
47.6k
    evmParisConstraints() &&
229
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("block.basefee"), TypeProvider::uint256()) &&
230
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("block.blobbasefee"), TypeProvider::uint256()) &&
231
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("block.chainid"), TypeProvider::uint256()) &&
232
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("block.coinbase"), TypeProvider::address()) &&
233
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("block.prevrandao"), TypeProvider::uint256()) &&
234
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("block.gaslimit"), TypeProvider::uint256()) &&
235
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("block.number"), TypeProvider::uint256()) &&
236
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("block.slotnum"), TypeProvider::uint(64)) &&
237
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("block.timestamp"), TypeProvider::uint256()) &&
238
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("msg.sender"), TypeProvider::address()) &&
239
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("msg.value"), TypeProvider::uint256()) &&
240
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("tx.origin"), TypeProvider::address()) &&
241
47.6k
    smt::symbolicUnknownConstraints(m_tx.member("tx.gasprice"), TypeProvider::uint256());
242
47.6k
}
243
244
smtutil::Expression SymbolicState::txNonPayableConstraint() const
245
46.2k
{
246
46.2k
  return m_tx.member("msg.value") == 0;
247
46.2k
}
248
249
smtutil::Expression SymbolicState::txFunctionConstraints(FunctionDefinition const& _function) const
250
30.0k
{
251
30.0k
  smtutil::Expression conj = _function.isPayable() ? smtutil::Expression(true) : txNonPayableConstraint();
252
30.0k
  if (_function.isPartOfExternalInterface())
253
27.5k
  {
254
27.5k
    auto sig = TypeProvider::function(_function)->externalIdentifier();
255
27.5k
    conj = conj && m_tx.member("msg.sig") == sig;
256
27.5k
    auto b0 = sig >> (3 * 8);
257
27.5k
    auto b1 = (sig & 0x00ff0000) >> (2 * 8);
258
27.5k
    auto b2 = (sig & 0x0000ff00) >> (1 * 8);
259
27.5k
    auto b3 = (sig & 0x000000ff);
260
27.5k
    auto data = smtutil::Expression::tuple_get(m_tx.member("msg.data"), 0);
261
27.5k
    conj = conj && smtutil::Expression::select(data, 0) == b0;
262
27.5k
    conj = conj && smtutil::Expression::select(data, 1) == b1;
263
27.5k
    conj = conj && smtutil::Expression::select(data, 2) == b2;
264
27.5k
    conj = conj && smtutil::Expression::select(data, 3) == b3;
265
27.5k
    auto length = smtutil::Expression::tuple_get(m_tx.member("msg.data"), 1);
266
    // TODO add ABI size of function input parameters here \/
267
27.5k
    conj = conj && length >= 4;
268
27.5k
  }
269
270
30.0k
  return conj;
271
30.0k
}
272
273
void SymbolicState::prepareForSourceUnit(SourceUnit const& _source, bool _storage)
274
33.2k
{
275
33.2k
  auto allSources = _source.referencedSourceUnits(true);
276
33.2k
  allSources.insert(&_source);
277
33.2k
  std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> abiCalls;
278
33.2k
  std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> bytesConcatCalls;
279
33.2k
  std::set<ContractDefinition const*, ASTCompareByID<ContractDefinition>> contracts;
280
33.2k
  for (auto const& source: allSources)
281
34.3k
  {
282
34.3k
    abiCalls += SMTEncoder::collectABICalls(source);
283
34.3k
    bytesConcatCalls += SMTEncoder::collectBytesConcatCalls(source);
284
34.3k
    for (auto node: source->nodes())
285
82.6k
      if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
286
36.2k
        contracts.insert(contract);
287
34.3k
  }
288
33.2k
  buildState(contracts, _storage);
289
33.2k
  buildABIFunctions(abiCalls);
290
33.2k
  buildBytesConcatFunctions(bytesConcatCalls);
291
33.2k
}
292
293
/// Private helpers.
294
295
std::string SymbolicState::contractSuffix(ContractDefinition const& _contract) const
296
0
{
297
0
  return "_" + _contract.name() + "_" + std::to_string(_contract.id());
298
0
}
299
300
std::string SymbolicState::contractStorageKey(ContractDefinition const& _contract) const
301
0
{
302
0
  return "storage" + contractSuffix(_contract);
303
0
}
304
305
std::string SymbolicState::stateVarStorageKey(VariableDeclaration const& _var, ContractDefinition const& _contract) const
306
0
{
307
0
  return _var.name() + "_" + std::to_string(_var.id()) + contractSuffix(_contract);
308
0
}
309
310
void SymbolicState::buildState(std::set<ContractDefinition const*, ASTCompareByID<ContractDefinition>> const& _contracts, bool _allStorages)
311
33.2k
{
312
33.2k
  std::map<std::string, SortPointer> stateMembers{
313
33.2k
    {"balances", std::make_shared<smtutil::ArraySort>(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort)}
314
33.2k
  };
315
316
33.2k
  if (_allStorages)
317
0
  {
318
0
    std::vector<std::string> memberNames;
319
0
    std::vector<SortPointer> memberSorts;
320
0
    for (auto contract: _contracts)
321
0
    {
322
0
      std::string suffix = contractSuffix(*contract);
323
324
      // z3 doesn't like empty tuples, so if the contract has 0
325
      // state vars we can't put it there.
326
0
      auto stateVars = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*contract);
327
0
      if (stateVars.empty())
328
0
        continue;
329
330
0
      auto names = applyMap(stateVars, [&](auto var) {
331
0
        return var->name() + "_" + std::to_string(var->id()) + suffix;
332
0
      });
333
0
      auto sorts = applyMap(stateVars, [](auto var) { return smtSortAbstractFunction(*var->type()); });
334
335
0
      std::string name = "storage" + suffix;
336
0
      auto storageTuple = std::make_shared<smtutil::TupleSort>(
337
0
        name + "_type", names, sorts
338
0
      );
339
340
0
      auto storageSort = std::make_shared<smtutil::ArraySort>(
341
0
        smtSort(*TypeProvider::address()),
342
0
        storageTuple
343
0
      );
344
345
0
      memberNames.emplace_back(name);
346
0
      memberSorts.emplace_back(storageSort);
347
0
    }
348
349
0
    stateMembers.emplace(
350
0
      "isActive",
351
0
      std::make_shared<smtutil::ArraySort>(smtSort(*TypeProvider::address()), smtutil::SortProvider::boolSort)
352
0
    );
353
0
    stateMembers.emplace(
354
0
      "storage",
355
0
      std::make_shared<smtutil::TupleSort>(
356
0
        "storage_type", memberNames, memberSorts
357
0
      )
358
0
    );
359
0
  }
360
361
33.2k
  m_state = std::make_unique<BlockchainVariable>(
362
33.2k
    "state",
363
33.2k
    std::move(stateMembers),
364
33.2k
    m_context
365
33.2k
  );
366
33.2k
}
367
368
void SymbolicState::buildBytesConcatFunctions(std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> const& _bytesConcatCalls)
369
33.2k
{
370
33.2k
  std::map<std::string, SortPointer> functions;
371
372
33.2k
  for (auto const* funCall: _bytesConcatCalls)
373
296
  {
374
296
    auto t = dynamic_cast<FunctionType const*>(funCall->expression().annotation().type);
375
296
    solAssert(t->kind() == FunctionType::Kind::BytesConcat, "Expected bytes.concat function");
376
377
296
    auto const& args = funCall->sortedArguments();
378
379
296
    auto argTypes = [](auto const& _args) {
380
662
      return util::applyMap(_args, [](auto arg) { return arg->annotation().type; });
381
296
    };
382
383
    // bytes.concat : (bytes/literal/fixed bytes, ... ) -> bytes
384
296
    std::vector<frontend::Type const*> inTypes = argTypes(args);
385
386
296
    auto replaceUserDefinedValueTypes = [](auto& _types) {
387
296
      for (auto& t: _types)
388
662
        if (auto userType = dynamic_cast<UserDefinedValueType const*>(t))
389
0
          t = &userType->underlyingType();
390
296
    };
391
296
    auto replaceStringLiteralTypes = [](auto& _types) {
392
296
      for (auto& t: _types)
393
662
        if (t->category() == frontend::Type::Category::StringLiteral)
394
176
          t = TypeProvider::bytesMemory();
395
296
    };
396
296
    replaceUserDefinedValueTypes(inTypes);
397
296
    replaceStringLiteralTypes(inTypes);
398
399
296
    auto name = t->richIdentifier();
400
296
    for (auto paramType: inTypes)
401
662
      name += "_" + paramType->richIdentifier();
402
403
296
    frontend::Type const* outType = TypeProvider::bytesMemory();
404
296
    name += "_" + outType->richIdentifier();
405
406
296
    m_bytesConcatMembers[funCall] = {name, inTypes, outType};
407
408
296
    if (functions.count(name))
409
134
      continue;
410
411
    /// If there is only one parameter, we use that type directly.
412
    /// Otherwise we create a tuple wrapping the necessary types.
413
162
    auto typesToSort = [](auto const& _types, std::string const& _name) -> std::shared_ptr<Sort> {
414
162
      if (_types.size() == 1)
415
8
        return smtSortAbstractFunction(*_types.front());
416
417
154
      std::vector<std::string> inNames;
418
154
      std::vector<SortPointer> sorts;
419
540
      for (unsigned i = 0; i < _types.size(); ++i)
420
386
      {
421
386
        inNames.emplace_back(_name + "_input_" + std::to_string(i));
422
386
        sorts.emplace_back(smtSortAbstractFunction(*_types.at(i)));
423
386
      }
424
154
      return std::make_shared<smtutil::TupleSort>(
425
154
        _name + "_input",
426
154
        inNames,
427
154
        sorts
428
154
      );
429
162
    };
430
431
162
    auto functionSort = std::make_shared<smtutil::ArraySort>(
432
162
      typesToSort(inTypes, name),
433
162
      smtSortAbstractFunction(*outType)
434
162
    );
435
436
162
    functions[name] = functionSort;
437
162
  }
438
439
33.2k
  m_bytesConcat = std::make_unique<BlockchainVariable>("bytesConcat", std::move(functions), m_context);
440
33.2k
}
441
442
void SymbolicState::buildABIFunctions(std::set<FunctionCall const*, ASTCompareByID<FunctionCall>> const& _abiFunctions)
443
33.2k
{
444
33.2k
  std::map<std::string, SortPointer> functions;
445
446
33.2k
  for (auto const* funCall: _abiFunctions)
447
1.03k
  {
448
1.03k
    auto t = dynamic_cast<FunctionType const*>(funCall->expression().annotation().type);
449
450
1.03k
    auto const& args = funCall->sortedArguments();
451
1.03k
    auto const& paramTypes = t->parameterTypes();
452
1.03k
    auto const& returnTypes = t->returnParameterTypes();
453
454
1.03k
    auto argTypes = [](auto const& _args) {
455
908
      return util::applyMap(_args, [](auto arg) { return arg->annotation().type; });
456
698
    };
457
458
    /// Since each abi.* function may have a different number of input/output parameters,
459
    /// we generically compute those types.
460
1.03k
    std::vector<frontend::Type const*> inTypes;
461
1.03k
    std::vector<frontend::Type const*> outTypes;
462
1.03k
    if (t->kind() == FunctionType::Kind::ABIDecode)
463
300
    {
464
      /// abi.decode : (bytes, tuple_of_types(return_types)) -> (return_types)
465
300
      solAssert(args.size() == 2, "Unexpected number of arguments for abi.decode");
466
300
      inTypes.emplace_back(TypeProvider::bytesMemory());
467
300
      auto argType = args.at(1)->annotation().type;
468
300
      if (auto const* tupleType = dynamic_cast<TupleType const*>(argType))
469
44
        for (auto componentType: tupleType->components())
470
88
        {
471
88
          auto typeType = dynamic_cast<TypeType const*>(componentType);
472
88
          solAssert(typeType, "");
473
88
          outTypes.emplace_back(typeType->actualType());
474
88
        }
475
256
      else if (auto const* typeType = dynamic_cast<TypeType const*>(argType))
476
256
        outTypes.emplace_back(typeType->actualType());
477
0
      else
478
256
        solAssert(false, "Unexpected argument of abi.decode");
479
300
    }
480
738
    else if (t->kind() == FunctionType::Kind::ABIEncodeCall)
481
40
    {
482
      // abi.encodeCall : (functionPointer, tuple_of_args_or_one_non_tuple_arg(arguments)) -> bytes
483
40
      solAssert(args.size() == 2, "Unexpected number of arguments for abi.encodeCall");
484
485
40
      outTypes.emplace_back(TypeProvider::bytesMemory());
486
40
      inTypes.emplace_back(args.at(0)->annotation().type);
487
40
      inTypes.emplace_back(args.at(1)->annotation().type);
488
40
    }
489
698
    else
490
698
    {
491
698
      outTypes = returnTypes;
492
698
      if (
493
698
        t->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
494
652
        t->kind() == FunctionType::Kind::ABIEncodeWithSignature
495
698
      )
496
90
      {
497
        /// abi.encodeWithSelector : (bytes4, one_or_more_types) -> bytes
498
        /// abi.encodeWithSignature : (string, one_or_more_types) -> bytes
499
90
        inTypes.emplace_back(paramTypes.front());
500
90
        inTypes += argTypes(std::vector<ASTPointer<Expression const>>(args.begin() + 1, args.end()));
501
90
      }
502
608
      else
503
608
      {
504
        /// abi.encode/abi.encodePacked : one_or_more_types -> bytes
505
608
        solAssert(
506
608
          t->kind() == FunctionType::Kind::ABIEncode ||
507
608
          t->kind() == FunctionType::Kind::ABIEncodePacked,
508
608
          ""
509
608
        );
510
608
        inTypes = argTypes(args);
511
608
      }
512
698
    }
513
514
    /// Rational numbers and string literals add the concrete values to the type name,
515
    /// so we replace them by uint256 and bytes since those are the same as their SMT types.
516
    /// TODO we could also replace all types by their ABI type.
517
2.07k
    auto replaceTypes = [](auto& _types) {
518
2.07k
      for (auto& t: _types)
519
2.46k
        if (t->category() == frontend::Type::Category::RationalNumber)
520
294
          t = TypeProvider::uint256();
521
2.16k
        else if (t->category() == frontend::Type::Category::StringLiteral)
522
36
          t = TypeProvider::bytesMemory();
523
2.13k
        else if (auto userType = dynamic_cast<UserDefinedValueType const*>(t))
524
20
          t = &userType->underlyingType();
525
2.07k
    };
526
1.03k
    replaceTypes(inTypes);
527
1.03k
    replaceTypes(outTypes);
528
529
1.03k
    auto name = t->richIdentifier();
530
1.03k
    for (auto paramType: inTypes + outTypes)
531
2.46k
      name += "_" + paramType->richIdentifier();
532
533
1.03k
    m_abiMembers[funCall] = {name, inTypes, outTypes};
534
535
1.03k
    if (functions.count(name))
536
304
      continue;
537
538
    /// If there is only one input or output parameter, we use that type directly.
539
    /// Otherwise we create a tuple wrapping the necessary input or output types.
540
1.46k
    auto typesToSort = [](auto const& _types, std::string const& _name) -> std::shared_ptr<Sort> {
541
1.46k
      if (_types.size() == 1)
542
1.18k
        return smtSortAbstractFunction(*_types.front());
543
544
288
      std::vector<std::string> inNames;
545
288
      std::vector<SortPointer> sorts;
546
850
      for (unsigned i = 0; i < _types.size(); ++i)
547
562
      {
548
562
        inNames.emplace_back(_name + "_input_" + std::to_string(i));
549
562
        sorts.emplace_back(smtSortAbstractFunction(*_types.at(i)));
550
562
      }
551
288
      return std::make_shared<smtutil::TupleSort>(
552
288
        _name + "_input",
553
288
        inNames,
554
288
        sorts
555
288
      );
556
1.46k
    };
557
558
734
    auto functionSort = std::make_shared<smtutil::ArraySort>(
559
734
      typesToSort(inTypes, name),
560
734
      typesToSort(outTypes, name)
561
734
    );
562
563
734
    functions[name] = functionSort;
564
734
  }
565
566
33.2k
  m_abi = std::make_unique<BlockchainVariable>("abi", std::move(functions), m_context);
567
33.2k
}
568
569
smtutil::Expression SymbolicState::abiFunction(frontend::FunctionCall const* _funCall)
570
1.11k
{
571
1.11k
  solAssert(m_abi, "");
572
1.11k
  return m_abi->member(std::get<0>(m_abiMembers.at(_funCall)));
573
1.11k
}
574
575
SymbolicState::SymbolicABIFunction const& SymbolicState::abiFunctionTypes(FunctionCall const* _funCall) const
576
1.11k
{
577
1.11k
  return m_abiMembers.at(_funCall);
578
1.11k
}
579
580
smtutil::Expression SymbolicState::bytesConcatFunction(frontend::FunctionCall const* _funCall)
581
355
{
582
355
  solAssert(m_bytesConcat, "");
583
355
  return m_bytesConcat->member(std::get<0>(m_bytesConcatMembers.at(_funCall)));
584
355
}
585
586
SymbolicState::SymbolicBytesConcatFunction const& SymbolicState::bytesConcatFunctionTypes(FunctionCall const* _funCall) const
587
355
{
588
355
  return m_bytesConcatMembers.at(_funCall);
589
355
}