/src/solidity/test/tools/ossfuzz/protoToAbiV2.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <test/tools/ossfuzz/abiV2Proto.pb.h> |
4 | | |
5 | | #include <libsolutil/FixedHash.h> |
6 | | #include <libsolutil/Keccak256.h> |
7 | | #include <libsolutil/StringUtils.h> |
8 | | #include <libsolutil/Whiskers.h> |
9 | | #include <libsolutil/Numeric.h> |
10 | | |
11 | | #include <liblangutil/Exceptions.h> |
12 | | |
13 | | #include <boost/algorithm/string.hpp> |
14 | | |
15 | | #include <ostream> |
16 | | #include <random> |
17 | | #include <sstream> |
18 | | |
19 | | /** |
20 | | * Template of the solidity test program generated by this converter is as follows: |
21 | | * |
22 | | * pragma solidity >=0.0; |
23 | | * pragma experimental ABIEncoderV2; |
24 | | * |
25 | | * contract C { |
26 | | * // State variable |
27 | | * string sv_0; |
28 | | * // Test function that is called by the VM. |
29 | | * // There are 2 variations of this function: one returns |
30 | | * // the output of test_calldata_coding() and the other |
31 | | * // returns the output of test_returndata_coding(). The |
32 | | * // proto field called Test decides which one of the two |
33 | | * // are chosen for creating a test case. |
34 | | * function test() public returns (uint) { |
35 | | * // The protobuf field "Contract.test" decides which of |
36 | | * // the two internal functions "test_calldata_coding()" |
37 | | * // and "test_returndata_coding()" are called. Here, |
38 | | * // we assume that the protobuf field equals "CALLDATA_CODER" |
39 | | * return this.test_calldata_coding() |
40 | | * } |
41 | | * |
42 | | * // The following function is generated if the protobuf field |
43 | | * // "Contract.test" is equal to "RETURNDATA_CODER". |
44 | | * function test_returndata_coding() internal returns (uint) { |
45 | | * string memory lv_0, bytes memory lv_1 = test_returndata_external(); |
46 | | * if (lv_0 != 044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d) |
47 | | * return 1; |
48 | | * if (lv_1 != "1") |
49 | | * return 2; |
50 | | * return 0; |
51 | | * } |
52 | | * |
53 | | * // The following function is generated if the protobuf field |
54 | | * // "Contract.test" is equal to "RETURNDATA_CODER". |
55 | | * function test_returndata_external() external returns (string memory, bytes memory) |
56 | | * { |
57 | | * sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"; |
58 | | * bytes memory lv_0 = "1"; |
59 | | * return (sv_0, lv_0); |
60 | | * } |
61 | | * |
62 | | * // The following function is generated if the protobuf field |
63 | | * // "Contract.test" is equal to "CALLDATA_CODER". |
64 | | * function test_calldata_coding() internal returns (uint) { |
65 | | * // Local variable |
66 | | * bytes lv_1 = "1"; |
67 | | * sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"; |
68 | | * uint returnVal = this.coder_public(sv_0, lv_1); |
69 | | * if (returnVal != 0) |
70 | | * return returnVal; |
71 | | * // Since the return codes in the public and external coder functions are identical |
72 | | * // we offset error code by a fixed amount (200000) for differentiation. |
73 | | * returnVal = this.coder_external(sv_0, lv_1); |
74 | | * if (returnVal != 0) |
75 | | * return 200000 + returnVal; |
76 | | * // Encode parameters |
77 | | * bytes memory argumentEncoding = abi.encode(<parameter_names>); |
78 | | * returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>); |
79 | | * // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data. |
80 | | * if (returnVal != 0) |
81 | | * return returnVal; |
82 | | * |
83 | | * returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>); |
84 | | * // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data. |
85 | | * // Offset return value to distinguish between failures originating from coder_public and coder_external. |
86 | | * if (returnVal != 0) |
87 | | * return uint(200000) + returnVal; |
88 | | * // Return zero if all checks pass. |
89 | | * return 0; |
90 | | * } |
91 | | * |
92 | | * /// Accepts function selector, correct argument encoding, and an invalid encoding length as input. |
93 | | * /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding |
94 | | * /// succeeds. Returns zero if both calls meet expectation. |
95 | | * function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz) |
96 | | * public returns (uint) { |
97 | | * ... |
98 | | * } |
99 | | * |
100 | | * /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns |
101 | | * /// the correct and incorrect abi encoding for calling the function specified by the function selector. |
102 | | * function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz) |
103 | | * internal pure returns (bytes memory, bytes memory) { |
104 | | * ... |
105 | | * } |
106 | | * |
107 | | * /// Compares two dynamically sized bytes arrays for equality. |
108 | | * function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) { |
109 | | * ... |
110 | | * } |
111 | | * |
112 | | * // Public function that is called by test() function. Accepts one or more arguments and returns |
113 | | * // a uint value (zero if abi en/decoding was successful, non-zero otherwise) |
114 | | * function coder_public(string memory c_0, bytes memory c_1) public pure returns (uint) { |
115 | | * if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d")) |
116 | | * return 1; |
117 | | * if (!bytesCompare(c_1, "1")) |
118 | | * return 2; |
119 | | * return 0; |
120 | | * } |
121 | | * |
122 | | * // External function that is called by test() function. Accepts one or more arguments and returns |
123 | | * // a uint value (zero if abi en/decoding was successful, non-zero otherwise) |
124 | | * function coder_external(string calldata c_0, bytes calldata c_1) external pure returns (uint) { |
125 | | * if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d")) |
126 | | * return 1; |
127 | | * if (!bytesCompare(c_1, "1")) |
128 | | * return 2; |
129 | | * return 0; |
130 | | * } |
131 | | * } |
132 | | */ |
133 | | |
134 | | namespace solidity::test::abiv2fuzzer |
135 | | { |
136 | | using RandomEngine = std::mt19937_64; |
137 | | using Distribution = std::uniform_int_distribution<unsigned>; |
138 | | using Bernoulli = std::bernoulli_distribution; |
139 | | |
140 | | /// Converts a protobuf input into a Solidity program that tests |
141 | | /// abi coding. |
142 | | class ProtoConverter |
143 | | { |
144 | | public: |
145 | | ProtoConverter(unsigned _seed): |
146 | | m_isStateVar(true), |
147 | | m_counter(0), |
148 | | m_varCounter(0), |
149 | | m_returnValue(1), |
150 | | m_isLastDynParamRightPadded(false), |
151 | | m_structCounter(0), |
152 | | m_numStructsAdded(0) |
153 | 319 | { |
154 | 319 | m_random = std::make_unique<RandomEngine>(_seed); |
155 | 319 | } |
156 | | |
157 | | ProtoConverter(ProtoConverter const&) = delete; |
158 | | ProtoConverter(ProtoConverter&&) = delete; |
159 | | std::string contractToString(Contract const& _input); |
160 | | std::string isabelleTypeString() const; |
161 | | std::string isabelleValueString() const; |
162 | | bool coderFunction() const |
163 | 142 | { |
164 | 142 | return m_test == Contract_Test::Contract_Test_CALLDATA_CODER; |
165 | 142 | } |
166 | | private: |
167 | | enum class Delimiter |
168 | | { |
169 | | ADD, |
170 | | SKIP |
171 | | }; |
172 | | /// Enum of possible function types that decode abi |
173 | | /// encoded parameters. |
174 | | enum class CalleeType |
175 | | { |
176 | | PUBLIC, |
177 | | EXTERNAL |
178 | | }; |
179 | | |
180 | | /// Each external parameter representation contains the following: |
181 | | /// - Delimiter prefix |
182 | | /// - Boolean that is true if value type, false otherwise |
183 | | /// - String representation of type |
184 | | /// - Parameter name |
185 | | using ParameterPack = std::tuple<Delimiter, bool, std::string, std::string>; |
186 | | |
187 | | /// Visitors for various Protobuf types |
188 | | /// Visit top-level contract specification |
189 | | void visit(Contract const&); |
190 | | |
191 | | /// Convert test function specification into Solidity test |
192 | | /// function |
193 | | /// @param _testSpec: Protobuf test function specification |
194 | | /// @param _storageDefs: String containing Solidity assignment |
195 | | /// statements to be placed inside the scope of the test function. |
196 | | std::string visit(TestFunction const& _testSpec, std::string const& _storageDefs); |
197 | | |
198 | | /// Visitors for the remaining protobuf types. They convert |
199 | | /// the input protobuf specification type into Solidity code. |
200 | | /// @return A pair of strings, first of which contains Solidity |
201 | | /// code to be placed inside contract scope, second of which contains |
202 | | /// Solidity code to be placed inside test function scope. |
203 | | std::pair<std::string, std::string> visit(VarDecl const&); |
204 | | std::pair<std::string, std::string> visit(Type const&); |
205 | | std::pair<std::string, std::string> visit(ValueType const&); |
206 | | std::pair<std::string, std::string> visit(NonValueType const&); |
207 | | std::pair<std::string, std::string> visit(BoolType const&); |
208 | | std::pair<std::string, std::string> visit(IntegerType const&); |
209 | | std::pair<std::string, std::string> visit(FixedByteType const&); |
210 | | std::pair<std::string, std::string> visit(AddressType const&); |
211 | | std::pair<std::string, std::string> visit(DynamicByteArrayType const&); |
212 | | std::pair<std::string, std::string> visit(ArrayType const&); |
213 | | std::pair<std::string, std::string> visit(StructType const&); |
214 | | |
215 | | /// Convert a protobuf type @a _T into Solidity code to be placed |
216 | | /// inside contract and test function scopes. |
217 | | /// @param: _type (of parameterized type protobuf type T) is the type |
218 | | /// of protobuf input to be converted. |
219 | | /// @param: _isValueType is true if _type is a Solidity value type e.g., uint |
220 | | /// and false otherwise e.g., string |
221 | | /// @return: A pair of strings, first of which contains Solidity |
222 | | /// code to be placed inside contract scope, second of which contains |
223 | | /// Solidity code to be placed inside test function scope. |
224 | | template <typename T> |
225 | | std::pair<std::string, std::string> processType(T const& _type, bool _isValueType); |
226 | | |
227 | | /// Convert a protobuf type @a _T into Solidity variable assignment and check |
228 | | /// statements to be placed inside contract and test function scopes. |
229 | | /// @param: _varName is the name of the Solidity variable |
230 | | /// @param: _paramName is the name of the Solidity parameter that is passed |
231 | | /// to the test function |
232 | | /// @param: _type (of parameterized type protobuf type T) is the type |
233 | | /// of protobuf input to be converted. |
234 | | /// @return: A pair of strings, first of which contains Solidity |
235 | | /// statements to be placed inside contract scope, second of which contains |
236 | | /// Solidity statements to be placed inside test function scope. |
237 | | template <typename T> |
238 | | std::pair<std::string, std::string> assignChecker( |
239 | | std::string const& _varName, |
240 | | std::string const& _paramName, |
241 | | T _type |
242 | | ); |
243 | | |
244 | | /// Convert a protobuf type @a _T into Solidity variable declaration statement. |
245 | | /// @param: _varName is the name of the Solidity variable |
246 | | /// @param: _paramName is the name of the Solidity parameter that is passed |
247 | | /// to the test function |
248 | | /// @param: _type (of parameterized type protobuf type T) is the type |
249 | | /// of protobuf input to be converted. |
250 | | /// @param: _isValueType is a boolean that is true if _type is a |
251 | | /// Solidity value type e.g., uint and false otherwise e.g., string |
252 | | /// @param: _location is the Solidity location qualifier string to |
253 | | /// be used inside variable declaration statements |
254 | | /// @return: A pair of strings, first of which contains Solidity |
255 | | /// variable declaration statement to be placed inside contract scope, |
256 | | /// second of which contains Solidity variable declaration statement |
257 | | /// to be placed inside test function scope. |
258 | | template <typename T> |
259 | | std::pair<std::string, std::string> varDecl( |
260 | | std::string const& _varName, |
261 | | std::string const& _paramName, |
262 | | T _type, |
263 | | bool _isValueType, |
264 | | std::string const& _location |
265 | | ); |
266 | | |
267 | | /// Appends a function parameter to the function parameter stream. |
268 | | void appendTypedParams( |
269 | | CalleeType _calleeType, |
270 | | bool _isValueType, |
271 | | std::string const& _typeString, |
272 | | std::string const& _varName, |
273 | | Delimiter _delimiter |
274 | | ); |
275 | | |
276 | | /// Appends a function parameter to the public test function's |
277 | | /// parameter stream. |
278 | | void appendTypedParamsPublic( |
279 | | bool _isValueType, |
280 | | std::string const& _typeString, |
281 | | std::string const& _varName, |
282 | | Delimiter _delimiter = Delimiter::ADD |
283 | | ); |
284 | | |
285 | | /// Appends a function parameter to the external test function's |
286 | | /// parameter stream. |
287 | | void appendTypedParamsExternal( |
288 | | bool _isValueType, |
289 | | std::string const& _typeString, |
290 | | std::string const& _varName, |
291 | | Delimiter _delimiter = Delimiter::ADD |
292 | | ); |
293 | | |
294 | | /// Append types to typed stream used by returndata coders. |
295 | | void appendTypes( |
296 | | bool _isValueType, |
297 | | std::string const& _typeString, |
298 | | Delimiter _delimiter |
299 | | ); |
300 | | |
301 | | /// Append typed return value. |
302 | | void appendTypedReturn( |
303 | | bool _isValueType, |
304 | | std::string const& _typeString, |
305 | | Delimiter _delimiter |
306 | | ); |
307 | | |
308 | | /// Append type name to type string meant to be |
309 | | /// passed to Isabelle coder API. |
310 | | void appendToIsabelleTypeString( |
311 | | std::string const& _typeString, |
312 | | Delimiter _delimiter |
313 | | ); |
314 | | |
315 | | /// Append @a _valueString to value string meant to be |
316 | | /// passed to Isabelle coder API. |
317 | | void appendToIsabelleValueString( |
318 | | std::string const& _valueString, |
319 | | Delimiter _delimiter |
320 | | ); |
321 | | |
322 | | /// Returns a Solidity variable declaration statement |
323 | | /// @param _type: string containing Solidity type of the |
324 | | /// variable to be declared. |
325 | | /// @param _varName: string containing Solidity variable |
326 | | /// name |
327 | | /// @param _qualifier: string containing location where |
328 | | /// the variable will be placed |
329 | | std::string getVarDecl( |
330 | | std::string const& _type, |
331 | | std::string const& _varName, |
332 | | std::string const& _qualifier |
333 | | ); |
334 | | |
335 | | /// Return checks that are encoded as Solidity if statements |
336 | | /// as string |
337 | | std::string equalityChecksAsString(); |
338 | | |
339 | | /// Return comma separated typed function parameters as string |
340 | | std::string typedParametersAsString(CalleeType _calleeType); |
341 | | |
342 | | /// Return commonly used Solidity helper functions as string |
343 | | std::string commonHelperFunctions(); |
344 | | |
345 | | /// Return helper functions used to test calldata coding |
346 | | std::string calldataHelperFunctions(); |
347 | | |
348 | | /// Return top-level calldata coder test function as string |
349 | | std::string testCallDataFunction(unsigned _invalidLength); |
350 | | |
351 | | /// Return top-level returndata coder test function as string |
352 | | std::string testReturnDataFunction(); |
353 | | |
354 | | /// Return the next variable count that is used for |
355 | | /// variable naming. |
356 | | unsigned getNextVarCounter() |
357 | 501 | { |
358 | 501 | return m_varCounter++; |
359 | 501 | } |
360 | | |
361 | | /// Return a pair of names for Solidity variable and the same variable when |
362 | | /// passed either as a function parameter or used to store the tuple |
363 | | /// returned from a function. |
364 | | /// @param _varCounter: name suffix |
365 | | /// @param _stateVar: predicate that is true for state variables, false otherwise |
366 | | std::pair<std::string, std::string> newVarNames(unsigned _varCounter, bool _stateVar) |
367 | 501 | { |
368 | 501 | std::string varName = _stateVar ? s_stateVarNamePrefix : s_localVarNamePrefix; |
369 | 501 | return std::make_pair( |
370 | 501 | varName + std::to_string(_varCounter), |
371 | 501 | paramName() + std::to_string(_varCounter) |
372 | 501 | ); |
373 | 501 | } |
374 | | |
375 | | std::string paramName() |
376 | 501 | { |
377 | 501 | switch (m_test) |
378 | 501 | { |
379 | 414 | case Contract_Test::Contract_Test_CALLDATA_CODER: |
380 | 414 | return s_paramNamePrefix; |
381 | 87 | case Contract_Test::Contract_Test_RETURNDATA_CODER: |
382 | 87 | return s_localVarNamePrefix; |
383 | 501 | } |
384 | 501 | } |
385 | | |
386 | | /// Checks if the last dynamically encoded Solidity type is right |
387 | | /// padded, returning true if it is and false otherwise. |
388 | | bool isLastDynParamRightPadded() |
389 | 319 | { |
390 | 319 | return m_isLastDynParamRightPadded; |
391 | 319 | } |
392 | | |
393 | | /// Convert delimiter to a comma or null string. |
394 | | static std::string delimiterToString(Delimiter _delimiter, bool _space = true); |
395 | | /// Generates number in the range [1, @param _n] uniformly at random. |
396 | | unsigned randomNumberOneToN(unsigned _n) |
397 | 319 | { |
398 | 319 | return Distribution(1, _n)(*m_random); |
399 | 319 | } |
400 | | /// Generates boolean that has a bernoulli distribution defined by @param _p. |
401 | | bool randomBool(double _p) |
402 | 2.48k | { |
403 | 2.48k | return Bernoulli{_p}(*m_random); |
404 | 2.48k | } |
405 | | |
406 | | /// Contains the test program |
407 | | std::ostringstream m_output; |
408 | | /// Contains a subset of the test program. This subset contains |
409 | | /// checks to be encoded in the test program |
410 | | std::ostringstream m_checks; |
411 | | /// Contains typed parameter list to be passed to callee functions |
412 | | std::ostringstream m_typedParamsPublic; |
413 | | /// Contains parameter list to be passed to callee functions |
414 | | std::ostringstream m_untypedParamsExternal; |
415 | | /// Contains type string to be passed to Isabelle API |
416 | | std::ostringstream m_isabelleTypeString; |
417 | | /// Contains values to be encoded in the format accepted |
418 | | /// by the Isabelle API. |
419 | | std::ostringstream m_isabelleValueString; |
420 | | /// Contains type stream to be used in returndata coder function |
421 | | /// signature |
422 | | std::ostringstream m_types; |
423 | | std::ostringstream m_typedReturn; |
424 | | /// Argument names to be passed to coder functions |
425 | | std::ostringstream m_argsCoder; |
426 | | /// Predicate that is true if we are in contract scope |
427 | | bool m_isStateVar; |
428 | | unsigned m_counter; |
429 | | unsigned m_varCounter; |
430 | | /// Monotonically increasing return value for error reporting |
431 | | unsigned m_returnValue; |
432 | | /// Flag that indicates if last dynamically encoded parameter |
433 | | /// passed to a function call is of a type that is going to be |
434 | | /// right padded by the ABI encoder. |
435 | | bool m_isLastDynParamRightPadded; |
436 | | /// Struct counter |
437 | | unsigned m_structCounter; |
438 | | unsigned m_numStructsAdded; |
439 | | /// Enum stating abiv2 coder to be tested |
440 | | Contract_Test m_test; |
441 | | /// Representation of external parameters |
442 | | std::vector<ParameterPack> m_externalParamsRep; |
443 | | /// Random number generator |
444 | | std::unique_ptr<RandomEngine> m_random; |
445 | | /// Prefixes for declared and parameterized variable names |
446 | | static auto constexpr s_localVarNamePrefix = "lv_"; |
447 | | static auto constexpr s_stateVarNamePrefix = "sv_"; |
448 | | static auto constexpr s_paramNamePrefix = "p_"; |
449 | | /// Maximum number of indirections to test calldata coding |
450 | | static unsigned constexpr s_maxIndirections = 5; |
451 | | }; |
452 | | |
453 | | /// Visitor interface for Solidity protobuf types. |
454 | | template <typename T> |
455 | | class AbiV2ProtoVisitor |
456 | | { |
457 | | public: |
458 | | static unsigned constexpr s_maxArrayDimensions = 3; |
459 | 354k | virtual ~AbiV2ProtoVisitor() = default; solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<bool>::~AbiV2ProtoVisitor() Line | Count | Source | 459 | 233k | virtual ~AbiV2ProtoVisitor() = default; |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::~AbiV2ProtoVisitor() Line | Count | Source | 459 | 120k | virtual ~AbiV2ProtoVisitor() = default; |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::~AbiV2ProtoVisitor() Line | Count | Source | 459 | 501 | virtual ~AbiV2ProtoVisitor() = default; |
|
460 | | |
461 | | virtual T visit(BoolType const& _node) = 0; |
462 | | virtual T visit(IntegerType const& _node) = 0; |
463 | | virtual T visit(FixedByteType const& _node) = 0; |
464 | | virtual T visit(AddressType const& _node) = 0; |
465 | | virtual T visit(DynamicByteArrayType const& _node) = 0; |
466 | | virtual T visit(ArrayType const& _node) = 0; |
467 | | virtual T visit(StructType const& _node) = 0; |
468 | | virtual T visit(ValueType const& _node) |
469 | 282k | { |
470 | 282k | return visitValueType(_node); |
471 | 282k | } solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::visit(solidity::test::abiv2fuzzer::ValueType const&) Line | Count | Source | 469 | 33.9k | { | 470 | 33.9k | return visitValueType(_node); | 471 | 33.9k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::visit(solidity::test::abiv2fuzzer::ValueType const&) Line | Count | Source | 469 | 47.3k | { | 470 | 47.3k | return visitValueType(_node); | 471 | 47.3k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<bool>::visit(solidity::test::abiv2fuzzer::ValueType const&) Line | Count | Source | 469 | 201k | { | 470 | 201k | return visitValueType(_node); | 471 | 201k | } |
|
472 | | virtual T visit(NonValueType const& _node) |
473 | 506k | { |
474 | 506k | return visitNonValueType(_node); |
475 | 506k | } solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::visit(solidity::test::abiv2fuzzer::NonValueType const&) Line | Count | Source | 473 | 28.4k | { | 474 | 28.4k | return visitNonValueType(_node); | 475 | 28.4k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::visit(solidity::test::abiv2fuzzer::NonValueType const&) Line | Count | Source | 473 | 38.7k | { | 474 | 38.7k | return visitNonValueType(_node); | 475 | 38.7k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<bool>::visit(solidity::test::abiv2fuzzer::NonValueType const&) Line | Count | Source | 473 | 439k | { | 474 | 439k | return visitNonValueType(_node); | 475 | 439k | } |
|
476 | | virtual T visit(Type const& _node) |
477 | 864k | { |
478 | 864k | return visitType(_node); |
479 | 864k | } solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<bool>::visit(solidity::test::abiv2fuzzer::Type const&) Line | Count | Source | 477 | 703k | { | 478 | 703k | return visitType(_node); | 479 | 703k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::visit(solidity::test::abiv2fuzzer::Type const&) Line | Count | Source | 477 | 62.4k | { | 478 | 62.4k | return visitType(_node); | 479 | 62.4k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::visit(solidity::test::abiv2fuzzer::Type const&) Line | Count | Source | 477 | 98.3k | { | 478 | 98.3k | return visitType(_node); | 479 | 98.3k | } |
|
480 | | |
481 | | enum class DataType |
482 | | { |
483 | | BYTES, |
484 | | VALUE, |
485 | | ARRAY |
486 | | }; |
487 | | |
488 | | /// Prefixes for declared and parameterized variable names |
489 | | static auto constexpr s_structNamePrefix = "S"; |
490 | | |
491 | | // Static function definitions |
492 | | static bool isValueType(DataType _dataType) |
493 | | { |
494 | | return _dataType == DataType::VALUE; |
495 | | } |
496 | | |
497 | | static unsigned getIntWidth(IntegerType const& _x) |
498 | 25.6k | { |
499 | 25.6k | return 8 * ((_x.width() % 32) + 1); |
500 | 25.6k | } |
501 | | |
502 | | static bool isIntSigned(IntegerType const& _x) |
503 | 11.1k | { |
504 | 11.1k | return _x.is_signed(); |
505 | 11.1k | } |
506 | | |
507 | | static std::string getIntTypeAsString(IntegerType const& _x) |
508 | 11.1k | { |
509 | 11.1k | return ((isIntSigned(_x) ? "int" : "uint") + std::to_string(getIntWidth(_x))); |
510 | 11.1k | } |
511 | | |
512 | | static unsigned getFixedByteWidth(FixedByteType const& _x) |
513 | 8.65k | { |
514 | 8.65k | return (_x.width() % 32) + 1; |
515 | 8.65k | } |
516 | | |
517 | | static std::string getFixedByteTypeAsString(FixedByteType const& _x) |
518 | 4.03k | { |
519 | 4.03k | return "bytes" + std::to_string(getFixedByteWidth(_x)); |
520 | 4.03k | } |
521 | | |
522 | | // Convert _counter to string and return its keccak256 hash |
523 | | static u256 hashUnsignedInt(unsigned _counter) |
524 | 21.0k | { |
525 | 21.0k | return util::keccak256(util::h256(_counter)); |
526 | 21.0k | } |
527 | | |
528 | | static u256 maskUnsignedInt(unsigned _counter, unsigned _numMaskNibbles) |
529 | 21.0k | { |
530 | 21.0k | return hashUnsignedInt(_counter) & u256("0x" + std::string(_numMaskNibbles, 'f')); |
531 | 21.0k | } |
532 | | |
533 | | // Requires caller to pass number of nibbles (twice the number of bytes) as second argument. |
534 | | // Note: Don't change HexPrefix::Add. See comment in fixedByteValueAsString(). |
535 | | static std::string maskUnsignedIntToHex(unsigned _counter, unsigned _numMaskNibbles) |
536 | 21.0k | { |
537 | 21.0k | return "0x" + toHex(maskUnsignedInt(_counter, _numMaskNibbles)); |
538 | 21.0k | } |
539 | | |
540 | | /// Dynamically sized arrays can have a length of at least zero |
541 | | /// and at most s_maxArrayLength. |
542 | | static unsigned getDynArrayLengthFromFuzz(unsigned _fuzz, unsigned _counter) |
543 | 6.65k | { |
544 | | // Increment modulo value by one in order to meet upper bound |
545 | 6.65k | return (_fuzz + _counter) % (s_maxArrayLength + 1); |
546 | 6.65k | } |
547 | | |
548 | | /// Statically sized arrays must have a length of at least one |
549 | | /// and at most s_maxArrayLength. |
550 | | static unsigned getStaticArrayLengthFromFuzz(unsigned _fuzz) |
551 | 4.22k | { |
552 | 4.22k | return _fuzz % s_maxArrayLength + 1; |
553 | 4.22k | } solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::getStaticArrayLengthFromFuzz(unsigned int) Line | Count | Source | 551 | 2.72k | { | 552 | 2.72k | return _fuzz % s_maxArrayLength + 1; | 553 | 2.72k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::getStaticArrayLengthFromFuzz(unsigned int) Line | Count | Source | 551 | 1.50k | { | 552 | 1.50k | return _fuzz % s_maxArrayLength + 1; | 553 | 1.50k | } |
|
554 | | |
555 | | /// Returns a pseudo-random value for the size of a string/hex |
556 | | /// literal. Used for creating variable length hex/string literals. |
557 | | /// @param _counter Monotonically increasing counter value |
558 | | static unsigned getVarLength(unsigned _counter) |
559 | 6.36k | { |
560 | | // Since _counter values are usually small, we use |
561 | | // this linear equation to make the number derived from |
562 | | // _counter approach a uniform distribution over |
563 | | // [0, s_maxDynArrayLength] |
564 | 6.36k | auto v = (_counter + 879) * 32 % (s_maxDynArrayLength + 1); |
565 | | /// Always return an even number because Isabelle string |
566 | | /// values are formatted as hex literals |
567 | 6.36k | if (v % 2 == 1) |
568 | 3.07k | return v + 1; |
569 | 3.29k | else |
570 | 3.29k | return v; |
571 | 6.36k | } |
572 | | protected: |
573 | | T visitValueType(ValueType const& _type) |
574 | 282k | { |
575 | 282k | switch (_type.value_type_oneof_case()) |
576 | 282k | { |
577 | 67.5k | case ValueType::kInty: |
578 | 67.5k | return visit(_type.inty()); |
579 | 34.5k | case ValueType::kByty: |
580 | 34.5k | return visit(_type.byty()); |
581 | 75.5k | case ValueType::kAdty: |
582 | 75.5k | return visit(_type.adty()); |
583 | 88.0k | case ValueType::kBoolty: |
584 | 88.0k | return visit(_type.boolty()); |
585 | 17.2k | case ValueType::VALUE_TYPE_ONEOF_NOT_SET: |
586 | 17.2k | return T(); |
587 | 282k | } |
588 | 282k | } solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::visitValueType(solidity::test::abiv2fuzzer::ValueType const&) Line | Count | Source | 574 | 33.9k | { | 575 | 33.9k | switch (_type.value_type_oneof_case()) | 576 | 33.9k | { | 577 | 11.1k | case ValueType::kInty: | 578 | 11.1k | return visit(_type.inty()); | 579 | 4.02k | case ValueType::kByty: | 580 | 4.02k | return visit(_type.byty()); | 581 | 7.79k | case ValueType::kAdty: | 582 | 7.79k | return visit(_type.adty()); | 583 | 11.0k | case ValueType::kBoolty: | 584 | 11.0k | return visit(_type.boolty()); | 585 | 0 | case ValueType::VALUE_TYPE_ONEOF_NOT_SET: | 586 | 0 | return T(); | 587 | 33.9k | } | 588 | 33.9k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::visitValueType(solidity::test::abiv2fuzzer::ValueType const&) Line | Count | Source | 574 | 47.3k | { | 575 | 47.3k | switch (_type.value_type_oneof_case()) | 576 | 47.3k | { | 577 | 14.5k | case ValueType::kInty: | 578 | 14.5k | return visit(_type.inty()); | 579 | 4.61k | case ValueType::kByty: | 580 | 4.61k | return visit(_type.byty()); | 581 | 10.0k | case ValueType::kAdty: | 582 | 10.0k | return visit(_type.adty()); | 583 | 13.9k | case ValueType::kBoolty: | 584 | 13.9k | return visit(_type.boolty()); | 585 | 4.23k | case ValueType::VALUE_TYPE_ONEOF_NOT_SET: | 586 | 4.23k | return T(); | 587 | 47.3k | } | 588 | 47.3k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<bool>::visitValueType(solidity::test::abiv2fuzzer::ValueType const&) Line | Count | Source | 574 | 201k | { | 575 | 201k | switch (_type.value_type_oneof_case()) | 576 | 201k | { | 577 | 41.9k | case ValueType::kInty: | 578 | 41.9k | return visit(_type.inty()); | 579 | 25.8k | case ValueType::kByty: | 580 | 25.8k | return visit(_type.byty()); | 581 | 57.6k | case ValueType::kAdty: | 582 | 57.6k | return visit(_type.adty()); | 583 | 63.0k | case ValueType::kBoolty: | 584 | 63.0k | return visit(_type.boolty()); | 585 | 13.0k | case ValueType::VALUE_TYPE_ONEOF_NOT_SET: | 586 | 13.0k | return T(); | 587 | 201k | } | 588 | 201k | } |
|
589 | | |
590 | | T visitNonValueType(NonValueType const& _type) |
591 | 506k | { |
592 | 506k | switch (_type.nonvalue_type_oneof_case()) |
593 | 506k | { |
594 | 46.2k | case NonValueType::kDynbytearray: |
595 | 46.2k | return visit(_type.dynbytearray()); |
596 | 46.8k | case NonValueType::kArrtype: |
597 | 46.8k | return visit(_type.arrtype()); |
598 | 397k | case NonValueType::kStype: |
599 | 397k | return visit(_type.stype()); |
600 | 15.5k | case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET: |
601 | 15.5k | return T(); |
602 | 506k | } |
603 | 506k | } solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::visitNonValueType(solidity::test::abiv2fuzzer::NonValueType const&) Line | Count | Source | 591 | 28.4k | { | 592 | 28.4k | switch (_type.nonvalue_type_oneof_case()) | 593 | 28.4k | { | 594 | 5.81k | case NonValueType::kDynbytearray: | 595 | 5.81k | return visit(_type.dynbytearray()); | 596 | 6.53k | case NonValueType::kArrtype: | 597 | 6.53k | return visit(_type.arrtype()); | 598 | 16.1k | case NonValueType::kStype: | 599 | 16.1k | return visit(_type.stype()); | 600 | 0 | case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET: | 601 | 0 | return T(); | 602 | 28.4k | } | 603 | 28.4k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::visitNonValueType(solidity::test::abiv2fuzzer::NonValueType const&) Line | Count | Source | 591 | 38.7k | { | 592 | 38.7k | switch (_type.nonvalue_type_oneof_case()) | 593 | 38.7k | { | 594 | 6.34k | case NonValueType::kDynbytearray: | 595 | 6.34k | return visit(_type.dynbytearray()); | 596 | 10.4k | case NonValueType::kArrtype: | 597 | 10.4k | return visit(_type.arrtype()); | 598 | 19.7k | case NonValueType::kStype: | 599 | 19.7k | return visit(_type.stype()); | 600 | 2.18k | case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET: | 601 | 2.18k | return T(); | 602 | 38.7k | } | 603 | 38.7k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<bool>::visitNonValueType(solidity::test::abiv2fuzzer::NonValueType const&) Line | Count | Source | 591 | 439k | { | 592 | 439k | switch (_type.nonvalue_type_oneof_case()) | 593 | 439k | { | 594 | 34.1k | case NonValueType::kDynbytearray: | 595 | 34.1k | return visit(_type.dynbytearray()); | 596 | 29.8k | case NonValueType::kArrtype: | 597 | 29.8k | return visit(_type.arrtype()); | 598 | 361k | case NonValueType::kStype: | 599 | 361k | return visit(_type.stype()); | 600 | 13.3k | case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET: | 601 | 13.3k | return T(); | 602 | 439k | } | 603 | 439k | } |
|
604 | | |
605 | | T visitType(Type const& _type) |
606 | 864k | { |
607 | 864k | switch (_type.type_oneof_case()) |
608 | 864k | { |
609 | 282k | case Type::kVtype: |
610 | 282k | return visit(_type.vtype()); |
611 | 506k | case Type::kNvtype: |
612 | 506k | return visit(_type.nvtype()); |
613 | 74.6k | case Type::TYPE_ONEOF_NOT_SET: |
614 | 74.6k | return T(); |
615 | 864k | } |
616 | 864k | } solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<bool>::visitType(solidity::test::abiv2fuzzer::Type const&) Line | Count | Source | 606 | 703k | { | 607 | 703k | switch (_type.type_oneof_case()) | 608 | 703k | { | 609 | 201k | case Type::kVtype: | 610 | 201k | return visit(_type.vtype()); | 611 | 439k | case Type::kNvtype: | 612 | 439k | return visit(_type.nvtype()); | 613 | 62.3k | case Type::TYPE_ONEOF_NOT_SET: | 614 | 62.3k | return T(); | 615 | 703k | } | 616 | 703k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::visitType(solidity::test::abiv2fuzzer::Type const&) Line | Count | Source | 606 | 62.4k | { | 607 | 62.4k | switch (_type.type_oneof_case()) | 608 | 62.4k | { | 609 | 33.9k | case Type::kVtype: | 610 | 33.9k | return visit(_type.vtype()); | 611 | 28.4k | case Type::kNvtype: | 612 | 28.4k | return visit(_type.nvtype()); | 613 | 0 | case Type::TYPE_ONEOF_NOT_SET: | 614 | 0 | return T(); | 615 | 62.4k | } | 616 | 62.4k | } |
solidity::test::abiv2fuzzer::AbiV2ProtoVisitor<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >::visitType(solidity::test::abiv2fuzzer::Type const&) Line | Count | Source | 606 | 98.3k | { | 607 | 98.3k | switch (_type.type_oneof_case()) | 608 | 98.3k | { | 609 | 47.3k | case Type::kVtype: | 610 | 47.3k | return visit(_type.vtype()); | 611 | 38.7k | case Type::kNvtype: | 612 | 38.7k | return visit(_type.nvtype()); | 613 | 12.2k | case Type::TYPE_ONEOF_NOT_SET: | 614 | 12.2k | return T(); | 615 | 98.3k | } | 616 | 98.3k | } |
|
617 | | private: |
618 | | static unsigned constexpr s_maxArrayLength = 4; |
619 | | static unsigned constexpr s_maxDynArrayLength = 256; |
620 | | }; |
621 | | |
622 | | /// Converts a protobuf type into a Solidity type string. |
623 | | class TypeVisitor: public AbiV2ProtoVisitor<std::string> |
624 | | { |
625 | | public: |
626 | | TypeVisitor(unsigned _structSuffix = 0): |
627 | | m_indentation(1), |
628 | | m_structCounter(_structSuffix), |
629 | | m_structStartCounter(_structSuffix), |
630 | | m_structFieldCounter(0), |
631 | | m_isLastDynParamRightPadded(false) |
632 | 57.7k | {} |
633 | | |
634 | | std::string visit(BoolType const&) override; |
635 | | std::string visit(IntegerType const&) override; |
636 | | std::string visit(FixedByteType const&) override; |
637 | | std::string visit(AddressType const&) override; |
638 | | std::string visit(ArrayType const&) override; |
639 | | std::string visit(DynamicByteArrayType const&) override; |
640 | | std::string visit(StructType const&) override; |
641 | | using AbiV2ProtoVisitor<std::string>::visit; |
642 | | std::string baseType() |
643 | 0 | { |
644 | 0 | return m_baseType; |
645 | 0 | } |
646 | | bool isLastDynParamRightPadded() |
647 | 501 | { |
648 | 501 | return m_isLastDynParamRightPadded; |
649 | 501 | } |
650 | | std::string structDef() |
651 | 48.2k | { |
652 | 48.2k | return m_structDef.str(); |
653 | 48.2k | } |
654 | | unsigned numStructs() |
655 | 48.2k | { |
656 | 48.2k | return m_structCounter - m_structStartCounter; |
657 | 48.2k | } |
658 | | static bool arrayOfStruct(ArrayType const& _type) |
659 | 1.63k | { |
660 | 1.63k | Type const& baseType = _type.t(); |
661 | 1.63k | if (baseType.has_nvtype() && baseType.nvtype().has_stype()) |
662 | 903 | return true; |
663 | 731 | else if (baseType.has_nvtype() && baseType.nvtype().has_arrtype()) |
664 | 274 | return arrayOfStruct(baseType.nvtype().arrtype()); |
665 | 457 | else |
666 | 457 | return false; |
667 | 1.63k | } |
668 | | std::string isabelleTypeString() |
669 | 48.2k | { |
670 | 48.2k | return m_structTupleString.stream.str(); |
671 | 48.2k | } |
672 | | private: |
673 | | struct StructTupleString |
674 | | { |
675 | 57.7k | StructTupleString() = default; |
676 | | unsigned index = 0; |
677 | | std::ostringstream stream; |
678 | | void start() |
679 | 16.5k | { |
680 | 16.5k | stream << "("; |
681 | 16.5k | } |
682 | | void end() |
683 | 16.5k | { |
684 | 16.5k | stream << ")"; |
685 | 16.5k | } |
686 | | void addTypeStringToTuple(std::string& _typeString); |
687 | | void addArrayBracketToType(std::string& _arrayBracket); |
688 | | }; |
689 | | void structDefinition(StructType const&); |
690 | | |
691 | | std::string indentation() |
692 | 80.7k | { |
693 | 80.7k | return std::string(m_indentation * 1, '\t'); |
694 | 80.7k | } |
695 | | std::string lineString(std::string const& _line) |
696 | 80.7k | { |
697 | 80.7k | return indentation() + _line + "\n"; |
698 | 80.7k | } |
699 | | std::string m_baseType; |
700 | | std::ostringstream m_structDef; |
701 | | /// Utility type for conveniently composing a tuple |
702 | | /// string for struct types. |
703 | | StructTupleString m_structTupleString; |
704 | | unsigned m_indentation; |
705 | | unsigned m_structCounter; |
706 | | unsigned m_structStartCounter; |
707 | | unsigned m_structFieldCounter; |
708 | | bool m_isLastDynParamRightPadded; |
709 | | |
710 | | static auto constexpr s_structTypeName = "S"; |
711 | | }; |
712 | | |
713 | | /// Returns a pair of strings, first of which contains assignment statements |
714 | | /// to initialize a given type, and second of which contains checks to be |
715 | | /// placed inside the coder function to test abi en/decoding. |
716 | | class AssignCheckVisitor: public AbiV2ProtoVisitor<std::pair<std::string, std::string>> |
717 | | { |
718 | | public: |
719 | | AssignCheckVisitor( |
720 | | std::string _varName, |
721 | | std::string _paramName, |
722 | | unsigned _errorStart, |
723 | | bool _stateVar, |
724 | | unsigned _counter, |
725 | | unsigned _structCounter |
726 | | ) |
727 | 501 | { |
728 | 501 | m_counter = m_counterStart = _counter; |
729 | 501 | m_varName = _varName; |
730 | 501 | m_paramName = _paramName; |
731 | 501 | m_errorCode = m_errorStart = _errorStart; |
732 | 501 | m_indentation = 2; |
733 | 501 | m_stateVar = _stateVar; |
734 | 501 | m_structCounter = m_structStart = _structCounter; |
735 | 501 | } |
736 | | std::pair<std::string, std::string> visit(BoolType const&) override; |
737 | | std::pair<std::string, std::string> visit(IntegerType const&) override; |
738 | | std::pair<std::string, std::string> visit(FixedByteType const&) override; |
739 | | std::pair<std::string, std::string> visit(AddressType const&) override; |
740 | | std::pair<std::string, std::string> visit(ArrayType const&) override; |
741 | | std::pair<std::string, std::string> visit(DynamicByteArrayType const&) override; |
742 | | std::pair<std::string, std::string> visit(StructType const&) override; |
743 | | using AbiV2ProtoVisitor<std::pair<std::string, std::string>>::visit; |
744 | | |
745 | | unsigned errorStmts() |
746 | 501 | { |
747 | 501 | return m_errorCode - m_errorStart; |
748 | 501 | } |
749 | | |
750 | | unsigned counted() |
751 | 501 | { |
752 | 501 | return m_counter - m_counterStart; |
753 | 501 | } |
754 | | |
755 | | unsigned structs() |
756 | 0 | { |
757 | 0 | return m_structCounter - m_structStart; |
758 | 0 | } |
759 | | |
760 | | std::string isabelleValueString() |
761 | 501 | { |
762 | 501 | return m_valueStream.stream.str(); |
763 | 501 | } |
764 | | private: |
765 | | struct ValueStream |
766 | | { |
767 | 501 | ValueStream() = default; |
768 | | unsigned index = 0; |
769 | | std::ostringstream stream; |
770 | | void startStruct() |
771 | 13.7k | { |
772 | 13.7k | if (index >= 1) |
773 | 6.29k | stream << ","; |
774 | 13.7k | index = 0; |
775 | 13.7k | stream << "("; |
776 | 13.7k | } |
777 | | void endStruct() |
778 | 13.7k | { |
779 | 13.7k | stream << ")"; |
780 | 13.7k | } |
781 | | void startArray() |
782 | 5.89k | { |
783 | 5.89k | if (index >= 1) |
784 | 4.54k | stream << ","; |
785 | 5.89k | index = 0; |
786 | 5.89k | stream << "["; |
787 | 5.89k | } |
788 | | void endArray() |
789 | 5.89k | { |
790 | 5.89k | stream << "]"; |
791 | 5.89k | index++; |
792 | 5.89k | } |
793 | | void appendValue(std::string& _value); |
794 | | }; |
795 | | std::string indentation() |
796 | 113k | { |
797 | 113k | return std::string(m_indentation * 1, '\t'); |
798 | 113k | } |
799 | | unsigned counter() |
800 | 56.1k | { |
801 | 56.1k | return m_counter++; |
802 | 56.1k | } |
803 | | |
804 | | std::pair<std::string, std::string> assignAndCheckStringPair( |
805 | | std::string const& _varRef, |
806 | | std::string const& _checkRef, |
807 | | std::string const& _assignValue, |
808 | | std::string const& _checkValue, |
809 | | DataType _type |
810 | | ); |
811 | | std::string assignString(std::string const&, std::string const&); |
812 | | std::string checkString(std::string const&, std::string const&, DataType); |
813 | | unsigned m_counter; |
814 | | unsigned m_counterStart; |
815 | | std::string m_varName; |
816 | | std::string m_paramName; |
817 | | unsigned m_errorCode; |
818 | | unsigned m_errorStart; |
819 | | unsigned m_indentation; |
820 | | bool m_stateVar; |
821 | | unsigned m_structCounter; |
822 | | unsigned m_structStart; |
823 | | ValueStream m_valueStream; |
824 | | bool m_forcedVisit = false; |
825 | | }; |
826 | | |
827 | | /// Returns a valid value (as a string) for a given type. |
828 | | class ValueGetterVisitor: AbiV2ProtoVisitor<std::string> |
829 | | { |
830 | | public: |
831 | 63.1k | ValueGetterVisitor(unsigned _counter = 0): m_counter(_counter) {} |
832 | | |
833 | | std::string visit(BoolType const&) override; |
834 | | std::string visit(IntegerType const&) override; |
835 | | std::string visit(FixedByteType const&) override; |
836 | | std::string visit(AddressType const&) override; |
837 | | std::string visit(DynamicByteArrayType const&) override; |
838 | | std::string visit(ArrayType const&) override |
839 | 0 | { |
840 | 0 | solAssert(false, "ABIv2 proto fuzzer: Cannot call valuegettervisitor on complex type"); |
841 | 0 | } |
842 | | std::string visit(StructType const&) override |
843 | 0 | { |
844 | 0 | solAssert(false, "ABIv2 proto fuzzer: Cannot call valuegettervisitor on complex type"); |
845 | 0 | } |
846 | | using AbiV2ProtoVisitor<std::string>::visit; |
847 | | static std::string isabelleAddressValueAsString(std::string& _solAddressString); |
848 | | static std::string isabelleBytesValueAsString(std::string& _solFixedBytesString); |
849 | | private: |
850 | | unsigned counter() |
851 | 49.5k | { |
852 | 49.5k | return m_counter++; |
853 | 49.5k | } |
854 | | |
855 | | static std::string addressValueAsString(unsigned _counter); |
856 | | static std::string fixedByteValueAsString(unsigned _width, unsigned _counter); |
857 | | |
858 | | /// Returns a hex literal if _isHexLiteral is true, a string literal otherwise. |
859 | | /// The size of the returned literal is _numBytes bytes. |
860 | | /// @param _decorate If true, the returned string is enclosed within double quotes |
861 | | /// if _isHexLiteral is false. |
862 | | /// @param _isHexLiteral If true, the returned string is enclosed within |
863 | | /// double quotes prefixed by the string "hex" if _decorate is true. If |
864 | | /// _decorate is false, the returned string is returned as-is. |
865 | | /// @return hex value as string |
866 | | static std::string hexValueAsString( |
867 | | unsigned _numBytes, |
868 | | unsigned _counter, |
869 | | bool _isHexLiteral, |
870 | | bool _decorate = true |
871 | | ); |
872 | | |
873 | | /// Returns a hex/string literal of variable length whose value and |
874 | | /// size are pseudo-randomly determined from the counter value. |
875 | | /// @param _counter A monotonically increasing counter value |
876 | | /// @param _isHexLiteral Flag that indicates whether hex (if true) or |
877 | | /// string literal (false) is desired |
878 | | /// @return A variable length hex/string value |
879 | | static std::string bytesArrayValueAsString(unsigned _counter, bool _isHexLiteral); |
880 | | |
881 | | /// Concatenates the hash value obtained from monotonically increasing counter |
882 | | /// until the desired number of bytes determined by _numBytes. |
883 | | /// @param _width Desired number of bytes for hex value |
884 | | /// @param _counter A counter value used for creating a keccak256 hash |
885 | | /// @param _isHexLiteral Since this routine may be used to construct |
886 | | /// string or hex literals, this flag is used to construct a valid output. |
887 | | /// @return Valid hex or string literal of size _width bytes |
888 | | static std::string variableLengthValueAsString( |
889 | | unsigned _width, |
890 | | unsigned _counter, |
891 | | bool _isHexLiteral |
892 | | ); |
893 | | |
894 | | /// Returns a value that is @a _numBytes bytes long. |
895 | | /// @param _numBytes: Number of bytes of desired value |
896 | | /// @param _counter: A counter value |
897 | | /// @param _isHexLiteral: True if desired value is a hex literal, false otherwise |
898 | | static std::string croppedString(unsigned _numBytes, unsigned _counter, bool _isHexLiteral); |
899 | | |
900 | | unsigned m_counter; |
901 | | }; |
902 | | |
903 | | /// Returns true if protobuf array specification is well-formed, false otherwise |
904 | | class ValidityVisitor: AbiV2ProtoVisitor<bool> |
905 | | { |
906 | | public: |
907 | 209k | ValidityVisitor(): m_arrayDimensions(0) {} |
908 | | |
909 | | bool visit(BoolType const&) override |
910 | 50.2k | { |
911 | 50.2k | return true; |
912 | 50.2k | } |
913 | | |
914 | | bool visit(IntegerType const&) override |
915 | 34.3k | { |
916 | 34.3k | return true; |
917 | 34.3k | } |
918 | | |
919 | | bool visit(FixedByteType const&) override |
920 | 21.9k | { |
921 | 21.9k | return true; |
922 | 21.9k | } |
923 | | |
924 | | bool visit(AddressType const&) override |
925 | 49.2k | { |
926 | 49.2k | return true; |
927 | 49.2k | } |
928 | | |
929 | | bool visit(DynamicByteArrayType const&) override |
930 | 29.1k | { |
931 | 29.1k | return true; |
932 | 29.1k | } |
933 | | |
934 | | bool visit(ArrayType const& _type) override |
935 | 62.2k | { |
936 | | // Mark array type as invalid in one of the following is true |
937 | | // - contains more than s_maxArrayDimensions dimensions |
938 | | // - contains an invalid base type, which happens in the |
939 | | // following cases |
940 | | // - array base type is invalid |
941 | | // - array base type is empty |
942 | 62.2k | m_arrayDimensions++; |
943 | 62.2k | if (m_arrayDimensions > s_maxArrayDimensions) |
944 | 398 | return false; |
945 | 61.8k | return visit(_type.t()); |
946 | 62.2k | } |
947 | | |
948 | | bool visit(StructType const& _type) override |
949 | 432k | { |
950 | | // A struct is marked invalid only if all of its fields |
951 | | // are invalid. This is done to prevent an empty struct |
952 | | // being defined (which is a Solidity error). |
953 | 432k | for (auto const& t: _type.t()) |
954 | 481k | if (visit(t)) |
955 | 415k | return true; |
956 | 16.2k | return false; |
957 | 432k | } |
958 | | |
959 | | unsigned m_arrayDimensions; |
960 | | using AbiV2ProtoVisitor<bool>::visit; |
961 | | }; |
962 | | |
963 | | /// Returns true if visited type is dynamically encoded by the abi coder, |
964 | | /// false otherwise. |
965 | | class DynParamVisitor: AbiV2ProtoVisitor<bool> |
966 | | { |
967 | | public: |
968 | 23.6k | DynParamVisitor() = default; |
969 | | |
970 | | bool visit(BoolType const&) override |
971 | 12.7k | { |
972 | 12.7k | return false; |
973 | 12.7k | } |
974 | | |
975 | | bool visit(IntegerType const&) override |
976 | 7.59k | { |
977 | 7.59k | return false; |
978 | 7.59k | } |
979 | | |
980 | | bool visit(FixedByteType const&) override |
981 | 3.89k | { |
982 | 3.89k | return false; |
983 | 3.89k | } |
984 | | |
985 | | bool visit(AddressType const&) override |
986 | 8.38k | { |
987 | 8.38k | return false; |
988 | 8.38k | } |
989 | | |
990 | | bool visit(DynamicByteArrayType const&) override |
991 | 4.98k | { |
992 | 4.98k | return true; |
993 | 4.98k | } |
994 | | |
995 | | bool visit(ArrayType const& _type) override |
996 | 11.6k | { |
997 | | // Return early if array spec is not well-formed |
998 | 11.6k | if (!ValidityVisitor().visit(_type)) |
999 | 1.89k | return false; |
1000 | | |
1001 | | // Array is dynamically encoded if it at least one of the following is true |
1002 | | // - at least one dimension is dynamically sized |
1003 | | // - base type is dynamically encoded |
1004 | 9.80k | if (!_type.is_static()) |
1005 | 7.10k | return true; |
1006 | 2.69k | else |
1007 | 2.69k | return visit(_type.t()); |
1008 | 9.80k | } |
1009 | | |
1010 | | bool visit(StructType const& _type) override |
1011 | 53.1k | { |
1012 | | // Return early if empty struct |
1013 | 53.1k | if (!ValidityVisitor().visit(_type)) |
1014 | 1.51k | return false; |
1015 | | |
1016 | | // Struct is dynamically encoded if at least one of its fields |
1017 | | // is dynamically encoded. |
1018 | 51.6k | for (auto const& t: _type.t()) |
1019 | 91.2k | if (visit(t)) |
1020 | 13.3k | return true; |
1021 | 38.2k | return false; |
1022 | 51.6k | } |
1023 | | |
1024 | | using AbiV2ProtoVisitor<bool>::visit; |
1025 | | }; |
1026 | | |
1027 | | } |