mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add adaptor and support library and interface inheritance
This commit is contained in:
parent
1d533694e5
commit
8973ca3c85
@ -42,6 +42,7 @@ struct InternalCompilerError: virtual util::Exception {};
|
||||
struct FatalError: virtual util::Exception {};
|
||||
struct UnimplementedFeatureError: virtual util::Exception {};
|
||||
struct InvalidAstError: virtual util::Exception {};
|
||||
struct FuzzerError: virtual util::Exception {};
|
||||
|
||||
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
|
||||
#define solAssert(CONDITION, DESCRIPTION) \
|
||||
|
@ -219,8 +219,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
||||
else
|
||||
solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported.");
|
||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
|
||||
solAssert(
|
||||
assertThrow(
|
||||
2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16,
|
||||
CompilerError,
|
||||
"Stack too deep, try removing local variables."
|
||||
);
|
||||
// fetch target storage reference
|
||||
|
@ -455,7 +455,11 @@ void CompilerUtils::encodeToMemory(
|
||||
// leave end_of_mem as dyn head pointer
|
||||
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
||||
dynPointers++;
|
||||
solAssert((argSize + dynPointers) < 16, "Stack too deep, try using fewer variables.");
|
||||
assertThrow(
|
||||
(argSize + dynPointers) < 16,
|
||||
CompilerError,
|
||||
"Stack too deep, try using fewer variables."
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -497,8 +501,9 @@ void CompilerUtils::encodeToMemory(
|
||||
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
|
||||
{
|
||||
// copy tail pointer (=mem_end - mem_start) to memory
|
||||
solAssert(
|
||||
assertThrow(
|
||||
(2 + dynPointers) <= 16,
|
||||
CompilerError,
|
||||
"Stack too deep(" + to_string(2 + dynPointers) + "), try using fewer variables."
|
||||
);
|
||||
m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2;
|
||||
@ -1251,7 +1256,11 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||
|
||||
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
||||
{
|
||||
solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
|
||||
assertThrow(
|
||||
_stackDepth <= 16,
|
||||
CompilerError,
|
||||
"Stack too deep, try removing local variables."
|
||||
);
|
||||
for (unsigned i = 0; i < _itemSize; ++i)
|
||||
m_context << dupInstruction(_stackDepth);
|
||||
}
|
||||
@ -1273,14 +1282,22 @@ void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize)
|
||||
|
||||
void CompilerUtils::rotateStackUp(unsigned _items)
|
||||
{
|
||||
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
|
||||
assertThrow(
|
||||
_items - 1 <= 16,
|
||||
CompilerError,
|
||||
"Stack too deep, try removing local variables."
|
||||
);
|
||||
for (unsigned i = 1; i < _items; ++i)
|
||||
m_context << swapInstruction(_items - i);
|
||||
}
|
||||
|
||||
void CompilerUtils::rotateStackDown(unsigned _items)
|
||||
{
|
||||
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
|
||||
assertThrow(
|
||||
_items - 1 <= 16,
|
||||
CompilerError,
|
||||
"Stack too deep, try removing local variables."
|
||||
);
|
||||
for (unsigned i = 1; i < _items; ++i)
|
||||
m_context << swapInstruction(i);
|
||||
}
|
||||
|
@ -208,8 +208,9 @@ void CodeGenerator::assemble(
|
||||
}
|
||||
catch (StackTooDeepError const& _e)
|
||||
{
|
||||
yulAssert(
|
||||
assertThrow(
|
||||
false,
|
||||
CompilerError,
|
||||
"Stack too deep when compiling inline assembly" +
|
||||
(_e.comment() ? ": " + *_e.comment() : ".")
|
||||
);
|
||||
|
@ -85,7 +85,7 @@ void FuzzerUtil::testCompiler(string const& _input, bool _optimize)
|
||||
compiler.setOptimiserSettings(optimiserSettings);
|
||||
try
|
||||
{
|
||||
compiler.compile();
|
||||
solAssert(compiler.compile(), "Compilation failed");
|
||||
}
|
||||
catch (Error const&)
|
||||
{
|
||||
@ -96,6 +96,12 @@ void FuzzerUtil::testCompiler(string const& _input, bool _optimize)
|
||||
catch (UnimplementedFeatureError const&)
|
||||
{
|
||||
}
|
||||
catch (CompilerError const&)
|
||||
{
|
||||
}
|
||||
catch (StackTooDeepException const&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void FuzzerUtil::runCompiler(string const& _input, bool _quiet)
|
||||
|
662
test/tools/ossfuzz/SolProtoAdaptor.cpp
Normal file
662
test/tools/ossfuzz/SolProtoAdaptor.cpp
Normal file
@ -0,0 +1,662 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <test/tools/ossfuzz/SolProtoAdaptor.h>
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <libsolutil/Whiskers.h>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace solidity::test::solprotofuzzer::adaptor;
|
||||
using namespace solidity::test::solprotofuzzer;
|
||||
using namespace std;
|
||||
using namespace solidity::util;
|
||||
|
||||
namespace
|
||||
{
|
||||
SolFunctionStateMutability mutabilityConverter(InterfaceFunction_StateMutability _mut)
|
||||
{
|
||||
switch (_mut)
|
||||
{
|
||||
case InterfaceFunction_StateMutability_PURE:
|
||||
return SolFunctionStateMutability::PURE;
|
||||
case InterfaceFunction_StateMutability_VIEW:
|
||||
return SolFunctionStateMutability::VIEW;
|
||||
case InterfaceFunction_StateMutability_PAYABLE:
|
||||
return SolFunctionStateMutability::PAYABLE;
|
||||
}
|
||||
}
|
||||
|
||||
SolFunctionStateMutability mutabilityConverter(ContractFunction_StateMutability _mut)
|
||||
{
|
||||
switch (_mut)
|
||||
{
|
||||
case ContractFunction_StateMutability_PURE:
|
||||
return SolFunctionStateMutability::PURE;
|
||||
case ContractFunction_StateMutability_VIEW:
|
||||
return SolFunctionStateMutability::VIEW;
|
||||
case ContractFunction_StateMutability_PAYABLE:
|
||||
return SolFunctionStateMutability::PAYABLE;
|
||||
}
|
||||
}
|
||||
|
||||
SolLibraryFunctionStateMutability mutabilityConverter(LibraryFunction_StateMutability _mut)
|
||||
{
|
||||
switch (_mut)
|
||||
{
|
||||
case LibraryFunction_StateMutability_PURE:
|
||||
return SolLibraryFunctionStateMutability::PURE;
|
||||
case LibraryFunction_StateMutability_VIEW:
|
||||
return SolLibraryFunctionStateMutability::VIEW;
|
||||
}
|
||||
}
|
||||
|
||||
SolFunctionVisibility visibilityConverter(ContractFunction_Visibility _vis)
|
||||
{
|
||||
switch (_vis)
|
||||
{
|
||||
case ContractFunction_Visibility_PUBLIC:
|
||||
return SolFunctionVisibility::PUBLIC;
|
||||
case ContractFunction_Visibility_PRIVATE:
|
||||
return SolFunctionVisibility::PRIVATE;
|
||||
case ContractFunction_Visibility_EXTERNAL:
|
||||
return SolFunctionVisibility::EXTERNAL;
|
||||
case ContractFunction_Visibility_INTERNAL:
|
||||
return SolFunctionVisibility::INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
SolFunctionVisibility visibilityConverter(LibraryFunction_Visibility _vis)
|
||||
{
|
||||
switch (_vis)
|
||||
{
|
||||
case LibraryFunction_Visibility_PUBLIC:
|
||||
return SolFunctionVisibility::PUBLIC;
|
||||
case LibraryFunction_Visibility_PRIVATE:
|
||||
return SolFunctionVisibility::PRIVATE;
|
||||
case LibraryFunction_Visibility_EXTERNAL:
|
||||
return SolFunctionVisibility::EXTERNAL;
|
||||
case LibraryFunction_Visibility_INTERNAL:
|
||||
return SolFunctionVisibility::INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
string functionVisibility(SolFunctionVisibility _vis)
|
||||
{
|
||||
switch (_vis)
|
||||
{
|
||||
case SolFunctionVisibility::PUBLIC:
|
||||
return "public";
|
||||
case SolFunctionVisibility::PRIVATE:
|
||||
return "private";
|
||||
case SolFunctionVisibility::EXTERNAL:
|
||||
return "external";
|
||||
case SolFunctionVisibility::INTERNAL:
|
||||
return "internal";
|
||||
}
|
||||
}
|
||||
|
||||
string functionMutability(SolFunctionStateMutability _mut)
|
||||
{
|
||||
switch (_mut)
|
||||
{
|
||||
case SolFunctionStateMutability::PURE:
|
||||
return "pure";
|
||||
case SolFunctionStateMutability::VIEW:
|
||||
return "view";
|
||||
case SolFunctionStateMutability::PAYABLE:
|
||||
return "payable";
|
||||
}
|
||||
}
|
||||
|
||||
string libraryFunctionMutability(SolLibraryFunctionStateMutability _mut)
|
||||
{
|
||||
switch (_mut)
|
||||
{
|
||||
case SolLibraryFunctionStateMutability::PURE:
|
||||
return "pure";
|
||||
case SolLibraryFunctionStateMutability::VIEW:
|
||||
return "view";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SolInterfaceFunction::SolInterfaceFunction(
|
||||
std::string _functionName,
|
||||
SolFunctionStateMutability _mutability,
|
||||
bool _override
|
||||
)
|
||||
{
|
||||
m_functionName = _functionName;
|
||||
m_mutability = _mutability;
|
||||
m_override = _override;
|
||||
}
|
||||
|
||||
bool SolInterfaceFunction::operator==(SolInterfaceFunction const& _rhs) const
|
||||
{
|
||||
// TODO: Change this once we permit arbitrary function parameter types
|
||||
return name() == _rhs.name();
|
||||
}
|
||||
|
||||
bool SolInterfaceFunction::operator!=(SolInterfaceFunction const& _rhs) const
|
||||
{
|
||||
// TODO: Change this once we permit arbitrary function parameter types
|
||||
return name() != _rhs.name();
|
||||
}
|
||||
|
||||
string SolInterfaceFunction::str() const
|
||||
{
|
||||
string bodyStr = Whiskers(R"(
|
||||
{
|
||||
return <uint>;
|
||||
})")
|
||||
("uint", "0")
|
||||
.render();
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>() external <stateMutability><?isOverride> override</isOverride>
|
||||
<?isVirtual> virtual</isVirtual> returns (uint)<?isImplemented><body><!isImplemented>;</isImplemented>
|
||||
)")
|
||||
("functionName", name())
|
||||
("stateMutability", functionMutability(mutability()))
|
||||
("isOverride", override())
|
||||
("isVirtual", isVirtual())
|
||||
("isImplemented", implement())
|
||||
("body", bodyStr)
|
||||
.render();
|
||||
}
|
||||
|
||||
SolContractFunction::SolContractFunction(
|
||||
ContractFunction const& _function,
|
||||
std::string _contractName,
|
||||
std::string _functionName,
|
||||
std::string _returnValue
|
||||
)
|
||||
{
|
||||
m_contractName = _contractName;
|
||||
m_functionName = _functionName;
|
||||
m_visibility = visibilityConverter(_function.vis());
|
||||
m_mutability = mutabilityConverter(_function.mut());
|
||||
m_virtual = _function.virtualfunc();
|
||||
m_returnValue = _returnValue;
|
||||
}
|
||||
|
||||
bool SolContractFunction::operator==(SolContractFunction const& _rhs) const
|
||||
{
|
||||
return this->m_visibility == _rhs.m_visibility && this->m_mutability == _rhs.m_mutability;
|
||||
}
|
||||
|
||||
bool SolContractFunction::operator!=(SolContractFunction const& _rhs) const
|
||||
{
|
||||
return this->m_visibility != _rhs.m_visibility || this->m_mutability != _rhs.m_mutability;
|
||||
}
|
||||
|
||||
string SolContractFunction::str() const
|
||||
{
|
||||
string bodyStr = Whiskers(R"(
|
||||
{
|
||||
return <uint>;
|
||||
})")
|
||||
("uint", returnValue())
|
||||
.render();
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>()<?isOverride> override</isOverride>
|
||||
<?isVirtual> virtual</isVirtual> <visibility> <stateMutability>
|
||||
returns (uint)<?isImplemented><body><!isImplemented>;</isImplemented>)")
|
||||
("functionName", name())
|
||||
("isVirtual", isVirtual())
|
||||
("isOverride", "_override")
|
||||
("visibility", functionVisibility(visibility()))
|
||||
("stateMutability", functionMutability(mutability()))
|
||||
("body", bodyStr)
|
||||
("isImplemented", "_implement")
|
||||
.render();
|
||||
}
|
||||
|
||||
SolLibraryFunction::SolLibraryFunction(
|
||||
LibraryFunction const& _function,
|
||||
std::string _libraryName,
|
||||
std::string _functionName,
|
||||
std::string _returnValue
|
||||
)
|
||||
{
|
||||
m_libraryName = _libraryName;
|
||||
m_functionName = _functionName;
|
||||
m_visibility = visibilityConverter(_function.vis());
|
||||
m_mutability = mutabilityConverter(_function.mut());
|
||||
m_returnValue = _returnValue;
|
||||
}
|
||||
|
||||
string SolLibraryFunction::str() const
|
||||
{
|
||||
string bodyStr = Whiskers(R"(
|
||||
{
|
||||
return <uint>;
|
||||
})")
|
||||
("uint", returnValue())
|
||||
.render();
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(uint) <visibility> <stateMutability> returns (uint)<body>)")
|
||||
("functionName", name())
|
||||
("visibility", functionVisibility(visibility()))
|
||||
("stateMutability", libraryFunctionMutability(mutability()))
|
||||
("body", bodyStr)
|
||||
.render();
|
||||
}
|
||||
|
||||
SolBaseContract::SolBaseContract(ProtoBaseContract _base, string _name, shared_ptr<SolRandomNumGenerator> _prng)
|
||||
{
|
||||
if (auto c = get<Contract const*>(_base))
|
||||
m_base.push_back(
|
||||
make_shared<SolContract>(SolContract(*c, _name, _prng))
|
||||
);
|
||||
else if (auto i = get<Interface const*>(_base))
|
||||
m_base.push_back(
|
||||
make_shared<SolInterface>(SolInterface(*i, _name, _prng))
|
||||
);
|
||||
}
|
||||
|
||||
void SolInterface::overrideHelper(
|
||||
shared_ptr<SolInterfaceFunction> _function,
|
||||
shared_ptr<SolInterface> _base
|
||||
)
|
||||
{
|
||||
auto functionName = _function->name();
|
||||
auto mutability = _function->mutability();
|
||||
// Check if two or more bases define this function
|
||||
bool multipleOverride = false;
|
||||
// If function has already been overridden, add
|
||||
// new base to list of overridden bases
|
||||
for (auto &m: m_overrideMap)
|
||||
{
|
||||
// Must override if two or more bases define the
|
||||
// same function
|
||||
if (m.first->operator==(*_function))
|
||||
{
|
||||
// Report error if state mutability of identically
|
||||
// named functions differ
|
||||
if (m.first->mutability() != _function->mutability())
|
||||
assertThrow(
|
||||
false,
|
||||
langutil::FuzzerError,
|
||||
"Input specifies multiple function overrides with identical names"
|
||||
" and parameter types but different mutability."
|
||||
);
|
||||
// Add new base to list of overridden bases
|
||||
m_overrideMap[m.first].push_back(_base);
|
||||
multipleOverride = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If function has not been overridden, add new override
|
||||
if (!multipleOverride)
|
||||
m_overrideMap.insert(
|
||||
pair(
|
||||
make_shared<SolInterfaceFunction>(
|
||||
SolInterfaceFunction(
|
||||
functionName,
|
||||
mutability,
|
||||
true
|
||||
)
|
||||
),
|
||||
vector<shared_ptr<SolInterface>>{_base}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void SolInterface::addOverrides()
|
||||
{
|
||||
for (auto &base: m_baseInterfaces)
|
||||
{
|
||||
// Override base interface functions
|
||||
for (auto &f: base->m_interfaceFunctions)
|
||||
overrideHelper(f, base);
|
||||
// Override base interface functions that are themselves overrides
|
||||
for (auto &e: base->m_overrideMap)
|
||||
overrideHelper(e.first, base);
|
||||
}
|
||||
}
|
||||
|
||||
void SolInterface::addBases(Interface const& _interface)
|
||||
{
|
||||
for (auto &b: _interface.bases())
|
||||
{
|
||||
auto base = make_shared<SolInterface>(SolInterface(b, newBaseName(), m_prng));
|
||||
m_baseInterfaces.push_back(base);
|
||||
cout << "Added " << base->name() << " as base" << endl;
|
||||
// Worst case, we override all base functions so we
|
||||
// increment derived contract's function index by
|
||||
// this amount.
|
||||
m_functionIndex += base->functionIndex();
|
||||
m_lastBaseName = base->lastBaseName();
|
||||
}
|
||||
}
|
||||
|
||||
void SolInterface::addFunctions(Interface const& _interface)
|
||||
{
|
||||
for (auto &f: _interface.funcdef())
|
||||
m_interfaceFunctions.push_back(
|
||||
make_shared<SolInterfaceFunction>(
|
||||
SolInterfaceFunction(
|
||||
newFunctionName(),
|
||||
mutabilityConverter(f.mut())
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
SolInterface::SolInterface(Interface const& _interface, string _name, shared_ptr<SolRandomNumGenerator> _prng)
|
||||
{
|
||||
cout << "Constructing " << _name << endl;
|
||||
m_prng = _prng;
|
||||
m_interfaceName = _name;
|
||||
m_lastBaseName = m_interfaceName;
|
||||
addBases(_interface);
|
||||
addOverrides();
|
||||
addFunctions(_interface);
|
||||
}
|
||||
|
||||
string SolInterface::baseNames() const
|
||||
{
|
||||
ostringstream bases;
|
||||
string separator{};
|
||||
for (auto &b: m_baseInterfaces)
|
||||
{
|
||||
bases << separator << b->name();
|
||||
if (separator.empty())
|
||||
separator = ", ";
|
||||
}
|
||||
return bases.str();
|
||||
}
|
||||
|
||||
string SolInterface::baseInterfaceStr() const
|
||||
{
|
||||
ostringstream baseInterfaces;
|
||||
for (auto &b: m_baseInterfaces)
|
||||
baseInterfaces << b->str();
|
||||
|
||||
return baseInterfaces.str();
|
||||
}
|
||||
|
||||
string SolInterface::overrideStr() const
|
||||
{
|
||||
ostringstream overriddenFunctions;
|
||||
for (auto &f: m_overrideMap)
|
||||
{
|
||||
ostringstream overriddenBaseNames;
|
||||
if (f.second.size() > 1)
|
||||
{
|
||||
string sep{};
|
||||
for (auto &b: f.second)
|
||||
{
|
||||
overriddenBaseNames << Whiskers(R"(<sep><name>)")
|
||||
("sep", sep)
|
||||
("name", b->name())
|
||||
.render();
|
||||
if (sep.empty())
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
overriddenFunctions << Whiskers(R"(
|
||||
function <functionName>() external <stateMutability> override<?multiple>(<baseNames>)</multiple> returns (uint);)")
|
||||
("functionName", f.first->name())
|
||||
("stateMutability", functionMutability(f.first->mutability()))
|
||||
("multiple", f.second.size() > 1)
|
||||
("baseNames", overriddenBaseNames.str())
|
||||
.render();
|
||||
}
|
||||
return overriddenFunctions.str();
|
||||
}
|
||||
|
||||
string SolInterface::str() const
|
||||
{
|
||||
ostringstream functions;
|
||||
ostringstream bases;
|
||||
|
||||
// Print overridden functions
|
||||
functions << overrideStr();
|
||||
// Print non-overridden functions
|
||||
for (auto &f: m_interfaceFunctions)
|
||||
functions << f->str();
|
||||
|
||||
for (auto &b: m_baseInterfaces)
|
||||
bases << b->str();
|
||||
|
||||
return Whiskers(R"(
|
||||
<bases>
|
||||
interface <programName><?inheritance> is <baseNames></inheritance> {
|
||||
<functionDefs>
|
||||
})")
|
||||
("bases", bases.str())
|
||||
("programName", name())
|
||||
("inheritance", m_baseInterfaces.size() > 0)
|
||||
("baseNames", baseNames())
|
||||
("functionDefs", functions.str())
|
||||
.render();
|
||||
}
|
||||
|
||||
SolContract::SolContract(Contract const& _contract, std::string _name, shared_ptr<SolRandomNumGenerator> _prng)
|
||||
{
|
||||
m_prng = _prng;
|
||||
m_contractName = _name;
|
||||
m_abstract = _contract.abstract();
|
||||
for (auto &f: _contract.funcdef())
|
||||
m_contractFunctions.push_back(
|
||||
make_unique<SolContractFunction>(
|
||||
SolContractFunction(
|
||||
f,
|
||||
m_contractName,
|
||||
newFunctionName(),
|
||||
newReturnValue()
|
||||
)
|
||||
)
|
||||
);
|
||||
for (auto &b: _contract.bases())
|
||||
{
|
||||
switch (b.contract_or_interface_oneof_case())
|
||||
{
|
||||
case ContractOrInterface::kC:
|
||||
m_baseContracts.push_back(
|
||||
make_unique<SolBaseContract>(
|
||||
SolBaseContract(&b.c(), newContractBaseName(), m_prng)
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ContractOrInterface::kI:
|
||||
m_baseContracts.push_back(
|
||||
make_unique<SolBaseContract>(
|
||||
SolBaseContract(&b.i(), newInterfaceBaseName(), m_prng)
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ContractOrInterface::CONTRACT_OR_INTERFACE_ONEOF_NOT_SET:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SolLibrary::addFunction(LibraryFunction const& _function)
|
||||
{
|
||||
// Register function name and return value
|
||||
string functionName = newFunctionName();
|
||||
string outputStr = newReturnValue();
|
||||
|
||||
SolFunctionVisibility visibility = visibilityConverter(_function.vis());
|
||||
|
||||
if (visibility == SolFunctionVisibility::PUBLIC || visibility == SolFunctionVisibility::EXTERNAL)
|
||||
{
|
||||
solAssert(!m_publicFunctionMap.count(functionName), "Sol proto adapter: Duplicate library function");
|
||||
m_publicFunctionMap.insert(pair(functionName, outputStr));
|
||||
}
|
||||
|
||||
// Create and add function to library
|
||||
m_functions.push_back(
|
||||
make_unique<SolLibraryFunction>(
|
||||
SolLibraryFunction(_function,
|
||||
name(),
|
||||
functionName,
|
||||
outputStr
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
SolLibrary::SolLibrary(Library const& _library, string _name)
|
||||
{
|
||||
m_libraryName = _name;
|
||||
for (LibraryFunction const& f: _library.funcdef())
|
||||
addFunction(f);
|
||||
}
|
||||
|
||||
string SolLibrary::str() const
|
||||
{
|
||||
ostringstream functions;
|
||||
|
||||
for (auto &f: m_functions)
|
||||
functions << f->str();
|
||||
|
||||
return Whiskers(R"(
|
||||
library <name> {
|
||||
<functions>
|
||||
})")
|
||||
("name", name())
|
||||
("functions", functions.str())
|
||||
.render();
|
||||
}
|
||||
|
||||
bool SolLibrary::validTest() const
|
||||
{
|
||||
return m_publicFunctionMap.size() > 0;
|
||||
}
|
||||
|
||||
pair<string, string> SolLibrary::pseudoRandomTest(unsigned _randomIdx)
|
||||
{
|
||||
solAssert(m_publicFunctionMap.size() > 0, "Sol proto adaptor: Empty library map");
|
||||
string chosenFunction;
|
||||
unsigned numFunctions = m_publicFunctionMap.size();
|
||||
unsigned functionIndex = _randomIdx % numFunctions;
|
||||
unsigned mapIdx = 0;
|
||||
for (auto &e: m_publicFunctionMap)
|
||||
{
|
||||
if (functionIndex == mapIdx)
|
||||
chosenFunction = e.first;
|
||||
mapIdx++;
|
||||
}
|
||||
solAssert(m_publicFunctionMap.count(chosenFunction), "Sol proto adaptor: Invalid library function chosen");
|
||||
return pair(chosenFunction, m_publicFunctionMap[chosenFunction]);
|
||||
}
|
||||
|
||||
CFunctionOverride::CFunctionOverrideType CFunctionOverride::functionType() const
|
||||
{
|
||||
if (holds_alternative<unique_ptr<SolContractFunction const>>(m_function.second))
|
||||
return CFunctionOverrideType::CONTRACT;
|
||||
solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
|
||||
return CFunctionOverrideType::INTERFACE;
|
||||
}
|
||||
|
||||
string CFunctionOverride::name() const
|
||||
{
|
||||
if (holds_alternative<unique_ptr<SolContractFunction const>>(m_function.second))
|
||||
return get<unique_ptr<SolContractFunction const>>(m_function.second)->name();
|
||||
solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
|
||||
return get<unique_ptr<SolInterfaceFunction const>>(m_function.second)->name();
|
||||
}
|
||||
|
||||
bool CFunctionOverride::interfaceFunction() const
|
||||
{
|
||||
return functionType() == CFunctionOverrideType::INTERFACE;
|
||||
}
|
||||
|
||||
bool CFunctionOverride::contractFunction() const
|
||||
{
|
||||
return functionType() == CFunctionOverrideType::CONTRACT;
|
||||
}
|
||||
|
||||
SolFunctionVisibility CFunctionOverride::visibility() const
|
||||
{
|
||||
if (contractFunction())
|
||||
return get<unique_ptr<SolContractFunction const>>(m_function.second)->visibility();
|
||||
solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
|
||||
return SolFunctionVisibility::EXTERNAL;
|
||||
}
|
||||
|
||||
SolFunctionStateMutability CFunctionOverride::mutability() const
|
||||
{
|
||||
if (contractFunction())
|
||||
return get<unique_ptr<SolContractFunction const>>(m_function.second)->mutability();
|
||||
solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
|
||||
return get<unique_ptr<SolInterfaceFunction const>>(m_function.second)->mutability();
|
||||
}
|
||||
|
||||
string CFunctionOverride::str() const
|
||||
{
|
||||
string bodyStr = Whiskers(R"(
|
||||
{
|
||||
return <uint>;
|
||||
})")
|
||||
("uint", returnValue())
|
||||
.render();
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>() override <?isVirtual> virtual</isVirtual> <visibility> <stateMutability>
|
||||
returns (uint)<?isImplemented><body><!isImplemented>;</isImplemented>)")
|
||||
("functionName", name())
|
||||
("isVirtual", virtualized() || !implemented())
|
||||
("visibility", functionVisibility(visibility()))
|
||||
("stateMutability", functionMutability(mutability()))
|
||||
("body", bodyStr)
|
||||
("isImplemented", implemented())
|
||||
.render();
|
||||
}
|
||||
|
||||
//string CFunctionOverride::commaSeparatedBaseNames()
|
||||
//{
|
||||
// ostringstream baseNames;
|
||||
// string separator{};
|
||||
// for (auto &override: m_function)
|
||||
// {
|
||||
// auto base = override.first;
|
||||
// string baseName;
|
||||
// if (auto b = get<SolInterface const*>(base))
|
||||
// baseName = b->name();
|
||||
// else
|
||||
// baseName = get<SolContract const*>(base)->name();
|
||||
//
|
||||
// baseNames << Whiskers(R"(<sep><base>)")
|
||||
// ("sep", separator)
|
||||
// ("base", baseName)
|
||||
// .render();
|
||||
// if (separator.empty())
|
||||
// separator = ", ";
|
||||
// }
|
||||
// return baseNames.str();
|
||||
//}
|
||||
|
||||
//string CFunctionOverride::baseName() const
|
||||
//{
|
||||
// auto base = m_function.first;
|
||||
// if (contractFunction())
|
||||
// return get<shared_ptr<SolContract const>>(base)->name();
|
||||
// solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
|
||||
// return get<shared_ptr<SolInterface const>>(base)->name();
|
||||
//}
|
520
test/tools/ossfuzz/SolProtoAdaptor.h
Normal file
520
test/tools/ossfuzz/SolProtoAdaptor.h
Normal file
@ -0,0 +1,520 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <test/tools/ossfuzz/protoToSol.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace solidity::test::solprotofuzzer::adaptor
|
||||
{
|
||||
/// Solidity contract abstraction class
|
||||
struct SolContract;
|
||||
/// Solidity interface abstraction class
|
||||
struct SolInterface;
|
||||
/// Solidity library abstraction class
|
||||
struct SolLibrary;
|
||||
/// Solidity interface function abstraction class
|
||||
struct SolInterfaceFunction;
|
||||
/// Solidity contract function abstraction class
|
||||
struct SolContractFunction;
|
||||
/// Solidity library function abstraction class
|
||||
struct SolLibraryFunction;
|
||||
/// Solidity contract function override abstraction class
|
||||
struct CFunctionOverride;
|
||||
/// Solidity interface function override abstraction class
|
||||
struct IFunctionOverride;
|
||||
|
||||
/// Type that defines a contract function override as a variant of interface or
|
||||
/// contract function types.
|
||||
using OverrideFunction = std::variant<std::unique_ptr<SolInterfaceFunction const>, std::unique_ptr<SolContractFunction const>>;
|
||||
/// Type that defines a list of base contracts as a list of variants of interface or
|
||||
/// contract types.
|
||||
using BaseContracts = std::vector<std::variant<std::shared_ptr<SolInterface const>, std::shared_ptr<SolContract const>>>;
|
||||
/// Type that defines a contract function override as a pair of base contracts
|
||||
/// and its (their) function.
|
||||
using OverrideCFunction = std::pair<BaseContracts, OverrideFunction>;
|
||||
/// Type that defines an interface function override as a pair of interface
|
||||
/// and its function.
|
||||
using OverrideIFunction = std::pair<std::vector<std::shared_ptr<SolInterface const>>, std::unique_ptr<SolInterfaceFunction const>>;
|
||||
/// Type that defines a map of interface overrides
|
||||
using InterfaceOverrideMap = std::map<std::shared_ptr<SolInterfaceFunction>, std::vector<std::shared_ptr<SolInterface>>>;
|
||||
/// Type that defines an interface function as either a vanilla interface function
|
||||
/// or an override function.
|
||||
using IFunction = std::variant<std::unique_ptr<SolInterfaceFunction>, OverrideIFunction>;
|
||||
/// Type that defines a contract function as either a vanilla contract function
|
||||
/// or an override function.
|
||||
using CFunction = std::variant<std::unique_ptr<SolContractFunction>, OverrideCFunction>;
|
||||
/// Variant type that points to one of contract, interface protobuf messages
|
||||
using ProtoBaseContract = std::variant<Contract const*, Interface const*>;
|
||||
|
||||
enum class SolFunctionVisibility
|
||||
{
|
||||
PUBLIC,
|
||||
PRIVATE,
|
||||
INTERNAL,
|
||||
EXTERNAL
|
||||
};
|
||||
|
||||
enum class SolFunctionStateMutability
|
||||
{
|
||||
PURE,
|
||||
VIEW,
|
||||
PAYABLE
|
||||
};
|
||||
|
||||
enum class SolLibraryFunctionStateMutability
|
||||
{
|
||||
PURE,
|
||||
VIEW
|
||||
};
|
||||
|
||||
struct SolInterfaceFunction
|
||||
{
|
||||
SolInterfaceFunction(
|
||||
std::string _functionName,
|
||||
SolFunctionStateMutability _mutability,
|
||||
bool _override = false
|
||||
);
|
||||
bool operator==(SolInterfaceFunction const& _rhs) const;
|
||||
bool operator!=(SolInterfaceFunction const& _rhs) const;
|
||||
std::string str() const;
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
return m_functionName;
|
||||
}
|
||||
SolFunctionStateMutability mutability() const
|
||||
{
|
||||
return m_mutability;
|
||||
}
|
||||
bool override() const
|
||||
{
|
||||
return m_override;
|
||||
}
|
||||
bool isVirtual() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool implement() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string m_functionName;
|
||||
SolFunctionStateMutability m_mutability = SolFunctionStateMutability::PURE;
|
||||
bool m_override = false;
|
||||
bool m_virtual = false;
|
||||
bool m_implement = false;
|
||||
};
|
||||
|
||||
struct SolContractFunction
|
||||
{
|
||||
SolContractFunction(
|
||||
ContractFunction const& _function,
|
||||
std::string _contractName,
|
||||
std::string _functionName,
|
||||
std::string _returnValue
|
||||
);
|
||||
bool operator==(SolContractFunction const& _rhs) const;
|
||||
bool operator!=(SolContractFunction const& _rhs) const;
|
||||
std::string str() const;
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
return m_functionName;
|
||||
}
|
||||
std::string contractName() const
|
||||
{
|
||||
return m_contractName;
|
||||
}
|
||||
bool isVirtual() const
|
||||
{
|
||||
return m_virtual;
|
||||
}
|
||||
std::string returnValue() const
|
||||
{
|
||||
return m_returnValue;
|
||||
}
|
||||
SolFunctionVisibility visibility() const
|
||||
{
|
||||
return m_visibility;
|
||||
}
|
||||
SolFunctionStateMutability mutability() const
|
||||
{
|
||||
return m_mutability;
|
||||
}
|
||||
|
||||
std::string m_contractName;
|
||||
std::string m_functionName;
|
||||
SolFunctionVisibility m_visibility = SolFunctionVisibility::PUBLIC;
|
||||
SolFunctionStateMutability m_mutability = SolFunctionStateMutability::PURE;
|
||||
bool m_virtual = false;
|
||||
std::string m_returnValue;
|
||||
};
|
||||
|
||||
struct SolLibraryFunction
|
||||
{
|
||||
SolLibraryFunction(
|
||||
LibraryFunction const& _function,
|
||||
std::string _libraryName,
|
||||
std::string _functionName,
|
||||
std::string _returnValue
|
||||
);
|
||||
std::string str() const;
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
return m_functionName;
|
||||
}
|
||||
std::string libraryName() const
|
||||
{
|
||||
return m_libraryName;
|
||||
}
|
||||
std::string returnValue() const
|
||||
{
|
||||
return m_returnValue;
|
||||
}
|
||||
SolFunctionVisibility visibility() const
|
||||
{
|
||||
return m_visibility;
|
||||
}
|
||||
SolLibraryFunctionStateMutability mutability() const
|
||||
{
|
||||
return m_mutability;
|
||||
}
|
||||
|
||||
std::string m_libraryName;
|
||||
std::string m_functionName;
|
||||
SolFunctionVisibility m_visibility = SolFunctionVisibility::PUBLIC;
|
||||
SolLibraryFunctionStateMutability m_mutability = SolLibraryFunctionStateMutability::PURE;
|
||||
std::string m_returnValue;
|
||||
};
|
||||
|
||||
struct SolLibrary
|
||||
{
|
||||
SolLibrary(Library const& _library, std::string _name);
|
||||
std::vector<std::unique_ptr<SolLibraryFunction>> m_functions;
|
||||
/// Maps publicly exposed function name to expected output
|
||||
std::map<std::string, std::string> m_publicFunctionMap;
|
||||
|
||||
void addFunction(LibraryFunction const& _function);
|
||||
|
||||
bool validTest() const;
|
||||
|
||||
/// Returns a pair of function name and expected output
|
||||
/// that is pseudo randomly chosen from the list of all
|
||||
/// library functions.
|
||||
/// @param _randomIdx A pseudo randomly generated number that is
|
||||
/// used to index the list of all library functions.
|
||||
std::pair<std::string, std::string> pseudoRandomTest(unsigned _randomIdx);
|
||||
|
||||
std::string str() const;
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
return m_libraryName;
|
||||
}
|
||||
std::string newFunctionName()
|
||||
{
|
||||
return "f" + std::to_string(m_functionIndex++);
|
||||
}
|
||||
std::string newReturnValue()
|
||||
{
|
||||
return std::to_string(m_returnValue++);
|
||||
}
|
||||
|
||||
std::string m_libraryName;
|
||||
unsigned m_functionIndex = 0;
|
||||
unsigned m_returnValue = 0;
|
||||
};
|
||||
|
||||
struct SolBaseContract
|
||||
{
|
||||
SolBaseContract(ProtoBaseContract _base, std::string _name, std::shared_ptr<SolRandomNumGenerator> _prng);
|
||||
|
||||
BaseContracts m_base;
|
||||
std::string m_baseName;
|
||||
std::shared_ptr<SolRandomNumGenerator> m_prng;
|
||||
};
|
||||
|
||||
struct SolContract
|
||||
{
|
||||
SolContract(Contract const& _contract, std::string _name, std::shared_ptr<SolRandomNumGenerator> _prng);
|
||||
|
||||
std::string str() const;
|
||||
|
||||
unsigned randomNumber()
|
||||
{
|
||||
return m_prng->operator()();
|
||||
}
|
||||
std::string name() const
|
||||
{
|
||||
return m_contractName;
|
||||
}
|
||||
|
||||
bool abstract() const
|
||||
{
|
||||
return m_abstract;
|
||||
}
|
||||
|
||||
std::string newFunctionName()
|
||||
{
|
||||
return "f" + std::to_string(m_functionIndex++);
|
||||
}
|
||||
|
||||
std::string newContractBaseName()
|
||||
{
|
||||
return name() + "B" + std::to_string(m_baseIndex++);
|
||||
}
|
||||
|
||||
std::string newInterfaceBaseName()
|
||||
{
|
||||
return "IB" + std::to_string(m_baseIndex++);
|
||||
}
|
||||
|
||||
std::string newReturnValue()
|
||||
{
|
||||
return std::to_string(m_returnValue++);
|
||||
}
|
||||
|
||||
std::string m_contractName;
|
||||
bool m_abstract = false;
|
||||
unsigned m_baseIndex = 0;
|
||||
unsigned m_functionIndex = 0;
|
||||
unsigned m_returnValue = 0;
|
||||
std::vector<std::unique_ptr<SolContractFunction>> m_contractFunctions;
|
||||
std::vector<std::unique_ptr<SolBaseContract>> m_baseContracts;
|
||||
std::vector<std::unique_ptr<CFunctionOverride>> m_overriddenFunctions;
|
||||
std::shared_ptr<SolRandomNumGenerator> m_prng;
|
||||
};
|
||||
|
||||
struct SolInterface
|
||||
{
|
||||
SolInterface(
|
||||
Interface const& _interface,
|
||||
std::string _interfaceName,
|
||||
std::shared_ptr<SolRandomNumGenerator> _prng
|
||||
);
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
return m_interfaceName;
|
||||
}
|
||||
|
||||
unsigned randomNumber() const
|
||||
{
|
||||
return m_prng->operator()();
|
||||
}
|
||||
|
||||
bool coinFlip() const
|
||||
{
|
||||
return randomNumber() % 2 == 0;
|
||||
}
|
||||
|
||||
std::string newFunctionName()
|
||||
{
|
||||
return "f" + std::to_string(m_functionIndex++);
|
||||
}
|
||||
void incrementFunctionIndex()
|
||||
{
|
||||
m_functionIndex++;
|
||||
}
|
||||
void resetFunctionIndex()
|
||||
{
|
||||
m_functionIndex = 0;
|
||||
}
|
||||
void setFunctionIndex(unsigned _index)
|
||||
{
|
||||
m_functionIndex = _index;
|
||||
}
|
||||
unsigned functionIndex() const
|
||||
{
|
||||
return m_functionIndex;
|
||||
}
|
||||
std::string newBaseName()
|
||||
{
|
||||
m_lastBaseName += "B";
|
||||
return m_lastBaseName;
|
||||
}
|
||||
std::string lastBaseName()
|
||||
{
|
||||
return m_lastBaseName;
|
||||
}
|
||||
|
||||
std::string str() const;
|
||||
std::string overrideStr() const;
|
||||
|
||||
/// Returns the Solidity code for all base interfaces
|
||||
/// inherited by this interface.
|
||||
std::string baseInterfaceStr() const;
|
||||
|
||||
/// Returns comma-space separated names of base interfaces inherited by
|
||||
/// this interface.
|
||||
std::string baseNames() const;
|
||||
|
||||
/// Add base contracts in a depth-first manner
|
||||
void addBases(Interface const& _interface);
|
||||
/// Add functions
|
||||
void addFunctions(Interface const& _interface);
|
||||
/// Add overrides
|
||||
void addOverrides();
|
||||
/// Helper for adding overrides
|
||||
void overrideHelper(
|
||||
std::shared_ptr<SolInterfaceFunction> _function,
|
||||
std::shared_ptr<SolInterface> _interface
|
||||
);
|
||||
|
||||
unsigned m_functionIndex = 0;
|
||||
std::string m_lastBaseName;
|
||||
std::string m_interfaceName;
|
||||
std::vector<std::shared_ptr<SolInterfaceFunction>> m_interfaceFunctions;
|
||||
std::vector<std::shared_ptr<SolInterface>> m_baseInterfaces;
|
||||
InterfaceOverrideMap m_overrideMap;
|
||||
std::shared_ptr<SolRandomNumGenerator> m_prng;
|
||||
};
|
||||
|
||||
struct CFunctionOverride
|
||||
{
|
||||
CFunctionOverride(
|
||||
BaseContracts _base,
|
||||
OverrideFunction _function,
|
||||
bool _implemented,
|
||||
bool _virtualized,
|
||||
bool _redeclared,
|
||||
std::string _returnValue
|
||||
)
|
||||
{
|
||||
m_function = std::make_pair(_base, std::move(_function));
|
||||
m_implemented = _implemented;
|
||||
m_virtualized = _virtualized;
|
||||
m_redeclared = _redeclared;
|
||||
m_returnValue = _returnValue;
|
||||
}
|
||||
|
||||
enum class CFunctionOverrideType
|
||||
{
|
||||
INTERFACE,
|
||||
CONTRACT
|
||||
};
|
||||
|
||||
std::string str() const;
|
||||
|
||||
std::string name() const;
|
||||
|
||||
CFunctionOverrideType functionType() const;
|
||||
|
||||
bool interfaceFunction() const;
|
||||
|
||||
bool contractFunction() const;
|
||||
|
||||
SolFunctionVisibility visibility() const;
|
||||
|
||||
SolFunctionStateMutability mutability() const;
|
||||
|
||||
std::string commaSeparatedBaseNames() const;
|
||||
|
||||
std::string baseName() const;
|
||||
|
||||
/// Overridden function
|
||||
OverrideCFunction m_function;
|
||||
/// Flag that is true if overridden function is implemented
|
||||
bool m_implemented = true;
|
||||
/// Flag that is true if overridden function is marked virtual
|
||||
bool m_virtualized = false;
|
||||
/// Flag that is true if overridden function is redeclared but not implemented
|
||||
bool m_redeclared = false;
|
||||
/// The uint value to be returned by the overridden function if it is
|
||||
/// implemented
|
||||
std::string m_returnValue;
|
||||
|
||||
bool implemented() const
|
||||
{
|
||||
return m_implemented;
|
||||
}
|
||||
|
||||
bool virtualized() const
|
||||
{
|
||||
return m_virtualized;
|
||||
}
|
||||
|
||||
bool redeclared() const
|
||||
{
|
||||
return m_redeclared;
|
||||
}
|
||||
|
||||
std::string returnValue() const
|
||||
{
|
||||
return m_returnValue;
|
||||
}
|
||||
};
|
||||
|
||||
struct IFunctionOverride
|
||||
{
|
||||
IFunctionOverride(
|
||||
std::vector<std::shared_ptr<SolInterface const>> _base,
|
||||
std::unique_ptr<SolInterfaceFunction const> _function,
|
||||
bool _implement,
|
||||
bool _virtual,
|
||||
bool _redeclare,
|
||||
std::string _returnValue = ""
|
||||
)
|
||||
{
|
||||
m_function = std::make_pair(_base, std::move(_function));
|
||||
m_implemented = _implement;
|
||||
m_virtualized = _virtual;
|
||||
m_redeclared = _redeclare;
|
||||
m_returnValue = _returnValue;
|
||||
}
|
||||
|
||||
std::string str() const;
|
||||
|
||||
bool implemented() const
|
||||
{
|
||||
return m_implemented;
|
||||
}
|
||||
|
||||
bool virtualized() const
|
||||
{
|
||||
return m_virtualized;
|
||||
}
|
||||
|
||||
bool redeclared() const
|
||||
{
|
||||
return m_redeclared;
|
||||
}
|
||||
|
||||
std::string returnValue() const
|
||||
{
|
||||
return m_returnValue;
|
||||
}
|
||||
|
||||
OverrideIFunction m_function;
|
||||
/// Flag that is true if overridden function is implemented in derived contract
|
||||
bool m_implemented = false;
|
||||
/// Flag that is true if overridden function implemented in derived contract is
|
||||
/// marked virtual
|
||||
bool m_virtualized = false;
|
||||
/// Flag that is true if overridden function is redeclared but not implemented
|
||||
bool m_redeclared = false;
|
||||
/// The uint value to be returned if the overridden interface function is implemented
|
||||
std::string m_returnValue;
|
||||
};
|
||||
}
|
@ -9,13 +9,16 @@ SolidityCompilationFramework::SolidityCompilationFramework(langutil::EVMVersion
|
||||
|
||||
solidity::bytes SolidityCompilationFramework::compileContract(
|
||||
std::string const& _sourceCode,
|
||||
std::string const& _contractName
|
||||
std::string const& _contractName,
|
||||
std::map<std::string, solidity::util::h160> const& _libraryAddresses,
|
||||
frontend::OptimiserSettings _optimization
|
||||
)
|
||||
{
|
||||
std::string sourceCode = _sourceCode;
|
||||
m_compiler.setSources({{"", sourceCode}});
|
||||
m_compiler.setLibraries(_libraryAddresses);
|
||||
m_compiler.setEVMVersion(m_evmVersion);
|
||||
m_compiler.setOptimiserSettings(m_optimiserSettings);
|
||||
m_compiler.setOptimiserSettings(_optimization);
|
||||
if (!m_compiler.compile())
|
||||
{
|
||||
langutil::SourceReferenceFormatter formatter(std::cerr);
|
||||
|
@ -23,12 +23,13 @@ public:
|
||||
}
|
||||
bytes compileContract(
|
||||
std::string const& _sourceCode,
|
||||
std::string const& _contractName = {}
|
||||
std::string const& _contractName,
|
||||
std::map<std::string, solidity::util::h160> const& _libraryAddresses = {},
|
||||
frontend::OptimiserSettings _optimization = frontend::OptimiserSettings::minimal()
|
||||
);
|
||||
protected:
|
||||
frontend::CompilerStack m_compiler;
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
frontend::OptimiserSettings m_optimiserSettings = frontend::OptimiserSettings::none();
|
||||
};
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,48 +14,29 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <test/tools/ossfuzz/solProto.pb.h>
|
||||
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace solidity::test::solprotofuzzer
|
||||
{
|
||||
struct ProgramInfo
|
||||
struct SolRandomNumGenerator
|
||||
{
|
||||
enum class ProgramType
|
||||
{
|
||||
LIBRARY,
|
||||
CONTRACT,
|
||||
INTERFACE
|
||||
};
|
||||
std::string m_name;
|
||||
std::string m_userDefinedTypes;
|
||||
ProgramType m_programType;
|
||||
};
|
||||
using RandomEngine = std::minstd_rand;
|
||||
|
||||
struct FunctionInfo
|
||||
{
|
||||
enum class FunctionVisibility
|
||||
explicit SolRandomNumGenerator(unsigned _seed): m_random(RandomEngine(_seed)) {}
|
||||
|
||||
unsigned operator()()
|
||||
{
|
||||
PUBLIC,
|
||||
PRIVATE,
|
||||
INTERNAL,
|
||||
EXTERNAL
|
||||
};
|
||||
enum class StateMutability
|
||||
{
|
||||
PURE,
|
||||
VIEW,
|
||||
PAYABLE
|
||||
};
|
||||
std::string m_name;
|
||||
std::string m_params;
|
||||
std::string m_returns;
|
||||
FunctionVisibility m_visibility;
|
||||
StateMutability m_mutability;
|
||||
bool m_override;
|
||||
bool m_virtual;
|
||||
return m_random();
|
||||
}
|
||||
|
||||
RandomEngine m_random;
|
||||
};
|
||||
|
||||
class ProtoConverter
|
||||
@ -65,82 +46,155 @@ public:
|
||||
ProtoConverter(ProtoConverter const&) = delete;
|
||||
ProtoConverter(ProtoConverter&&) = delete;
|
||||
std::string protoToSolidity(Program const&);
|
||||
private:
|
||||
using CI = std::variant<Contract const*, Interface 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:
|
||||
enum MostDerivedProgram {
|
||||
CONTRACT,
|
||||
INTERFACE,
|
||||
LIBRARY
|
||||
};
|
||||
|
||||
/// Variant type that points to one of contract, interface, library protobuf messages
|
||||
using CIL = std::variant<Contract const*, Interface const*, Library const*>;
|
||||
/// Variant type that points to one of contract, interface, library function protobuf
|
||||
/// messages
|
||||
using CILFunc = std::variant<ContractFunction const*, InterfaceFunction const*, LibraryFunction const*>;
|
||||
/// Variant type that points to one of contract, interface protobuf messages
|
||||
using CI = std::variant<Contract const*, Interface const*>;
|
||||
/// Variant type that points to one of contract, interface function protobuf messages
|
||||
using CIFunc = std::variant<ContractFunction const*, InterfaceFunction const*>;
|
||||
/// Tuple of contract or interface function variant and a boolean stating whether
|
||||
/// the function is implemented (true) or not and a second boolean stating whether
|
||||
/// the function is virtual (true) or not.
|
||||
using CITuple = std::tuple<CIFunc, bool, bool>;
|
||||
/// 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(ContractOrInterface const&);
|
||||
std::string visit(Interface const&);
|
||||
std::string visit(Interface const& _interface);
|
||||
/// Visitor for most derived interface messages.
|
||||
/// @param _interface is a const reference to interface protobuf message
|
||||
/// @returns a 3-tuple containing Solidity translation of all base contracts
|
||||
/// this interface derives from, names of all base contracts, and the Solidity
|
||||
/// translation of this interface.
|
||||
std::tuple<std::string, std::string, std::string>
|
||||
visitMostDerivedInterface(Interface const& _interface);
|
||||
std::string visit(Contract const&);
|
||||
std::string traverseOverrides(
|
||||
Interface const&,
|
||||
bool _isOverride,
|
||||
bool _implement,
|
||||
bool _inheritedByContract,
|
||||
bool _isVirtual
|
||||
/// Define overrides for most derived interface.
|
||||
std::string mostDerivedInterfaceOverrides(Interface const& _interface);
|
||||
/// Define overrides for most derived contract.
|
||||
std::string mostDerivedContractOverrides(Interface const& _interface);
|
||||
std::string traverseOverrides(Contract const&);
|
||||
std::string registerAndVisitFunction(
|
||||
CIL _program,
|
||||
CILFunc _func,
|
||||
unsigned _index,
|
||||
bool _override,
|
||||
bool _virtual,
|
||||
bool _implement
|
||||
);
|
||||
std::string traverseOverrides(
|
||||
Contract const&,
|
||||
bool _isAbstract
|
||||
std::string overrideFunction(CILFunc _function, bool _virtual, bool _implement);
|
||||
std::pair<bool, bool> contractFunctionParams(
|
||||
Contract const* _contract,
|
||||
ContractFunction const* _function
|
||||
);
|
||||
std::string visit(CILFunc _function, bool _override, bool _virtual, bool _implement);
|
||||
std::string visit(Library const&);
|
||||
std::pair<std::string, std::string> visit(Block const&);
|
||||
std::pair<std::string, std::string> visit(Statement const&);
|
||||
std::pair<std::string, std::string> visit(solidity::test::abiv2fuzzer::VarDecl const&, bool _stateVar);
|
||||
std::pair<std::string, std::string> visit(IfStmt const&);
|
||||
std::string visit(ForStmt const&);
|
||||
std::string visit(SwitchStmt const&);
|
||||
std::string visit(BreakStmt const&);
|
||||
std::string visit(ContinueStmt const&);
|
||||
std::string visit(ReturnStmt const&);
|
||||
std::pair<std::string, std::string> visit(DoStmt const&);
|
||||
std::pair<std::string, std::string> visit(WhileStmt const&);
|
||||
std::string visit(Expression const&);
|
||||
std::string visit(Literal const&);
|
||||
std::string visit(BinaryOp const&);
|
||||
std::string visit(UnaryOp const&);
|
||||
std::string visit(VarRef const&);
|
||||
std::tuple<std::string, std::string, std::string> visit(
|
||||
FunctionParamsAndReturns const& _pR,
|
||||
bool _isExternal,
|
||||
std::string _programName
|
||||
);
|
||||
std::string visit(
|
||||
InterfaceFunction const&,
|
||||
unsigned _index,
|
||||
bool _isOverride,
|
||||
std::string _programName,
|
||||
bool _implement = false,
|
||||
bool _inheritedByContract = false,
|
||||
bool _isVirtual = false
|
||||
);
|
||||
std::string visit(LibraryFunction const& _func, unsigned _index, std::string _programName);
|
||||
std::string visit(
|
||||
ContractFunction const&,
|
||||
unsigned _index,
|
||||
bool _isOverride,
|
||||
bool _isAbstractContract,
|
||||
bool _isVirtual,
|
||||
bool _isImplemented,
|
||||
std::string _programName
|
||||
);
|
||||
std::tuple<std::string, std::string, std::string> visitContractHelper(CI _cOrI, std::string _programName);
|
||||
std::string visit(Modifier const&);
|
||||
static std::string functionVisibility(ContractFunction::Visibility _vis);
|
||||
static std::string functionVisibility(LibraryFunction::Visibility _vis);
|
||||
static std::string stateMutability(ContractFunction::StateMutability _mut);
|
||||
static std::string stateMutability(LibraryFunction::StateMutability _mut);
|
||||
static std::string stateMutability(InterfaceFunction::StateMutability _mut);
|
||||
static bool disallowedContractFunction(ContractFunction const& _contractFunction, bool _isVirtual);
|
||||
std::string programName(CIL _program);
|
||||
std::string createFunctionName(CIL _program, unsigned _index);
|
||||
std::string functionName(CILFunc _function);
|
||||
void registerFunctionName(CIL _program, CILFunc _function, unsigned _index);
|
||||
std::tuple<std::string, std::string, std::string> visitProgramHelper(CIL _program);
|
||||
bool contractFunctionImplemented(Contract const* _contract, CIFunc _function);
|
||||
bool contractFunctionVirtual(Contract const* _contract, CIFunc _function);
|
||||
std::tuple<bool, bool, bool> mostDerivedContractOverrideParams();
|
||||
|
||||
std::pair<bool, bool> contractFunctionOverrideParams(
|
||||
Contract const* _base,
|
||||
Contract const* _derived,
|
||||
CIFunc _baseFunc
|
||||
);
|
||||
std::tuple<std::string, std::string, std::string> pseudoRandomLibraryTest(unsigned _randomIdx);
|
||||
|
||||
bool emptyLibrary(Library const& _library)
|
||||
{
|
||||
return _library.funcdef_size() == 0;
|
||||
}
|
||||
bool emptyLibraryTests()
|
||||
{
|
||||
return m_libraryTests.size() == 0;
|
||||
}
|
||||
|
||||
void openProgramScope(CIL _program);
|
||||
bool pseudoRandomCoinFlip()
|
||||
{
|
||||
return m_counter++ % 2 == 0;
|
||||
}
|
||||
bool mostDerivedProgramContract()
|
||||
{
|
||||
return m_mostDerivedProgram == MostDerivedProgram::CONTRACT;
|
||||
}
|
||||
bool mostDerivedProgramInterface()
|
||||
{
|
||||
return m_mostDerivedProgram == MostDerivedProgram::INTERFACE;
|
||||
}
|
||||
bool mostDerivedProgramAbstractContract()
|
||||
{
|
||||
return m_mostDerivedAbstractContract;
|
||||
}
|
||||
unsigned randomNumber();
|
||||
|
||||
#if 0
|
||||
static bool disallowedContractFunction(SolContractFunction const& _contractFunction, bool _isVirtual);
|
||||
#endif
|
||||
|
||||
unsigned m_numVars = 0;
|
||||
unsigned m_numStructs = 0;
|
||||
unsigned m_numMods = 0;
|
||||
unsigned m_numContracts = 0;
|
||||
bool m_isImplemented = false;
|
||||
unsigned m_numPrograms = 0;
|
||||
unsigned m_counter = 0;
|
||||
bool m_mostDerivedAbstractContract = false;
|
||||
bool m_libraryTest = false;
|
||||
std::shared_ptr<SolRandomNumGenerator> m_randomGen;
|
||||
|
||||
MostDerivedProgram m_mostDerivedProgram = MostDerivedProgram::CONTRACT;
|
||||
/// Map whose key is pointer to protobuf interface message
|
||||
/// and whose value is its contract name
|
||||
std::map<Interface const*, std::string> m_interfaceNameMap;
|
||||
/// Map whose key is pointer to protobuf contract message
|
||||
/// and whose value is its contract name
|
||||
std::map<Contract const*, std::string> m_contractNameMap;
|
||||
/// Map whose key is library name and whose value is the
|
||||
/// number of implemented functions in it.
|
||||
std::map<std::string, unsigned> m_libraryFuncMap;
|
||||
/// Map whose key is a const pointer to protobuf contract
|
||||
/// message and whose value is a list of 3-tuples that
|
||||
/// store a const pointer to a protobuf interface or contract
|
||||
/// function belonging to the keyed contract, a boolean flag that is
|
||||
/// true when the function is implemented, false otherwise, and
|
||||
/// a second boolean flag that is true when the function is virtualized
|
||||
/// false otherwise.
|
||||
std::map<Contract const*, std::vector<CITuple>> m_contractFunctionMap;
|
||||
/// Map whose key is a const pointer to protobuf contract, interface or
|
||||
/// library function message type and whose value is the function name
|
||||
/// assigned to it.
|
||||
std::map<CILFunc, std::string> m_functionNameMap;
|
||||
std::map<CIL, std::string> m_programNameMap;
|
||||
/// Map whose key is a const pointer to protobuf contract or interface
|
||||
/// function message type and whose value is a pair of const pointer to
|
||||
/// protobuf contract or interface it belongs to and its declaration
|
||||
/// position (which is an unsigned integer that starts from 0).
|
||||
std::map<CIFunc, std::pair<CI, unsigned>> m_functionProgramMap;
|
||||
|
||||
std::map<CI, std::vector<std::pair<CIFunc, std::string>>> m_programFunctionNameMap;
|
||||
|
||||
std::vector<std::tuple<std::string, std::string, std::string>> m_libraryTests;
|
||||
std::string m_libraryName;
|
||||
|
||||
static auto constexpr s_interfaceFunctionPrefix = "i";
|
||||
static auto constexpr s_libraryFunctionPrefix = "l";
|
||||
|
@ -17,22 +17,13 @@
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
import "abiV2Proto.proto";
|
||||
|
||||
message FunctionParamsAndReturns {
|
||||
repeated solidity.test.abiv2fuzzer.Type params = 1;
|
||||
repeated solidity.test.abiv2fuzzer.Type returns = 2;
|
||||
}
|
||||
|
||||
message InterfaceFunction {
|
||||
enum StateMutability {
|
||||
PURE = 0;
|
||||
VIEW = 1;
|
||||
PAYABLE = 2;
|
||||
}
|
||||
required FunctionParamsAndReturns paramandret = 1;
|
||||
required StateMutability mut = 2;
|
||||
required bool override = 3;
|
||||
required StateMutability mut = 1;
|
||||
}
|
||||
|
||||
message LibraryFunction {
|
||||
@ -47,11 +38,8 @@ message LibraryFunction {
|
||||
INTERNAL = 2;
|
||||
PRIVATE = 3;
|
||||
}
|
||||
required FunctionParamsAndReturns paramandret = 1;
|
||||
required Visibility vis = 2;
|
||||
required StateMutability mut = 3;
|
||||
required Block b = 4;
|
||||
optional Modifier m = 5;
|
||||
required Visibility vis = 1;
|
||||
required StateMutability mut = 2;
|
||||
}
|
||||
|
||||
message ContractFunction {
|
||||
@ -66,144 +54,25 @@ message ContractFunction {
|
||||
INTERNAL = 2;
|
||||
PRIVATE = 3;
|
||||
}
|
||||
required FunctionParamsAndReturns paramandret = 1;
|
||||
required Visibility vis = 2;
|
||||
required StateMutability mut = 3;
|
||||
required Block b = 4;
|
||||
optional Modifier m = 5;
|
||||
required bool implemented = 6;
|
||||
required bool virtualfunc = 7;
|
||||
required bool override = 8;
|
||||
}
|
||||
|
||||
message Modifier {
|
||||
repeated solidity.test.abiv2fuzzer.Type params = 1;
|
||||
required Block b = 2;
|
||||
}
|
||||
|
||||
/// Expressions
|
||||
message Expression {
|
||||
oneof expr_oneof {
|
||||
Literal lit = 1;
|
||||
BinaryOp bop = 2;
|
||||
UnaryOp uop = 3;
|
||||
VarRef ref = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message VarRef {
|
||||
required int32 varnum = 1;
|
||||
}
|
||||
|
||||
message Literal {
|
||||
oneof literal_oneof {
|
||||
bool blit = 1;
|
||||
string slit = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message BinaryOp {
|
||||
enum Operation {
|
||||
ADD = 1;
|
||||
SUB = 2;
|
||||
MUL = 3;
|
||||
DIV = 4;
|
||||
}
|
||||
required Operation op = 1;
|
||||
required Expression arg1 = 2;
|
||||
required Expression arg2 = 3;
|
||||
}
|
||||
|
||||
message UnaryOp {
|
||||
enum Operation {
|
||||
INC = 1;
|
||||
DEC = 2;
|
||||
}
|
||||
required Operation op = 1;
|
||||
required Expression arg = 2;
|
||||
}
|
||||
|
||||
/// Statements
|
||||
message ElseStmt {
|
||||
required Block statements = 1;
|
||||
}
|
||||
|
||||
message IfStmt {
|
||||
required Expression condition = 1;
|
||||
required Block statements = 2;
|
||||
optional ElseStmt else = 3;
|
||||
}
|
||||
|
||||
message ForStmt {
|
||||
required Block pre = 1;
|
||||
required Expression condition = 2;
|
||||
required Block post = 3;
|
||||
required Block body = 4;
|
||||
}
|
||||
|
||||
message CaseStmt {
|
||||
required Literal lit = 1;
|
||||
required Block block = 2;
|
||||
}
|
||||
|
||||
message SwitchStmt {
|
||||
required Expression condition = 1;
|
||||
repeated CaseStmt cases = 2;
|
||||
optional Block default = 3;
|
||||
}
|
||||
|
||||
message BreakStmt {}
|
||||
|
||||
message ContinueStmt {}
|
||||
|
||||
message ReturnStmt {
|
||||
required Expression value = 1;
|
||||
}
|
||||
|
||||
message DoStmt {
|
||||
required Expression condition = 1;
|
||||
required Block statements = 2;
|
||||
}
|
||||
|
||||
message WhileStmt {
|
||||
required Expression condition = 1;
|
||||
required Block statements = 2;
|
||||
}
|
||||
|
||||
message Statement {
|
||||
oneof stmt_oneof {
|
||||
solidity.test.abiv2fuzzer.VarDecl var = 1;
|
||||
IfStmt ifstmt = 2;
|
||||
ForStmt forstmt = 3;
|
||||
SwitchStmt switchstmt = 4;
|
||||
BreakStmt breakstmt = 5;
|
||||
ContinueStmt continuestmt = 6;
|
||||
ReturnStmt returnstmt = 7;
|
||||
DoStmt dostmt = 8;
|
||||
WhileStmt whilestmt = 9;
|
||||
}
|
||||
}
|
||||
|
||||
message Block {
|
||||
repeated Statement statements = 1;
|
||||
required Visibility vis = 1;
|
||||
required StateMutability mut = 2;
|
||||
required bool virtualfunc = 3;
|
||||
}
|
||||
|
||||
message Library {
|
||||
repeated solidity.test.abiv2fuzzer.VarDecl state_vars = 1;
|
||||
repeated LibraryFunction funcdef = 2;
|
||||
repeated LibraryFunction funcdef = 1;
|
||||
required uint64 random = 2;
|
||||
}
|
||||
|
||||
message Interface {
|
||||
repeated solidity.test.abiv2fuzzer.VarDecl state_vars = 1;
|
||||
repeated InterfaceFunction funcdef = 2;
|
||||
repeated Interface ancestors = 3;
|
||||
repeated InterfaceFunction funcdef = 1;
|
||||
repeated Interface bases = 2;
|
||||
}
|
||||
|
||||
message Contract {
|
||||
repeated solidity.test.abiv2fuzzer.VarDecl state_vars = 1;
|
||||
repeated ContractFunction funcdef = 2;
|
||||
required bool abstract = 3;
|
||||
repeated ContractOrInterface ancestors = 4;
|
||||
repeated ContractFunction funcdef = 1;
|
||||
required bool abstract = 2;
|
||||
repeated ContractOrInterface bases = 3;
|
||||
}
|
||||
|
||||
message ContractOrInterface {
|
||||
@ -221,8 +90,19 @@ message ContractType {
|
||||
}
|
||||
}
|
||||
|
||||
message Program {
|
||||
repeated ContractType contracts = 1;
|
||||
message TestContract {
|
||||
enum Type {
|
||||
LIBRARY = 0;
|
||||
CONTRACT = 1;
|
||||
}
|
||||
required Type type = 1;
|
||||
required uint64 programidx = 2;
|
||||
}
|
||||
|
||||
package solidity.test.solprotofuzzer;
|
||||
message Program {
|
||||
repeated ContractType contracts = 1;
|
||||
required TestContract test = 2;
|
||||
required uint64 seed = 3;
|
||||
}
|
||||
|
||||
package solidity.test.solprotofuzzer;
|
@ -17,16 +17,178 @@
|
||||
|
||||
#include <test/tools/ossfuzz/protoToSol.h>
|
||||
#include <test/tools/ossfuzz/solProto.pb.h>
|
||||
#include <test/tools/ossfuzz/abiV2FuzzerCommon.h>
|
||||
#include <test/EVMHost.h>
|
||||
|
||||
#include <test/tools/fuzzer_common.h>
|
||||
|
||||
#include <evmone/evmone.h>
|
||||
#include <src/libfuzzer/libfuzzer_macro.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
static evmc::VM evmone = evmc::VM{evmc_create_evmone()};
|
||||
|
||||
using namespace solidity;
|
||||
using namespace solidity::test;
|
||||
using namespace solidity::test::solprotofuzzer;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::test::abiv2fuzzer;
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
/// Compares two runs of EVMC returing true if they are
|
||||
/// equal and false otherwise.
|
||||
bool isOutputExpected(evmc::result const& _run1, evmc::result const& _run2)
|
||||
{
|
||||
if (_run1.output_size != _run2.output_size)
|
||||
return false;
|
||||
|
||||
if (getenv("SOL_DEBUG_FILE") != nullptr)
|
||||
{
|
||||
unsigned r = _run1.output_data[31] | _run1.output_data[30] << 8 |
|
||||
_run1.output_data[29] << 16 | _run1.output_data[28] << 24;
|
||||
std::cout << r << std::endl;
|
||||
}
|
||||
|
||||
return (memcmp(_run1.output_data, _run2.output_data, _run1.output_size) == 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<int64_t>::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<bytes, Json::Value> compileContract(
|
||||
std::string _sourceCode,
|
||||
std::string _contractName,
|
||||
std::map<std::string, solidity::util::h160> 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<std::string, solidity::util::h160> _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 [minimalBytecode, ids] = compileContract(
|
||||
_sourceCode,
|
||||
_contractName,
|
||||
_libraryAddressMap,
|
||||
_optimization
|
||||
);
|
||||
|
||||
return deployAndExecute(
|
||||
hostContext,
|
||||
minimalBytecode,
|
||||
ids[_methodName].asString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
{
|
||||
ProtoConverter converter;
|
||||
@ -39,6 +201,47 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
ofstream of(dump_path);
|
||||
of.write(sol_source.data(), sol_source.size());
|
||||
}
|
||||
FuzzerUtil::testCompiler(sol_source, /*optimize=*/false);
|
||||
|
||||
if (const char* 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<char>(ifstr),
|
||||
std::istreambuf_iterator<char>()
|
||||
};
|
||||
std::cout << sol_source << std::endl;
|
||||
}
|
||||
|
||||
auto minimalResult = compileDeployAndExecute(
|
||||
sol_source,
|
||||
":C",
|
||||
"test()",
|
||||
frontend::OptimiserSettings::minimal(),
|
||||
converter.libraryTest() ? converter.libraryName() : ""
|
||||
);
|
||||
|
||||
auto optResult = compileDeployAndExecute(
|
||||
sol_source,
|
||||
":C",
|
||||
"test()",
|
||||
frontend::OptimiserSettings::standard(),
|
||||
converter.libraryTest() ? converter.libraryName() : ""
|
||||
);
|
||||
|
||||
// Both executions should either return success or identical evmone status code
|
||||
bool successState = minimalResult.status_code == EVMC_SUCCESS && optResult.status_code == EVMC_SUCCESS;
|
||||
bool identicalState = minimalResult.status_code == optResult.status_code;
|
||||
bool executeState = successState || identicalState;
|
||||
solAssert(executeState, "Proto solc fuzzer: Different execution status");
|
||||
|
||||
if (successState)
|
||||
solAssert(
|
||||
isOutputExpected(minimalResult, optResult),
|
||||
"Proto solc fuzzer: Output mismatch"
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user