mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Added prelim support for contract inheritance tests and cleaned up proto converter
This commit is contained in:
parent
f63552f4ca
commit
89b5ac510b
@ -21,8 +21,6 @@
|
||||
|
||||
#include <libsolutil/Whiskers.h>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace solidity::test::solprotofuzzer::adaptor;
|
||||
@ -173,6 +171,7 @@ SolContractFunction::SolContractFunction(
|
||||
ContractFunction const& _function,
|
||||
std::string _contractName,
|
||||
std::string _functionName,
|
||||
bool _implemented,
|
||||
std::string _returnValue
|
||||
)
|
||||
{
|
||||
@ -182,6 +181,7 @@ SolContractFunction::SolContractFunction(
|
||||
m_mutability = mutabilityConverter(_function.mut());
|
||||
m_virtual = _function.virtualfunc();
|
||||
m_returnValue = _returnValue;
|
||||
m_implemented = _implemented;
|
||||
}
|
||||
|
||||
bool SolContractFunction::operator==(SolContractFunction const& _rhs) const
|
||||
@ -198,8 +198,25 @@ bool SolContractFunction::operator!=(SolContractFunction const& _rhs) const
|
||||
return name() != _rhs.name();
|
||||
}
|
||||
|
||||
bool SolContractFunction::disallowed() const
|
||||
{
|
||||
// Private virtual functions are disallowed
|
||||
if (visibility() == SolFunctionVisibility::PRIVATE && isVirtual())
|
||||
return true;
|
||||
// Private payable functions are disallowed
|
||||
else if (visibility() == SolFunctionVisibility::PRIVATE && mutability() == SolFunctionStateMutability::PAYABLE)
|
||||
return true;
|
||||
// Internal payable functions are disallowed
|
||||
else if (visibility() == SolFunctionVisibility::INTERNAL && mutability() == SolFunctionStateMutability::PAYABLE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
string SolContractFunction::str() const
|
||||
{
|
||||
if (disallowed())
|
||||
return "";
|
||||
|
||||
string bodyStr = Whiskers(R"(
|
||||
{
|
||||
return <uint>;
|
||||
@ -208,16 +225,14 @@ string SolContractFunction::str() const
|
||||
.render();
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>()<?isOverride> override</isOverride>
|
||||
<?isVirtual> virtual</isVirtual> <visibility> <stateMutability>
|
||||
function <functionName>()<?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")
|
||||
("isImplemented", implemented())
|
||||
.render();
|
||||
}
|
||||
|
||||
@ -253,16 +268,37 @@ string SolLibraryFunction::str() const
|
||||
.render();
|
||||
}
|
||||
|
||||
SolBaseContract::BaseType SolBaseContract::type() const
|
||||
{
|
||||
if (holds_alternative<shared_ptr<SolInterface>>(m_base))
|
||||
return BaseType::INTERFACE;
|
||||
else
|
||||
{
|
||||
solAssert(holds_alternative<shared_ptr<SolContract>>(m_base), "Sol proto fuzzer: Invalid base contract");
|
||||
return BaseType::CONTRACT;
|
||||
}
|
||||
}
|
||||
|
||||
string SolBaseContract::str()
|
||||
{
|
||||
switch (type())
|
||||
{
|
||||
case BaseType::INTERFACE:
|
||||
return interface()->str();
|
||||
case BaseType::CONTRACT:
|
||||
return contract()->str();
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
);
|
||||
if (holds_alternative<Contract const*>(_base))
|
||||
m_base = make_shared<SolContract>(SolContract(*get<Contract const*>(_base), _name, _prng));
|
||||
else
|
||||
{
|
||||
solAssert(holds_alternative<Interface const*>(_base), "Sol proto adaptor: Invalid base contract");
|
||||
m_base = make_shared<SolInterface>(SolInterface(*get<Interface const*>(_base), _name, _prng));
|
||||
}
|
||||
}
|
||||
|
||||
void SolInterface::overrideHelper(
|
||||
@ -314,7 +350,7 @@ void SolInterface::overrideHelper(
|
||||
// Use a pseudo-random coin flip to decide whether to override explicitly
|
||||
// or not. Implicit override means that the overridden function is not
|
||||
// redeclared with the override keyword.
|
||||
bool explicitOverride = coinFlip();
|
||||
bool explicitOverride = coinToss();
|
||||
// If function has not been overridden, add new override pseudo-randomly
|
||||
if (!multipleOverride)
|
||||
m_overrideMap.insert(
|
||||
@ -478,37 +514,49 @@ interface <programName><?inheritance> is <baseNames></inheritance> {
|
||||
|
||||
bool SolContract::validTest() const
|
||||
{
|
||||
return m_contractFunctionMap.size() > 0;
|
||||
// Check if at least one contract has one valid test function
|
||||
for (auto &c: m_contractFunctionMap)
|
||||
if (c.second.size() > 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
tuple<string, string, string> SolContract::pseudoRandomTest()
|
||||
tuple<string, string, string> SolContract::validContractTest()
|
||||
{
|
||||
solAssert(m_contractFunctionMap.size() > 0, "Sol proto adaptor: Empty contract map");
|
||||
string chosenContractName{};
|
||||
string chosenFunctionName{};
|
||||
string expectedOutput{};
|
||||
unsigned numFunctions = m_contractFunctionMap.size();
|
||||
unsigned contractIdx = randomNumber() % numFunctions;
|
||||
unsigned numContracts = m_contractFunctionMap.size();
|
||||
unsigned contractIdx = random() % numContracts;
|
||||
unsigned functionIdx = 0;
|
||||
unsigned mapIdx = 0;
|
||||
for (auto &e: m_contractFunctionMap)
|
||||
{
|
||||
if (contractIdx == mapIdx)
|
||||
{
|
||||
chosenContractName = e.first;
|
||||
functionIdx = random() % e.second.size();
|
||||
unsigned functionMapIdx = 0;
|
||||
for (auto &f: e.second)
|
||||
// Recurse if chosen contract has no valid test cases
|
||||
// We can be sure there is at least one contract with
|
||||
// a valid test case because validTest() has been
|
||||
// asserted by caller of this function.
|
||||
if (e.second.size() == 0)
|
||||
return validContractTest();
|
||||
else
|
||||
{
|
||||
if (functionIdx == functionMapIdx)
|
||||
chosenContractName = e.first;
|
||||
functionIdx = random() % e.second.size();
|
||||
unsigned functionMapIdx = 0;
|
||||
for (auto &f: e.second)
|
||||
{
|
||||
chosenFunctionName = f.first;
|
||||
expectedOutput = f.second;
|
||||
break;
|
||||
if (functionIdx == functionMapIdx)
|
||||
{
|
||||
chosenFunctionName = f.first;
|
||||
expectedOutput = f.second;
|
||||
break;
|
||||
}
|
||||
functionMapIdx++;
|
||||
}
|
||||
functionMapIdx++;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
mapIdx++;
|
||||
}
|
||||
@ -517,29 +565,261 @@ tuple<string, string, string> SolContract::pseudoRandomTest()
|
||||
return tuple(chosenContractName, chosenFunctionName, expectedOutput);
|
||||
}
|
||||
|
||||
void SolContract::overrideHelper()
|
||||
tuple<string, string, string> SolContract::pseudoRandomTest()
|
||||
{
|
||||
solAssert(validTest(), "Sol proto adaptor: No valid contract test cases");
|
||||
return validContractTest();
|
||||
}
|
||||
|
||||
void SolContract::interfaceFunctionOverride(
|
||||
std::shared_ptr<SolInterface> _base,
|
||||
std::shared_ptr<SolInterfaceFunction> _function
|
||||
)
|
||||
{
|
||||
string 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_overriddenInterfaceFunctions)
|
||||
{
|
||||
// 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() != mutability)
|
||||
assertThrow(
|
||||
false,
|
||||
langutil::FuzzerError,
|
||||
"Input specifies multiple function overrides with identical names"
|
||||
" and parameter types but different mutability."
|
||||
);
|
||||
// Should interface function be implemented: May be but if not it must be marked virtual
|
||||
// Should it be explicitly overridden: Yes
|
||||
// Should it be marked virtual: May be
|
||||
bool implement = abstract() ? coinToss() : true;
|
||||
bool virtualize = coinToss();
|
||||
if (abstract() && !implement)
|
||||
virtualize = true;
|
||||
// Add new base to list of overridden bases
|
||||
m_overriddenInterfaceFunctions[m.first].push_back(
|
||||
shared_ptr<IFunctionOverride>(
|
||||
make_shared<IFunctionOverride>(
|
||||
IFunctionOverride(
|
||||
_base,
|
||||
_function,
|
||||
this,
|
||||
implement,
|
||||
virtualize,
|
||||
true,
|
||||
newReturnValue()
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
multipleOverride = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Use a pseudo-random coin toss to decide whether to override explicitly
|
||||
// or not. Implicit override means that the overridden function is not
|
||||
// redeclared with the override keyword.
|
||||
bool explicitOverride = abstract() ? coinToss() : true;
|
||||
// If function has not been overridden, add new override pseudo-randomly
|
||||
// Should it be virtual: May be but only matters for explicit overrides
|
||||
// Should it be implemented: If non abstract, otherwise may be
|
||||
bool virtualize = explicitOverride ? coinToss() : false;
|
||||
bool implement = abstract() ? coinToss() : true;
|
||||
if (abstract() && explicitOverride && !implement)
|
||||
virtualize = true;
|
||||
if (!multipleOverride)
|
||||
m_overriddenInterfaceFunctions.insert(
|
||||
pair(
|
||||
_function,
|
||||
vector<shared_ptr<IFunctionOverride>>{
|
||||
make_shared<IFunctionOverride>(
|
||||
IFunctionOverride(
|
||||
_base,
|
||||
_function,
|
||||
this,
|
||||
implement,
|
||||
virtualize,
|
||||
explicitOverride,
|
||||
newReturnValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void SolContract::contractFunctionOverride(
|
||||
std::shared_ptr<SolContract> _base,
|
||||
std::shared_ptr<SolContractFunction> _function
|
||||
)
|
||||
{
|
||||
string functionName = _function->name();
|
||||
auto mutability = _function->mutability();
|
||||
auto visibility = _function->visibility();
|
||||
|
||||
// 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_overriddenContractFunctions)
|
||||
{
|
||||
// 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() != mutability || m.first->visibility() != visibility)
|
||||
assertThrow(
|
||||
false,
|
||||
langutil::FuzzerError,
|
||||
"Input specifies multiple contract function overrides with identical names"
|
||||
" and parameter types but different mutability and/or visibility."
|
||||
);
|
||||
/* Case 1: Base and derived are abstract
|
||||
* Case 2: Base and derived not abstract
|
||||
* Case 3: Derived abstract, base not
|
||||
* Case 4: Derived non abstract, base abstract
|
||||
*/
|
||||
bool implement = abstract() ? coinToss() : true;
|
||||
bool virtualize = coinToss();
|
||||
if (abstract() && !implement)
|
||||
virtualize = true;
|
||||
// Add new base to list of overridden bases
|
||||
m_overriddenContractFunctions[m.first].push_back(
|
||||
shared_ptr<CFunctionOverride>(
|
||||
make_shared<CFunctionOverride>(
|
||||
CFunctionOverride(
|
||||
_base,
|
||||
_function,
|
||||
this,
|
||||
implement,
|
||||
virtualize,
|
||||
true,
|
||||
newReturnValue()
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
multipleOverride = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool implement;
|
||||
if (_function->implemented())
|
||||
implement = true;
|
||||
else
|
||||
implement = abstract() ? coinToss() : true;
|
||||
bool virtualize = coinToss();
|
||||
if (!implement && abstract())
|
||||
virtualize = true;
|
||||
bool explicitOverride = true;
|
||||
if (_base->abstract() && !implement && abstract())
|
||||
explicitOverride = coinToss();
|
||||
|
||||
if (!multipleOverride)
|
||||
m_overriddenContractFunctions.insert(
|
||||
pair(
|
||||
_function,
|
||||
vector<shared_ptr<CFunctionOverride>>{
|
||||
make_shared<CFunctionOverride>(
|
||||
CFunctionOverride(
|
||||
_base,
|
||||
_function,
|
||||
this,
|
||||
implement,
|
||||
virtualize,
|
||||
explicitOverride,
|
||||
newReturnValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void SolContract::addOverrides()
|
||||
{
|
||||
|
||||
for (auto &base: m_baseContracts)
|
||||
{
|
||||
// Check if base is contract or interface
|
||||
if (base->type() == SolBaseContract::BaseType::INTERFACE)
|
||||
{
|
||||
// Override interface functions
|
||||
for (auto &f: base->interface()->m_interfaceFunctions)
|
||||
{
|
||||
interfaceFunctionOverride(base->interface(), f);
|
||||
}
|
||||
// Override interface overrides
|
||||
for (auto &m: base->interface()->m_overrideMap)
|
||||
{
|
||||
interfaceFunctionOverride(base->interface(), m.first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(base->type() == SolBaseContract::BaseType::CONTRACT, "Sol proto fuzzer: Base contract neither interface nor contract");
|
||||
// Override contract functions
|
||||
for (auto &f: base->contract()->m_contractFunctions)
|
||||
{
|
||||
contractFunctionOverride(base->contract(), f);
|
||||
}
|
||||
// Override contract overrides
|
||||
for (auto &m: base->contract()->m_overriddenContractFunctions)
|
||||
{
|
||||
for (auto &f: m.second)
|
||||
contractFunctionOverride(base->contract(), f->baseFunction());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SolContract::addFunctions(Contract const& _contract)
|
||||
{
|
||||
bool abs = abstract();
|
||||
string contractName = name();
|
||||
// Add contract to contract function map only if the contract
|
||||
// is not abstract.
|
||||
if (!abs)
|
||||
m_contractFunctionMap.insert(pair(contractName, map<string, string>{}));
|
||||
// Add functions
|
||||
for (auto &f: _contract.funcdef())
|
||||
m_contractFunctions.push_back(
|
||||
make_unique<SolContractFunction>(
|
||||
SolContractFunction(
|
||||
f,
|
||||
m_contractName,
|
||||
newFunctionName(),
|
||||
newReturnValue()
|
||||
)
|
||||
{
|
||||
auto function = make_shared<SolContractFunction>(
|
||||
SolContractFunction(
|
||||
f,
|
||||
contractName,
|
||||
newFunctionName(),
|
||||
(abs ? coinToss() : true),
|
||||
newReturnValue()
|
||||
)
|
||||
);
|
||||
m_contractFunctions.push_back(function);
|
||||
// If contract is not abstract, add its public and external
|
||||
// functions to contract function map.
|
||||
if (!abs)
|
||||
{
|
||||
auto visibility = function->visibility();
|
||||
string functionName = function->name();
|
||||
string expectedOutput = function->returnValue();
|
||||
// Register only public and external contract functions because only they can
|
||||
// be called from a different contract.
|
||||
if (visibility == SolFunctionVisibility::PUBLIC || visibility == SolFunctionVisibility::EXTERNAL)
|
||||
{
|
||||
solAssert(!m_contractFunctionMap[contractName].count(functionName), "Sol proto adaptor: Duplicate contract function");
|
||||
m_contractFunctionMap[contractName].insert(pair(functionName, expectedOutput));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SolContract::addBases(Contract const& _contract)
|
||||
@ -550,14 +830,14 @@ void SolContract::addBases(Contract const& _contract)
|
||||
{
|
||||
case ContractOrInterface::kC:
|
||||
m_baseContracts.push_back(
|
||||
make_unique<SolBaseContract>(
|
||||
make_shared<SolBaseContract>(
|
||||
SolBaseContract(&b.c(), newBaseName(), m_prng)
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ContractOrInterface::kI:
|
||||
m_baseContracts.push_back(
|
||||
make_unique<SolBaseContract>(
|
||||
make_shared<SolBaseContract>(
|
||||
SolBaseContract(&b.i(), newBaseName(), m_prng)
|
||||
)
|
||||
);
|
||||
@ -568,9 +848,135 @@ void SolContract::addBases(Contract const& _contract)
|
||||
}
|
||||
}
|
||||
|
||||
string SolContract::contractOverrideStr() const
|
||||
{
|
||||
ostringstream overriddenFunctions;
|
||||
for (auto &f: m_overriddenContractFunctions)
|
||||
{
|
||||
string bodyStr = Whiskers(R"(
|
||||
{
|
||||
return <uint>;
|
||||
})")
|
||||
("uint", f.second[0]->returnValue())
|
||||
.render();
|
||||
|
||||
bool implemented = f.second[0]->implemented();
|
||||
bool virtualized = f.second[0]->virtualized();
|
||||
|
||||
ostringstream overriddenBaseNames;
|
||||
if (f.second.size() > 1)
|
||||
{
|
||||
string sep{};
|
||||
for (auto &b: f.second)
|
||||
{
|
||||
overriddenBaseNames << Whiskers(R"(<sep><name>)")
|
||||
("sep", sep)
|
||||
("name", b->baseName())
|
||||
.render();
|
||||
if (sep.empty())
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assertThrow(
|
||||
f.second.size() == 1,
|
||||
langutil::FuzzerError,
|
||||
"Inconsistent override map"
|
||||
);
|
||||
if (!f.second[0]->explicitlyInherited())
|
||||
continue;
|
||||
}
|
||||
overriddenFunctions << Whiskers(R"(
|
||||
function <functionName>() <visibility> <stateMutability><?isVirtual> virtual</isVirtual>
|
||||
override<?multiple>(<baseNames>)</multiple> returns (uint)</isImplemented><body><!isImplemented>;</isImplemented>)")
|
||||
("functionName", f.first->name())
|
||||
("visibility", functionVisibility(f.first->visibility()))
|
||||
("stateMutability", functionMutability(f.first->mutability()))
|
||||
("isVirtual", virtualized)
|
||||
("multiple", f.second.size() > 1)
|
||||
("baseNames", overriddenBaseNames.str())
|
||||
("isImplemented", implemented)
|
||||
("body", bodyStr)
|
||||
.render();
|
||||
}
|
||||
return overriddenFunctions.str();
|
||||
}
|
||||
|
||||
string SolContract::interfaceOverrideStr() const
|
||||
{
|
||||
ostringstream overriddenFunctions;
|
||||
for (auto &f: m_overriddenInterfaceFunctions)
|
||||
{
|
||||
string bodyStr = Whiskers(R"(
|
||||
{
|
||||
return <uint>;
|
||||
})")
|
||||
("uint", f.second[0]->returnValue())
|
||||
.render();
|
||||
|
||||
bool implemented = f.second[0]->implemented();
|
||||
|
||||
ostringstream overriddenBaseNames;
|
||||
if (f.second.size() > 1)
|
||||
{
|
||||
string sep{};
|
||||
for (auto &b: f.second)
|
||||
{
|
||||
overriddenBaseNames << Whiskers(R"(<sep><name>)")
|
||||
("sep", sep)
|
||||
("name", b->baseName())
|
||||
.render();
|
||||
if (sep.empty())
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assertThrow(
|
||||
f.second.size() == 1,
|
||||
langutil::FuzzerError,
|
||||
"Inconsistent override map"
|
||||
);
|
||||
if (!f.second[0]->explicitlyInherited())
|
||||
continue;
|
||||
}
|
||||
overriddenFunctions << Whiskers(R"(
|
||||
function <functionName>() external <stateMutability>
|
||||
override<?multiple>(<baseNames>)</multiple> returns (uint)</isImplemented><body><!isImplemented>;</isImplemented>)")
|
||||
("functionName", f.first->name())
|
||||
("stateMutability", functionMutability(f.first->mutability()))
|
||||
("multiple", f.second.size() > 1)
|
||||
("baseNames", overriddenBaseNames.str())
|
||||
("isImplemented", implemented)
|
||||
("body", bodyStr)
|
||||
.render();
|
||||
}
|
||||
return overriddenFunctions.str();
|
||||
}
|
||||
|
||||
string SolContract::str() const
|
||||
{
|
||||
return "";
|
||||
ostringstream bases;
|
||||
for (auto &b: m_baseContracts)
|
||||
bases << b->str();
|
||||
|
||||
ostringstream functions;
|
||||
for (auto &f: m_contractFunctions)
|
||||
functions << f->str();
|
||||
|
||||
functions << interfaceOverrideStr() << contractOverrideStr();
|
||||
|
||||
return Whiskers(R"(
|
||||
<bases>
|
||||
<?isAbstract>abstract </isAbstract>contract <contractName> {
|
||||
<functions>
|
||||
})")
|
||||
("bases", bases.str())
|
||||
("isAbstract", abstract())
|
||||
("contractName", name())
|
||||
("functions", functions.str())
|
||||
.render();
|
||||
}
|
||||
|
||||
SolContract::SolContract(
|
||||
@ -662,50 +1068,25 @@ pair<string, string> SolLibrary::pseudoRandomTest()
|
||||
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;
|
||||
return m_baseFunction->name();
|
||||
}
|
||||
|
||||
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;
|
||||
return m_baseFunction->visibility();
|
||||
}
|
||||
|
||||
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();
|
||||
return m_baseFunction->mutability();
|
||||
}
|
||||
|
||||
string CFunctionOverride::str() const
|
||||
{
|
||||
solAssert(virtualized() || !implemented(), "Sol proto fuzzer: Invalid virtualization of contract function override");
|
||||
|
||||
string bodyStr = Whiskers(R"(
|
||||
{
|
||||
return <uint>;
|
||||
@ -717,7 +1098,7 @@ string CFunctionOverride::str() const
|
||||
function <functionName>() override <?isVirtual> virtual</isVirtual> <visibility> <stateMutability>
|
||||
returns (uint)<?isImplemented><body><!isImplemented>;</isImplemented>)")
|
||||
("functionName", name())
|
||||
("isVirtual", virtualized() || !implemented())
|
||||
("isVirtual", virtualized())
|
||||
("visibility", functionVisibility(visibility()))
|
||||
("stateMutability", functionMutability(mutability()))
|
||||
("body", bodyStr)
|
||||
@ -725,37 +1106,10 @@ string CFunctionOverride::str() const
|
||||
.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();
|
||||
//}
|
||||
string CFunctionOverride::baseName() const
|
||||
{
|
||||
return m_baseContract->name();
|
||||
}
|
||||
|
||||
IFunctionOverride::IFunctionOverride(
|
||||
std::shared_ptr<SolInterface const> _baseInterface,
|
||||
|
@ -115,10 +115,12 @@ struct SolContractFunction
|
||||
ContractFunction const& _function,
|
||||
std::string _contractName,
|
||||
std::string _functionName,
|
||||
bool _implement,
|
||||
std::string _returnValue
|
||||
);
|
||||
bool operator==(SolContractFunction const& _rhs) const;
|
||||
bool operator!=(SolContractFunction const& _rhs) const;
|
||||
bool disallowed() const;
|
||||
std::string str() const;
|
||||
|
||||
std::string name() const
|
||||
@ -133,6 +135,10 @@ struct SolContractFunction
|
||||
{
|
||||
return m_virtual;
|
||||
}
|
||||
bool implemented() const
|
||||
{
|
||||
return m_implemented;
|
||||
}
|
||||
std::string returnValue() const
|
||||
{
|
||||
return m_returnValue;
|
||||
@ -152,6 +158,7 @@ struct SolContractFunction
|
||||
SolFunctionStateMutability m_mutability = SolFunctionStateMutability::PURE;
|
||||
bool m_virtual = false;
|
||||
std::string m_returnValue;
|
||||
bool m_implemented = true;
|
||||
};
|
||||
|
||||
struct SolLibraryFunction
|
||||
@ -235,9 +242,28 @@ struct SolLibrary
|
||||
|
||||
struct SolBaseContract
|
||||
{
|
||||
enum BaseType
|
||||
{
|
||||
INTERFACE,
|
||||
CONTRACT
|
||||
};
|
||||
|
||||
SolBaseContract(ProtoBaseContract _base, std::string _name, std::shared_ptr<SolRandomNumGenerator> _prng);
|
||||
|
||||
BaseContracts m_base;
|
||||
std::variant<std::vector<std::shared_ptr<SolContractFunction>>, std::vector<std::shared_ptr<SolInterfaceFunction>>>
|
||||
baseFunctions();
|
||||
BaseType type() const;
|
||||
std::string str();
|
||||
std::shared_ptr<SolInterface> interface()
|
||||
{
|
||||
return std::get<std::shared_ptr<SolInterface>>(m_base);
|
||||
}
|
||||
std::shared_ptr<SolContract> contract()
|
||||
{
|
||||
return std::get<std::shared_ptr<SolContract>>(m_base);
|
||||
}
|
||||
|
||||
std::variant<std::shared_ptr<SolInterface>, std::shared_ptr<SolContract>> m_base;
|
||||
std::string m_baseName;
|
||||
std::shared_ptr<SolRandomNumGenerator> m_prng;
|
||||
};
|
||||
@ -247,17 +273,33 @@ struct SolContract
|
||||
SolContract(Contract const& _contract, std::string _name, std::shared_ptr<SolRandomNumGenerator> _prng);
|
||||
|
||||
std::string str() const;
|
||||
std::string interfaceOverrideStr() const;
|
||||
std::string contractOverrideStr() const;
|
||||
void addFunctions(Contract const& _contract);
|
||||
void addBases(Contract const& _contract);
|
||||
void addOverrides();
|
||||
void overrideHelper();
|
||||
void interfaceFunctionOverride(
|
||||
std::shared_ptr<SolInterface> _base,
|
||||
std::shared_ptr<SolInterfaceFunction> _function
|
||||
);
|
||||
void contractFunctionOverride(
|
||||
std::shared_ptr<SolContract> _base,
|
||||
std::shared_ptr<SolContractFunction> _function
|
||||
);
|
||||
|
||||
bool validTest() const;
|
||||
std::tuple<std::string, std::string, std::string> validContractTest();
|
||||
std::tuple<std::string, std::string, std::string> pseudoRandomTest();
|
||||
|
||||
unsigned randomNumber() const
|
||||
{
|
||||
return m_prng->operator()();
|
||||
}
|
||||
bool coinToss() const
|
||||
{
|
||||
return randomNumber() % 2 == 0;
|
||||
}
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
return m_contractName;
|
||||
@ -295,7 +337,8 @@ struct SolContract
|
||||
std::string m_lastBaseName;
|
||||
std::vector<std::shared_ptr<SolContractFunction>> m_contractFunctions;
|
||||
std::vector<std::shared_ptr<SolBaseContract>> m_baseContracts;
|
||||
std::vector<std::shared_ptr<CFunctionOverride>> m_overriddenFunctions;
|
||||
std::map<std::shared_ptr<SolContractFunction>, std::vector<std::shared_ptr<CFunctionOverride>>> m_overriddenContractFunctions;
|
||||
std::map<std::shared_ptr<SolInterfaceFunction>, std::vector<std::shared_ptr<IFunctionOverride>>> m_overriddenInterfaceFunctions;
|
||||
/// Maps non abstract contract name to list of publicly exposed function name
|
||||
/// and their expected output
|
||||
std::map<std::string, std::map<std::string, std::string>> m_contractFunctionMap;
|
||||
@ -320,7 +363,7 @@ struct SolInterface
|
||||
return m_prng->operator()();
|
||||
}
|
||||
|
||||
bool coinFlip() const
|
||||
bool coinToss() const
|
||||
{
|
||||
return randomNumber() % 2 == 0;
|
||||
}
|
||||
@ -396,42 +439,36 @@ struct SolInterface
|
||||
|
||||
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 DerivedType
|
||||
{
|
||||
ABSTRACTCONTRACT,
|
||||
CONTRACT
|
||||
};
|
||||
|
||||
enum class CFunctionOverrideType
|
||||
CFunctionOverride(
|
||||
std::shared_ptr<SolContract> _base,
|
||||
std::shared_ptr<SolContractFunction> _function,
|
||||
SolContract* _derived,
|
||||
bool _implemented,
|
||||
bool _virtualized,
|
||||
bool _explicitInheritance,
|
||||
std::string _returnValue
|
||||
)
|
||||
{
|
||||
INTERFACE,
|
||||
CONTRACT
|
||||
};
|
||||
m_baseContract = _base;
|
||||
m_baseFunction = _function;
|
||||
m_derivedProgram = _derived;
|
||||
m_implemented = _implemented;
|
||||
m_virtualized = _virtualized;
|
||||
m_explicitlyInherited = _explicitInheritance;
|
||||
m_returnValue = _returnValue;
|
||||
m_derivedType = _derived->abstract() ? DerivedType::ABSTRACTCONTRACT : DerivedType::CONTRACT;
|
||||
}
|
||||
|
||||
std::string str() const;
|
||||
|
||||
std::string name() const;
|
||||
|
||||
CFunctionOverrideType functionType() const;
|
||||
|
||||
bool interfaceFunction() const;
|
||||
|
||||
bool contractFunction() const;
|
||||
|
||||
SolFunctionVisibility visibility() const;
|
||||
@ -442,17 +479,30 @@ struct CFunctionOverride
|
||||
|
||||
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
|
||||
std::shared_ptr<SolContract> baseContract() const
|
||||
{
|
||||
return m_baseContract;
|
||||
}
|
||||
|
||||
std::shared_ptr<SolContractFunction> baseFunction() const
|
||||
{
|
||||
return m_baseFunction;
|
||||
}
|
||||
|
||||
std::shared_ptr<SolContract> m_baseContract;
|
||||
std::shared_ptr<SolContractFunction> m_baseFunction;
|
||||
SolContract* m_derivedProgram;
|
||||
|
||||
/// 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 by the overridden function if it is
|
||||
/// implemented
|
||||
bool m_explicitlyInherited = false;
|
||||
/// The uint value to be returned if the overridden interface function is implemented
|
||||
std::string m_returnValue;
|
||||
DerivedType m_derivedType;
|
||||
|
||||
bool implemented() const
|
||||
{
|
||||
@ -464,9 +514,9 @@ struct CFunctionOverride
|
||||
return m_virtualized;
|
||||
}
|
||||
|
||||
bool redeclared() const
|
||||
bool explicitlyInherited() const
|
||||
{
|
||||
return m_redeclared;
|
||||
return m_explicitlyInherited;
|
||||
}
|
||||
|
||||
std::string returnValue() const
|
||||
@ -518,6 +568,21 @@ struct IFunctionOverride
|
||||
std::string interfaceStr() const;
|
||||
std::string contractStr() const;
|
||||
|
||||
void setImplement()
|
||||
{
|
||||
m_implemented = true;
|
||||
}
|
||||
|
||||
void setVirtual()
|
||||
{
|
||||
m_virtualized = true;
|
||||
}
|
||||
|
||||
void setExplicitInherit()
|
||||
{
|
||||
m_explicitlyInherited = true;
|
||||
}
|
||||
|
||||
bool implemented() const
|
||||
{
|
||||
return m_implemented;
|
||||
|
@ -76,7 +76,6 @@ string ProtoConverter::visit(TestContract const& _testContract)
|
||||
testCode = Whiskers(R"(
|
||||
return 0;)")
|
||||
.render();
|
||||
#if 0
|
||||
else
|
||||
{
|
||||
auto testTuple = pseudoRandomContractTest();
|
||||
@ -90,7 +89,6 @@ string ProtoConverter::visit(TestContract const& _testContract)
|
||||
("expectedOutput", get<2>(testTuple))
|
||||
.render();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@ -146,8 +144,7 @@ string ProtoConverter::visit(ContractType const& _contractType)
|
||||
case ContractType::kC:
|
||||
m_mostDerivedAbstractContract = _contractType.c().abstract();
|
||||
m_mostDerivedProgram = MostDerivedProgram::CONTRACT;
|
||||
// return visit(_contractType.c());
|
||||
return "";
|
||||
return visit(_contractType.c());
|
||||
case ContractType::kL:
|
||||
m_mostDerivedProgram = MostDerivedProgram::LIBRARY;
|
||||
return visit(_contractType.l());
|
||||
@ -159,45 +156,6 @@ string ProtoConverter::visit(ContractType const& _contractType)
|
||||
}
|
||||
}
|
||||
|
||||
string ProtoConverter::visit(ContractOrInterface const& _contractOrInterface)
|
||||
{
|
||||
switch (_contractOrInterface.contract_or_interface_oneof_case())
|
||||
{
|
||||
case ContractOrInterface::kC:
|
||||
// return visit(_contractOrInterface.c());
|
||||
return "";
|
||||
case ContractOrInterface::kI:
|
||||
// return visit(_contractOrInterface.i());
|
||||
return "";
|
||||
case ContractOrInterface::CONTRACT_OR_INTERFACE_ONEOF_NOT_SET:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool ProtoConverter::contractFunctionImplemented(
|
||||
Contract const* _contract,
|
||||
CIFunc _function
|
||||
)
|
||||
{
|
||||
auto v = m_contractFunctionMap[_contract];
|
||||
for (auto &e: v)
|
||||
if (get<0>(e) == _function)
|
||||
return get<1>(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProtoConverter::contractFunctionVirtual(
|
||||
Contract const* _contract,
|
||||
CIFunc _function
|
||||
)
|
||||
{
|
||||
auto v = m_contractFunctionMap[_contract];
|
||||
for (auto &e: v)
|
||||
if (get<0>(e) == _function)
|
||||
return get<2>(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
string ProtoConverter::mostDerivedInterfaceOverrides(Interface const& _interface)
|
||||
{
|
||||
@ -721,23 +679,6 @@ unsigned ProtoConverter::randomNumber()
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool ProtoConverter::disallowedContractFunction(SolContractFunction const& _contractFunction, bool _isVirtual)
|
||||
{
|
||||
string visibility = functionVisibility(_contractFunction.m_visibility);
|
||||
string mutability = functionMutability(_contractFunction.m_mutability);
|
||||
|
||||
// Private virtual functions are disallowed
|
||||
if (visibility == "private" && _isVirtual)
|
||||
return true;
|
||||
// Private payable functions are disallowed
|
||||
else if (visibility == "private" && mutability == "payable")
|
||||
return true;
|
||||
// Internal payable functions are disallowed
|
||||
else if (visibility == "internal" && mutability == "payable")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
string ProtoConverter::functionName(CILFunc _function)
|
||||
{
|
||||
solAssert(m_functionNameMap.count(_function), "Sol proto fuzzer: Unregistered function");
|
||||
|
@ -77,50 +77,10 @@ private:
|
||||
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& _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&);
|
||||
/// 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 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::string visit(Library const& _library);
|
||||
std::string visit(Contract const& _contract);
|
||||
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();
|
||||
std::tuple<std::string, std::string, std::string> pseudoRandomContractTest();
|
||||
|
||||
@ -156,10 +116,6 @@ private:
|
||||
}
|
||||
unsigned randomNumber();
|
||||
|
||||
#if 0
|
||||
static bool disallowedContractFunction(SolContractFunction const& _contractFunction, bool _isVirtual);
|
||||
#endif
|
||||
|
||||
unsigned m_numPrograms = 0;
|
||||
unsigned m_counter = 0;
|
||||
bool m_mostDerivedAbstractContract = false;
|
||||
@ -167,15 +123,6 @@ private:
|
||||
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
|
||||
@ -189,21 +136,9 @@ private:
|
||||
/// 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::vector<std::tuple<std::string, std::string, std::string>> m_contractTests;
|
||||
std::string m_libraryName;
|
||||
|
||||
static auto constexpr s_interfaceFunctionPrefix = "i";
|
||||
static auto constexpr s_libraryFunctionPrefix = "l";
|
||||
static auto constexpr s_contractFunctionPrefix = "c";
|
||||
static auto constexpr s_functionPrefix = "func";
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user