diff --git a/.circleci/config.yml b/.circleci/config.yml
index 5ef9ea07e..49f78fa19 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -50,6 +50,7 @@ defaults:
cd build
protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz
protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz
+ protoc --proto_path=../test/tools/ossfuzz solProto.proto --cpp_out=../test/tools/ossfuzz
cmake .. -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} $CMAKE_OPTIONS
make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j4
@@ -97,6 +98,7 @@ defaults:
- test/tools/ossfuzz/strictasm_opt_ossfuzz
- test/tools/ossfuzz/yul_proto_diff_ossfuzz
- test/tools/ossfuzz/yul_proto_ossfuzz
+ - test/tools/ossfuzz/sol_proto_ossfuzz
# test result output directory
- artifacts_test_results: &artifacts_test_results
diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt
index 83543b9f0..ccd71a983 100644
--- a/test/tools/ossfuzz/CMakeLists.txt
+++ b/test/tools/ossfuzz/CMakeLists.txt
@@ -10,7 +10,7 @@ add_dependencies(ossfuzz
if (OSSFUZZ)
add_custom_target(ossfuzz_proto)
- add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz)
+ add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz sol_proto_ossfuzz)
add_custom_target(ossfuzz_abiv2)
add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz)
@@ -78,6 +78,25 @@ if (OSSFUZZ)
protobuf.a
)
set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
+
+ add_executable(sol_proto_ossfuzz
+ solProtoFuzzer.cpp
+ protoToSol.cpp
+ solProto.pb.cc
+ abiV2FuzzerCommon.cpp
+ ../../EVMHost.cpp
+ )
+ target_include_directories(sol_proto_ossfuzz PRIVATE
+ /usr/include/libprotobuf-mutator
+ )
+ target_link_libraries(sol_proto_ossfuzz PRIVATE solidity libsolc
+ evmc
+ evmone-standalone
+ protobuf-mutator-libfuzzer.a
+ protobuf-mutator.a
+ protobuf.a
+ )
+ set_target_properties(sol_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
else()
add_library(solc_opt_ossfuzz
solc_opt_ossfuzz.cpp
diff --git a/test/tools/ossfuzz/protoToSol.cpp b/test/tools/ossfuzz/protoToSol.cpp
new file mode 100644
index 000000000..aefbb41b9
--- /dev/null
+++ b/test/tools/ossfuzz/protoToSol.cpp
@@ -0,0 +1,228 @@
+/*
+ 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);
+ Whiskers u(R"(using for uint;)");
+ u("ind", "\t");
+ u("libraryName", get<0>(testTuple));
+ usingLibDecl = u.render();
+ Whiskers test(R"()");
+ test("endl", "\n");
+ test("ind", "\t\t");
+ test("varDecl", "uint x;");
+ Whiskers ifStmt(R"(if ()return 1;)");
+ Whiskers ifCond(R"(x.() != )");
+ ifCond("testFunction", get<1>(testTuple));
+ ifCond("expectedOutput", get<2>(testTuple));
+ ifStmt("cond", ifCond.render());
+ ifStmt("endl", "\n");
+ ifStmt("ind", "\t\t\t");
+ test("ifStmt", ifStmt.render());
+ break;
+ }
+ case TestContract::CONTRACT:
+ {
+ unsigned errorCode = 1;
+ unsigned contractVarIndex = 0;
+ for (auto const& 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);
+ Whiskers init(R"( = new ();)");
+ init("endl", "\n");
+ init("ind", "\t\t");
+ init("contractName", contractName);
+ init("contractVarName", contractVarName);
+ testCode << init.render();
+ for (auto const& t: testTuple.second)
+ {
+ Whiskers tc(R"()");
+ tc("endl", "\n");
+ tc("ind", "\t\t");
+ Whiskers ifStmt(R"(if ()return ;)");
+ Whiskers ifCond(R"(.() != )");
+ ifCond("contractVarName", contractVarName);
+ ifCond("testFunction", t.first);
+ ifCond("expectedOutput", t.second);
+ ifStmt("endl", "\n");
+ ifStmt("cond", ifCond.render());
+ ifStmt("ind", "\t\t\t");
+ ifStmt("errorCode", to_string(errorCode));
+ tc("ifStmt", ifStmt.render());
+ testCode << tc.render();
+ errorCode++;
+ }
+ contractVarIndex++;
+ }
+ break;
+ }
+ }
+ // Expected return value when all tests pass
+ testCode << Whiskers(R"(return 0;)")("endl", "\n")("ind", "\t\t").render();
+ return {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;)")("endl", "\n")("ind", "\t\t").render();
+ else
+ tie(usingLibDecl, testCode) = generateTestCase(_testContract);
+
+ Whiskers c(R"(contract C {})");
+ c("endl", "\n");
+ c("isLibrary", m_libraryTest);
+ c("usingDecl", usingLibDecl);
+ Whiskers f("function test() public returns (uint){}");
+ f("ind", "\t");
+ f("endl", "\n");
+ f("testCode", testCode);
+ c("function", f.render());
+ return c.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);
+
+ Whiskers p(R"(pragma solidity >=0.0;)");
+ p("endl", "\n");
+ p("contracts", contracts.str());
+ p("test", visit(_p.test()));
+ return p.render();
+}
+
+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.emplace(_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()();
+}
diff --git a/test/tools/ossfuzz/protoToSol.h b/test/tools/ossfuzz/protoToSol.h
new file mode 100644
index 000000000..de2220752
--- /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.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 m_randomGen;
+ /// Maps 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;
+};
+}
diff --git a/test/tools/ossfuzz/solProto.proto b/test/tools/ossfuzz/solProto.proto
new file mode 100644
index 000000000..86e938c7c
--- /dev/null
+++ b/test/tools/ossfuzz/solProto.proto
@@ -0,0 +1,111 @@
+/*
+ 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;
+ NONPAYABLE = 3;
+ }
+ required StateMutability mut = 1;
+}
+
+message LibraryFunction {
+ // Library functions cannot be payable
+ enum StateMutability {
+ PURE = 0;
+ VIEW = 1;
+ NONPAYABLE = 2;
+ }
+ 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;
+ NONPAYABLE = 3;
+ }
+ 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;
diff --git a/test/tools/ossfuzz/solProtoFuzzer.cpp b/test/tools/ossfuzz/solProtoFuzzer.cpp
new file mode 100644
index 000000000..80facb3e1
--- /dev/null
+++ b/test/tools/ossfuzz/solProtoFuzzer.cpp
@@ -0,0 +1,234 @@
+/*
+ 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
+
+#include
+#include
+
+#include
+
+static evmc::VM evmone = evmc::VM{evmc_create_evmone()};
+
+using namespace solidity::test::abiv2fuzzer;
+using namespace solidity::test::solprotofuzzer;
+using namespace solidity;
+using namespace solidity::test;
+using namespace solidity::util;
+using namespace std;
+
+namespace
+{
+/// Test function returns a uint256 value
+static size_t const expectedOutputLength = 32;
+/// Expected output value is decimal 0
+static uint8_t const expectedOutput[expectedOutputLength] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/// Compares the contents of the memory address pointed to
+/// by `_result` of `_length` bytes to the expected output.
+/// Returns true if `_result` matches expected output, false
+/// otherwise.
+bool isOutputExpected(evmc::result const& _run)
+{
+ if (_run.output_size != expectedOutputLength)
+ return false;
+
+ return memcmp(_run.output_data, expectedOutput, expectedOutputLength) == 0;
+}
+
+/// Accepts a reference to a user-specified input and returns an
+/// evmc_message with all of its fields zero initialized except
+/// gas and input fields.
+/// The gas field is set to the maximum permissible value so that we
+/// don't run into out of gas errors. The input field is copied from
+/// user input.
+evmc_message initializeMessage(bytes const& _input)
+{
+ // Zero initialize all message fields
+ evmc_message msg = {};
+ // Gas available (value of type int64_t) is set to its maximum
+ // value.
+ msg.gas = std::numeric_limits::max();
+ msg.input_data = _input.data();
+ msg.input_size = _input.size();
+ return msg;
+}
+
+/// Accepts host context implementation, and keccak256 hash of the function
+/// to be called at a specified address in the simulated blockchain as
+/// input and returns the result of the execution of the called function.
+evmc::result executeContract(
+ EVMHost& _hostContext,
+ bytes const& _functionHash,
+ evmc_address _deployedAddress
+)
+{
+ evmc_message message = initializeMessage(_functionHash);
+ message.destination = _deployedAddress;
+ message.kind = EVMC_CALL;
+ return _hostContext.call(message);
+}
+
+/// Accepts a reference to host context implementation and byte code
+/// as input and deploys it on the simulated blockchain. Returns the
+/// result of deployment.
+evmc::result deployContract(EVMHost& _hostContext, bytes const& _code)
+{
+ evmc_message message = initializeMessage(_code);
+ message.kind = EVMC_CREATE;
+ return _hostContext.call(message);
+}
+
+std::pair compileContract(
+ std::string _sourceCode,
+ std::string _contractName,
+ std::map const& _libraryAddresses = {},
+ frontend::OptimiserSettings _optimization = frontend::OptimiserSettings::minimal()
+)
+{
+ try
+ {
+ // Compile contract generated by the proto fuzzer
+ SolidityCompilationFramework solCompilationFramework;
+ return std::make_pair(
+ solCompilationFramework.compileContract(_sourceCode, _contractName, _libraryAddresses, _optimization),
+ solCompilationFramework.getMethodIdentifiers()
+ );
+ }
+ // Ignore stack too deep errors during compilation
+ catch (evmasm::StackTooDeepException const&)
+ {
+ return std::make_pair(bytes{}, Json::Value(0));
+ }
+}
+
+evmc::result deployAndExecute(EVMHost& _hostContext, bytes _byteCode, std::string _hexEncodedInput)
+{
+ // Deploy contract and signal failure if deploy failed
+ evmc::result createResult = deployContract(_hostContext, _byteCode);
+ solAssert(
+ createResult.status_code == EVMC_SUCCESS,
+ "Proto solc fuzzer: Contract creation failed"
+ );
+
+ // Execute test function and signal failure if EVM reverted or
+ // did not return expected output on successful execution.
+ evmc::result callResult = executeContract(
+ _hostContext,
+ fromHex(_hexEncodedInput),
+ createResult.create_address
+ );
+
+ // We don't care about EVM One failures other than EVMC_REVERT
+ solAssert(callResult.status_code != EVMC_REVERT, "Proto solc fuzzer: EVM One reverted");
+ return callResult;
+}
+
+evmc::result compileDeployAndExecute(
+ std::string _sourceCode,
+ std::string _contractName,
+ std::string _methodName,
+ frontend::OptimiserSettings _optimization,
+ std::string _libraryName = {}
+)
+{
+ bytes libraryBytecode;
+ Json::Value libIds;
+ // We target the default EVM which is the latest
+ langutil::EVMVersion version = {};
+ EVMHost hostContext(version, evmone);
+ std::map _libraryAddressMap;
+
+ // First deploy library
+ if (!_libraryName.empty())
+ {
+ tie(libraryBytecode, libIds) = compileContract(
+ _sourceCode,
+ _libraryName,
+ {},
+ _optimization
+ );
+ // Deploy contract and signal failure if deploy failed
+ evmc::result createResult = deployContract(hostContext, libraryBytecode);
+ solAssert(
+ createResult.status_code == EVMC_SUCCESS,
+ "Proto solc fuzzer: Library deployment failed"
+ );
+ _libraryAddressMap[_libraryName] = EVMHost::convertFromEVMC(createResult.create_address);
+ }
+
+ auto [bytecode, ids] = compileContract(
+ _sourceCode,
+ _contractName,
+ _libraryAddressMap,
+ _optimization
+ );
+
+ return deployAndExecute(
+ hostContext,
+ bytecode,
+ ids[_methodName].asString()
+ );
+}
+}
+
+DEFINE_PROTO_FUZZER(Program const& _input)
+{
+ ProtoConverter converter;
+ string sol_source = converter.protoToSolidity(_input);
+
+ if (char const* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
+ {
+ // With libFuzzer binary run this to generate a YUL source file x.yul:
+ // PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
+ ofstream of(dump_path);
+ of.write(sol_source.data(), sol_source.size());
+ }
+
+ if (char const* dump_path = getenv("SOL_DEBUG_FILE"))
+ {
+ sol_source.clear();
+ // With libFuzzer binary run this to generate a YUL source file x.yul:
+ // PROTO_FUZZER_LOAD_PATH=x.yul ./a.out proto-input
+ ifstream ifstr(dump_path);
+ sol_source = {
+ std::istreambuf_iterator(ifstr),
+ std::istreambuf_iterator()
+ };
+ std::cout << sol_source << std::endl;
+ }
+
+ auto minimalResult = compileDeployAndExecute(
+ sol_source,
+ ":C",
+ "test()",
+ frontend::OptimiserSettings::minimal(),
+ converter.libraryTest() ? converter.libraryName() : ""
+ );
+ bool successState = minimalResult.status_code == EVMC_SUCCESS;
+ if (successState)
+ solAssert(
+ isOutputExpected(minimalResult),
+ "Proto solc fuzzer: Output incorrect"
+ );
+}