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