/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 | } |