/src/solidity/test/tools/ossfuzz/protoToSol.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | #pragma once |
19 | | |
20 | | #include <test/tools/ossfuzz/solProto.pb.h> |
21 | | |
22 | | #include <random> |
23 | | #include <string> |
24 | | #include <utility> |
25 | | #include <variant> |
26 | | |
27 | | namespace solidity::test::solprotofuzzer |
28 | | { |
29 | | /// Random number generator that is seeded with a fuzzer |
30 | | /// supplied unsigned integer. |
31 | | struct SolRandomNumGenerator |
32 | | { |
33 | | using RandomEngine = std::minstd_rand; |
34 | | |
35 | 273 | explicit SolRandomNumGenerator(unsigned _seed): m_random(RandomEngine(_seed)) {} |
36 | | |
37 | | /// @returns a pseudo random unsigned integer |
38 | | unsigned operator()() |
39 | 0 | { |
40 | 0 | return static_cast<unsigned>(m_random()); |
41 | 0 | } |
42 | | |
43 | | RandomEngine m_random; |
44 | | }; |
45 | | |
46 | | /* There are two types of tests created by the converter: |
47 | | * - library test |
48 | | * - contract test |
49 | | * |
50 | | * The template for library test is the following: |
51 | | * |
52 | | * // Library generated from fuzzer protobuf specification |
53 | | * library L0 { |
54 | | * function f0(uint) public view returns (uint) { |
55 | | * return 31337; |
56 | | * } |
57 | | * function f1(uint) public pure returns (uint) { |
58 | | * return 455; |
59 | | * } |
60 | | * } |
61 | | * library L1 { |
62 | | * function f0(uint) external view returns (uint) { |
63 | | * return 607; |
64 | | * } |
65 | | * } |
66 | | * |
67 | | * // Test entry point |
68 | | * contract C { |
69 | | * // Uses a single pseudo randomly chosen library |
70 | | * // and calls a pseudo randomly chosen function |
71 | | * // returning a non-zero error code on failure or |
72 | | * // a zero uint when test passes. |
73 | | * using L0 for uint; |
74 | | * function test() public pure returns (uint) { |
75 | | * uint x; |
76 | | * if (x.f1() != 455) |
77 | | * return 1; |
78 | | * return 0; |
79 | | * } |
80 | | * } |
81 | | * |
82 | | * The template for contract test is the following |
83 | | * // Contracts generated from fuzzer protobuf specification |
84 | | * contract C0B { |
85 | | * function f0() public pure virtual returns (uint) |
86 | | * { |
87 | | * return 42; |
88 | | * } |
89 | | * } |
90 | | * contract C0 is C0B { |
91 | | * function f0() public pure override returns (uint) |
92 | | * { |
93 | | * return 1337; |
94 | | * } |
95 | | * } |
96 | | * |
97 | | * // Test entry point |
98 | | * contract C { |
99 | | * // Invokes one or more contract functions returning |
100 | | * // a non-zero error code for failure, a zero uint |
101 | | * // when all tests pass |
102 | | * function test() public pure returns (uint) |
103 | | * { |
104 | | * C0 tc0 = new C0(); |
105 | | * if (tc0.f0() != 1337) |
106 | | * return 1; |
107 | | * C0B tc1 = new C0B(); |
108 | | * if (tc1.f0() != 42) |
109 | | * return 2; |
110 | | * // Expected return value if all tests pass |
111 | | * return 0; |
112 | | * } |
113 | | * } |
114 | | */ |
115 | | class ProtoConverter |
116 | | { |
117 | | public: |
118 | 273 | ProtoConverter() {} |
119 | | ProtoConverter(ProtoConverter const&) = delete; |
120 | | ProtoConverter(ProtoConverter&&) = delete; |
121 | | std::string protoToSolidity(Program const&); |
122 | | /// @returns true if test calls a library function, false |
123 | | /// otherwise |
124 | | bool libraryTest() const; |
125 | | /// @returns name of the library under test |
126 | | std::string libraryName() const; |
127 | | private: |
128 | | /// Variant type that points to one of contract, interface, library protobuf messages |
129 | | using CIL = std::variant<Contract const*, Interface const*, Library const*>; |
130 | | /// Protobuf message visitors that accept a const reference to a protobuf message |
131 | | /// type and return its solidity translation. |
132 | | std::string visit(Program const&); |
133 | | std::string visit(TestContract const&); |
134 | | std::string visit(ContractType const&); |
135 | | std::string visit(Interface const& _interface); |
136 | | std::string visit(Library const& _library); |
137 | | std::string visit(Contract const& _contract); |
138 | | /// @returns a string pair containing a library declaration (relevant for library |
139 | | /// tests only) and a solidity test case |
140 | | std::pair<std::string, std::string> generateTestCase(TestContract const& _testContract); |
141 | | /// @returns name of a program i.e., contract, library or interface |
142 | | std::string programName(CIL _program); |
143 | | /// @returns a tuple containing the names of the library and function under |
144 | | /// test, and its expected output. |
145 | | std::tuple<std::string, std::string, std::string> pseudoRandomLibraryTest(); |
146 | | /// Performs bookkeeping for a fuzzer-supplied program |
147 | | void openProgramScope(CIL _program); |
148 | | /// @returns a deterministic pseudo random unsigned integer |
149 | | unsigned randomNumber(); |
150 | | /// @returns true if fuzzer supplied Library protobuf message |
151 | | /// contains zero functions, false otherwise. |
152 | | static bool emptyLibrary(Library const& _library) |
153 | 0 | { |
154 | 0 | return _library.funcdef_size() == 0; |
155 | 0 | } |
156 | | /// @returns true if there are no valid library test cases, false |
157 | | /// otherwise. |
158 | | bool emptyLibraryTests() |
159 | 273 | { |
160 | 273 | return m_libraryTests.empty(); |
161 | 273 | } |
162 | | /// @returns true if there are no valid contract test cases, false |
163 | | /// otherwise. |
164 | | bool emptyContractTests() |
165 | 0 | { |
166 | 0 | return m_contractTests.empty(); |
167 | 0 | } |
168 | | /// Numeric suffix that is part of program names e.g., "0" in "C0" |
169 | | unsigned m_programNumericSuffix = 0; |
170 | | /// Flag that states whether library call is tested (true) or not (false). |
171 | | bool m_libraryTest = false; |
172 | | /// A smart pointer to fuzzer driven random number generator |
173 | | std::shared_ptr<SolRandomNumGenerator> m_randomGen; |
174 | | /// Maps protobuf program to its string name |
175 | | std::map<CIL, std::string> m_programNameMap; |
176 | | /// List of tuples containing library name, function and its expected output |
177 | | std::vector<std::tuple<std::string, std::string, std::string>> m_libraryTests; |
178 | | /// Maps contract name to a map of function names and their expected output |
179 | | std::map<std::string, std::map<std::string, std::string>> m_contractTests; |
180 | | /// Name of the library under test, relevant if m_libraryTest is set |
181 | | std::string m_libraryName; |
182 | | /// Maximum number of local variables in test function to avoid stack too deep |
183 | | /// errors |
184 | | static unsigned constexpr s_maxVars = 15; |
185 | | }; |
186 | | } |