diff --git a/test/tools/ossfuzz/protoToSol.cpp b/test/tools/ossfuzz/protoToSol.cpp
new file mode 100644
index 000000000..6d9eaa98c
--- /dev/null
+++ b/test/tools/ossfuzz/protoToSol.cpp
@@ -0,0 +1,227 @@
+/*
+ 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 .
+*/
+
+#include
+
+#include
+
+#include
+
+#include
+
+using namespace solidity::test::solprotofuzzer;
+using namespace solidity::util;
+using namespace std;
+
+string ProtoConverter::protoToSolidity(Program const& _p)
+{
+ // Create random number generator with fuzzer supplied
+ // seed.
+ m_randomGen = make_shared(_p.seed());
+ return visit(_p);
+}
+
+pair ProtoConverter::generateTestCase(TestContract const& _testContract)
+{
+ ostringstream testCode;
+ string usingLibDecl;
+ switch (_testContract.type())
+ {
+ case TestContract::LIBRARY:
+ {
+ m_libraryTest = true;
+ auto testTuple = pseudoRandomLibraryTest();
+ m_libraryName = get<0>(testTuple);
+ usingLibDecl = Whiskers(R"(
+ using for uint;)")
+ ("libraryName", get<0>(testTuple))
+ .render();
+ testCode << Whiskers(R"(
+ uint x;
+ if (x.() != )
+ return 1;)")
+ ("testFunction", get<1>(testTuple))
+ ("expectedOutput", get<2>(testTuple))
+ .render();
+ break;
+ }
+ case TestContract::CONTRACT:
+ {
+ unsigned errorCode = 1;
+ unsigned contractVarIndex = 0;
+ for (auto& testTuple: m_contractTests)
+ {
+ // Do this to avoid stack too deep errors
+ // We require uint as a return var, so we
+ // cannot have more than 16 variables without
+ // running into stack too deep errors
+ if (contractVarIndex >= s_maxVars)
+ break;
+ string contractName = testTuple.first;
+ string contractVarName = "tc" + to_string(contractVarIndex);
+ testCode << Whiskers(R"(
+ = new ();)")
+ ("contractName", contractName)
+ ("contractVarName", contractVarName)
+ .render();
+ for (auto& t: testTuple.second)
+ {
+ testCode << Whiskers(R"(
+ if (.() != )
+ return ;)")
+ ("contractVarName", contractVarName)
+ ("testFunction", t.first)
+ ("expectedOutput", t.second)
+ ("errorCode", to_string(errorCode))
+ .render();
+ errorCode++;
+ }
+ contractVarIndex++;
+ }
+ break;
+ }
+ }
+ // Expected return value when all tests pass
+ testCode << Whiskers(R"(
+ return 0;)")
+ .render();
+ return pair(usingLibDecl, testCode.str());
+}
+
+string ProtoConverter::visit(TestContract const& _testContract)
+{
+ string testCode;
+ string usingLibDecl;
+ m_libraryTest = false;
+
+ // Simply return valid uint (zero) if there are
+ // no tests.
+ if (emptyLibraryTests() || emptyContractTests())
+ testCode = Whiskers(R"(
+ return 0;)")
+ .render();
+ else
+ tie(usingLibDecl, testCode) = generateTestCase(_testContract);
+
+ return Whiskers(R"(
+contract C {
+ function test() public returns (uint)
+ {
+ }
+}
+)")
+ ("isLibrary", m_libraryTest)
+ ("usingDecl", usingLibDecl)
+ ("testCode", testCode)
+ .render();
+}
+
+bool ProtoConverter::libraryTest() const
+{
+ return m_libraryTest;
+}
+
+string ProtoConverter::libraryName() const
+{
+ return m_libraryName;
+}
+
+string ProtoConverter::visit(Program const& _p)
+{
+ ostringstream program;
+ ostringstream contracts;
+
+ for (auto &contract: _p.contracts())
+ contracts << visit(contract);
+
+ program << Whiskers(R"(
+pragma solidity >=0.0;
+
+
+
+
+)")
+ ("contracts", contracts.str())
+ ("testContract", visit(_p.test()))
+ .render();
+ return program.str();
+}
+
+string ProtoConverter::visit(ContractType const& _contractType)
+{
+ switch (_contractType.contract_type_oneof_case())
+ {
+ case ContractType::kC:
+ return visit(_contractType.c());
+ case ContractType::kL:
+ return visit(_contractType.l());
+ case ContractType::kI:
+ return visit(_contractType.i());
+ case ContractType::CONTRACT_TYPE_ONEOF_NOT_SET:
+ return "";
+ }
+}
+
+string ProtoConverter::visit(Contract const& _contract)
+{
+ openProgramScope(&_contract);
+ return "";
+}
+
+string ProtoConverter::visit(Interface const& _interface)
+{
+ openProgramScope(&_interface);
+ return "";
+}
+
+string ProtoConverter::visit(Library const& _library)
+{
+ openProgramScope(&_library);
+ return "";
+}
+
+tuple ProtoConverter::pseudoRandomLibraryTest()
+{
+ solAssert(m_libraryTests.size() > 0, "Sol proto fuzzer: No library tests found");
+ unsigned index = randomNumber() % m_libraryTests.size();
+ return m_libraryTests[index];
+}
+
+void ProtoConverter::openProgramScope(CIL _program)
+{
+ string programNamePrefix;
+ if (holds_alternative(_program))
+ programNamePrefix = "C";
+ else if (holds_alternative(_program))
+ programNamePrefix = "I";
+ else
+ programNamePrefix = "L";
+ string programName = programNamePrefix + to_string(m_programNumericSuffix++);
+ m_programNameMap.insert(pair(_program, programName));
+}
+
+string ProtoConverter::programName(CIL _program)
+{
+ solAssert(m_programNameMap.count(_program), "Sol proto fuzzer: Unregistered program");
+ return m_programNameMap[_program];
+}
+
+unsigned ProtoConverter::randomNumber()
+{
+ solAssert(m_randomGen, "Sol proto fuzzer: Uninitialized random number generator");
+ return m_randomGen->operator()();
+}
\ No newline at end of file
diff --git a/test/tools/ossfuzz/protoToSol.h b/test/tools/ossfuzz/protoToSol.h
new file mode 100644
index 000000000..09c38ef49
--- /dev/null
+++ b/test/tools/ossfuzz/protoToSol.h
@@ -0,0 +1,185 @@
+/*
+ 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 .
+*/
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include
+
+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 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;
+ /// 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 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 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.size() == 0;
+ }
+ /// @returns true if there are no valid contract test cases, false
+ /// otherwise.
+ bool emptyContractTests()
+ {
+ return m_contractTests.size() == 0;
+ }
+ /// 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 m_randomGen;
+ /// Maps const pointer to protobuf program to its string name
+ std::map m_programNameMap;
+ /// List of tuples containing library name, function and its expected output
+ std::vector> m_libraryTests;
+ /// Maps contract name to a map of function names and their expected output
+ std::map> 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;
+};
+}
\ No newline at end of file
diff --git a/test/tools/ossfuzz/solProto.proto b/test/tools/ossfuzz/solProto.proto
new file mode 100644
index 000000000..df09b20ed
--- /dev/null
+++ b/test/tools/ossfuzz/solProto.proto
@@ -0,0 +1,108 @@
+/*
+ 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 .
+*/
+
+syntax = "proto2";
+
+message InterfaceFunction {
+ enum StateMutability {
+ PURE = 0;
+ VIEW = 1;
+ PAYABLE = 2;
+ }
+ required StateMutability mut = 1;
+}
+
+message LibraryFunction {
+ // Library functions cannot be payable
+ enum StateMutability {
+ PURE = 0;
+ VIEW = 1;
+ }
+ enum Visibility {
+ PUBLIC = 0;
+ EXTERNAL = 1;
+ INTERNAL = 2;
+ PRIVATE = 3;
+ }
+ required Visibility vis = 1;
+ required StateMutability mut = 2;
+}
+
+message ContractFunction {
+ enum StateMutability {
+ PURE = 0;
+ VIEW = 1;
+ PAYABLE = 2;
+ }
+ enum Visibility {
+ PUBLIC = 0;
+ EXTERNAL = 1;
+ INTERNAL = 2;
+ PRIVATE = 3;
+ }
+ required Visibility vis = 1;
+ required StateMutability mut = 2;
+ required bool virtualfunc = 3;
+}
+
+message Library {
+ repeated LibraryFunction funcdef = 1;
+}
+
+message Interface {
+ repeated InterfaceFunction funcdef = 1;
+ repeated Interface bases = 2;
+}
+
+message Contract {
+ repeated ContractFunction funcdef = 1;
+ required bool abstract = 2;
+ repeated ContractOrInterface bases = 3;
+}
+
+message ContractOrInterface {
+ oneof contract_or_interface_oneof {
+ Contract c = 1;
+ Interface i = 2;
+ }
+}
+
+message ContractType {
+ oneof contract_type_oneof {
+ Contract c = 1;
+ Library l = 2;
+ Interface i = 3;
+ }
+}
+
+message TestContract {
+ enum Type {
+ LIBRARY = 0;
+ CONTRACT = 1;
+ }
+ required Type type = 1;
+}
+
+message Program {
+ repeated ContractType contracts = 1;
+ required TestContract test = 2;
+ // Seed is an unsigned integer that initializes
+ // a pseudo random number generator.
+ required uint64 seed = 3;
+}
+
+package solidity.test.solprotofuzzer;
\ No newline at end of file