mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			187 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 	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;
 | |
| };
 | |
| }
 |