Made proto spec a bit leaner and started work on contracts

This commit is contained in:
Bhargava Shastry 2020-04-11 23:15:09 +02:00
parent 3ef6e09258
commit 515939670d
5 changed files with 147 additions and 72 deletions

View File

@ -186,12 +186,16 @@ SolContractFunction::SolContractFunction(
bool SolContractFunction::operator==(SolContractFunction const& _rhs) const
{
return this->m_visibility == _rhs.m_visibility && this->m_mutability == _rhs.m_mutability;
// TODO: Consider function parameters in addition to name once they are
// implemented.
return name() == _rhs.name();
}
bool SolContractFunction::operator!=(SolContractFunction const& _rhs) const
{
return this->m_visibility != _rhs.m_visibility || this->m_mutability != _rhs.m_mutability;
// TODO: Consider function parameters in addition to name once they are
// implemented.
return name() != _rhs.name();
}
string SolContractFunction::str() const
@ -352,9 +356,6 @@ void SolInterface::addBases(Interface const& _interface)
{
auto base = make_shared<SolInterface>(SolInterface(b, newBaseName(), m_prng));
m_baseInterfaces.push_back(base);
#if 0
cout << "Added " << base->name() << " as base" << endl;
#endif
// Worst case, we override all base functions so we
// increment derived contract's function index by
// this amount.
@ -378,9 +379,6 @@ void SolInterface::addFunctions(Interface const& _interface)
SolInterface::SolInterface(Interface const& _interface, string _name, shared_ptr<SolRandomNumGenerator> _prng)
{
#if 0
cout << "Constructing " << _name << endl;
#endif
m_prng = _prng;
m_interfaceName = _name;
m_lastBaseName = m_interfaceName;
@ -430,8 +428,16 @@ string SolInterface::overrideStr() const
sep = ", ";
}
}
else if (coinFlip())
continue;
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);)")
("functionName", f.first->name())
@ -470,11 +476,18 @@ interface <programName><?inheritance> is <baseNames></inheritance> {
.render();
}
SolContract::SolContract(Contract const& _contract, std::string _name, shared_ptr<SolRandomNumGenerator> _prng)
void SolContract::overrideHelper()
{
}
void SolContract::addOverrides()
{
}
void SolContract::addFunctions(Contract const& _contract)
{
m_prng = _prng;
m_contractName = _name;
m_abstract = _contract.abstract();
for (auto &f: _contract.funcdef())
m_contractFunctions.push_back(
make_unique<SolContractFunction>(
@ -486,6 +499,10 @@ SolContract::SolContract(Contract const& _contract, std::string _name, shared_pt
)
)
);
}
void SolContract::addBases(Contract const& _contract)
{
for (auto &b: _contract.bases())
{
switch (b.contract_or_interface_oneof_case())
@ -493,14 +510,14 @@ SolContract::SolContract(Contract const& _contract, std::string _name, shared_pt
case ContractOrInterface::kC:
m_baseContracts.push_back(
make_unique<SolBaseContract>(
SolBaseContract(&b.c(), newContractBaseName(), m_prng)
SolBaseContract(&b.c(), newBaseName(), m_prng)
)
);
break;
case ContractOrInterface::kI:
m_baseContracts.push_back(
make_unique<SolBaseContract>(
SolBaseContract(&b.i(), newInterfaceBaseName(), m_prng)
SolBaseContract(&b.i(), newBaseName(), m_prng)
)
);
break;
@ -510,6 +527,25 @@ SolContract::SolContract(Contract const& _contract, std::string _name, shared_pt
}
}
string SolContract::str() const
{
return "";
}
SolContract::SolContract(
Contract const& _contract,
std::string _name,
shared_ptr<SolRandomNumGenerator> _prng
)
{
m_prng = _prng;
m_contractName = _name;
m_abstract = _contract.abstract();
addBases(_contract);
addOverrides();
addFunctions(_contract);
}
void SolLibrary::addFunction(LibraryFunction const& _function)
{
// Register function name and return value
@ -536,9 +572,10 @@ void SolLibrary::addFunction(LibraryFunction const& _function)
);
}
SolLibrary::SolLibrary(Library const& _library, string _name)
SolLibrary::SolLibrary(Library const& _library, string _name, shared_ptr<SolRandomNumGenerator> _prng)
{
m_libraryName = _name;
m_prng = _prng;
for (LibraryFunction const& f: _library.funcdef())
addFunction(f);
}
@ -564,17 +601,20 @@ bool SolLibrary::validTest() const
return m_publicFunctionMap.size() > 0;
}
pair<string, string> SolLibrary::pseudoRandomTest(unsigned _randomIdx)
pair<string, string> SolLibrary::pseudoRandomTest()
{
solAssert(m_publicFunctionMap.size() > 0, "Sol proto adaptor: Empty library map");
string chosenFunction;
unsigned numFunctions = m_publicFunctionMap.size();
unsigned functionIndex = _randomIdx % numFunctions;
unsigned functionIndex = randomNumber() % numFunctions;
unsigned mapIdx = 0;
for (auto &e: m_publicFunctionMap)
{
if (functionIndex == mapIdx)
{
chosenFunction = e.first;
break;
}
mapIdx++;
}
solAssert(m_publicFunctionMap.count(chosenFunction), "Sol proto adaptor: Invalid library function chosen");

View File

@ -194,7 +194,7 @@ struct SolLibraryFunction
struct SolLibrary
{
SolLibrary(Library const& _library, std::string _name);
SolLibrary(Library const& _library, std::string _name, std::shared_ptr<SolRandomNumGenerator> _prng);
std::vector<std::unique_ptr<SolLibraryFunction>> m_functions;
/// Maps publicly exposed function name to expected output
std::map<std::string, std::string> m_publicFunctionMap;
@ -202,13 +202,15 @@ struct SolLibrary
void addFunction(LibraryFunction const& _function);
bool validTest() const;
unsigned randomNumber() const
{
return m_prng->operator()();
}
/// 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::pair<std::string, std::string> pseudoRandomTest();
std::string str() const;
@ -228,6 +230,7 @@ struct SolLibrary
std::string m_libraryName;
unsigned m_functionIndex = 0;
unsigned m_returnValue = 0;
std::shared_ptr<SolRandomNumGenerator> m_prng;
};
struct SolBaseContract
@ -244,8 +247,14 @@ struct SolContract
SolContract(Contract const& _contract, std::string _name, std::shared_ptr<SolRandomNumGenerator> _prng);
std::string str() const;
void addFunctions(Contract const& _contract);
void addBases(Contract const& _contract);
void addOverrides();
void overrideHelper();
bool validTest();
std::tuple<std::string, std::string, std::string> pseudoRandomTest();
unsigned randomNumber()
unsigned randomNumber() const
{
return m_prng->operator()();
}
@ -253,27 +262,27 @@ struct SolContract
{
return m_contractName;
}
bool abstract() const
{
return m_abstract;
}
std::string newFunctionName()
{
return "f" + std::to_string(m_functionIndex++);
}
std::string newContractBaseName()
unsigned functionIndex() const
{
return name() + "B" + std::to_string(m_baseIndex++);
return m_functionIndex;
}
std::string newInterfaceBaseName()
std::string newBaseName()
{
return "IB" + std::to_string(m_baseIndex++);
m_lastBaseName += "B";
return m_lastBaseName;
}
std::string lastBaseName() const
{
return m_lastBaseName;
}
std::string newReturnValue()
{
return std::to_string(m_returnValue++);
@ -281,12 +290,12 @@ struct SolContract
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::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::shared_ptr<SolRandomNumGenerator> m_prng;
};
@ -375,6 +384,13 @@ struct SolInterface
std::shared_ptr<SolRandomNumGenerator> m_prng;
};
/* Contract functions may be overridden by other contracts. Base and derived contracts
* may either be abstract or non-abstract. That gives us four possibilities:
* - both abstract
* - both non abstract
* - one of them abstract, the other non abstract
*/
struct CFunctionOverride
{
CFunctionOverride(
@ -393,6 +409,12 @@ struct CFunctionOverride
m_returnValue = _returnValue;
}
enum class DerivedType
{
ABSTRACTCONTRACT,
CONTRACT
};
enum class CFunctionOverrideType
{
INTERFACE,

View File

@ -54,7 +54,7 @@ string ProtoConverter::visit(TestContract const& _testContract)
else
{
m_libraryTest = true;
auto testTuple = pseudoRandomLibraryTest(_testContract.programidx());
auto testTuple = pseudoRandomLibraryTest();
m_libraryName = get<0>(testTuple);
usingLibDecl = Whiskers(R"(
using <libraryName> for uint;)")
@ -72,20 +72,24 @@ string ProtoConverter::visit(TestContract const& _testContract)
break;
}
case TestContract::CONTRACT:
#if 0
testCode = Whiskers(R"(
<contractName> testContract = new <contractName>();
if (testContract.<testFunction>() != <expectedOutput>)
return 1;
return 0;)")
("contractName", "")
("testFunction", "")
("expectedOutput", "")
.render();
#else
testCode = Whiskers(R"(
if (emptyContractTests())
testCode = Whiskers(R"(
return 0;)")
.render();
#if 0
else
{
auto testTuple = pseudoRandomContractTest();
testCode = Whiskers(R"(
<contractName> testContract = new <contractName>();
if (testContract.<testFunction>() != <expectedOutput>)
return 1;
return 0;)")
("contractName", get<0>(testTuple))
("testFunction", get<1>(testTuple))
("expectedOutput", get<2>(testTuple))
.render();
}
#endif
break;
}
@ -615,25 +619,24 @@ tuple<string, string, string> ProtoConverter::visitProgramHelper(CIL _program)
}
return make_tuple(bases.str(), baseNames.str(), funcs.str());
}
#endif
string ProtoConverter::visit(Contract const& _contract)
{
if (_contract.funcdef_size() == 0 && _contract.bases_size() == 0)
return "";
openProgramScope(&_contract);
auto [bases, baseNames, funcs] = visitProgramHelper(&_contract);
return Whiskers(R"(
<bases>
<?isAbstract>abstract </isAbstract>contract <programName><?inheritance> is <baseNames></inheritance> {
<functionDefs>
})")
("bases", bases)
("isAbstract", _contract.abstract())
("programName", programName(&_contract))
("inheritance", _contract.bases_size() > 0 && !baseNames.empty())
("baseNames", baseNames)
("functionDefs", funcs)
.render();
try {
auto contract = SolContract(_contract, programName(&_contract), m_randomGen);
return contract.str();
}
catch (langutil::FuzzerError const&)
{
// Return empty string if input specification is invalid.
return "";
}
}
#endif
string ProtoConverter::visit(Interface const& _interface)
{
@ -658,22 +661,29 @@ string ProtoConverter::visit(Library const& _library)
return "";
openProgramScope(&_library);
auto lib = SolLibrary(_library, programName(&_library));
auto lib = SolLibrary(_library, programName(&_library), m_randomGen);
if (lib.validTest())
{
auto libTestPair = lib.pseudoRandomTest(_library.random());
auto libTestPair = lib.pseudoRandomTest();
m_libraryTests.push_back({lib.name(), libTestPair.first, libTestPair.second});
}
return lib.str();
}
tuple<string, string, string> ProtoConverter::pseudoRandomLibraryTest(unsigned _randomIdx)
tuple<string, string, string> ProtoConverter::pseudoRandomLibraryTest()
{
solAssert(m_libraryTests.size() > 0, "Sol proto fuzzer: No library tests found");
unsigned index = _randomIdx % m_libraryTests.size();
unsigned index = randomNumber() % m_libraryTests.size();
return m_libraryTests[index];
}
tuple<string, string, string> ProtoConverter::pseudoRandomContractTest()
{
solAssert(m_contractTests.size() > 0, "Sol proto fuzzer: No contract tests found");
unsigned index = randomNumber() % m_contractTests.size();
return m_contractTests[index];
}
void ProtoConverter::openProgramScope(CIL _program)
{
string programNamePrefix;

View File

@ -121,7 +121,8 @@ private:
Contract const* _derived,
CIFunc _baseFunc
);
std::tuple<std::string, std::string, std::string> pseudoRandomLibraryTest(unsigned _randomIdx);
std::tuple<std::string, std::string, std::string> pseudoRandomLibraryTest();
std::tuple<std::string, std::string, std::string> pseudoRandomContractTest();
bool emptyLibrary(Library const& _library)
{
@ -131,6 +132,10 @@ private:
{
return m_libraryTests.size() == 0;
}
bool emptyContractTests()
{
return m_contractTests.size() == 0;
}
void openProgramScope(CIL _program);
bool pseudoRandomCoinFlip()
@ -155,7 +160,6 @@ private:
static bool disallowedContractFunction(SolContractFunction const& _contractFunction, bool _isVirtual);
#endif
unsigned m_numMods = 0;
unsigned m_numPrograms = 0;
unsigned m_counter = 0;
bool m_mostDerivedAbstractContract = false;
@ -194,6 +198,7 @@ private:
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";

View File

@ -61,7 +61,6 @@ message ContractFunction {
message Library {
repeated LibraryFunction funcdef = 1;
required uint64 random = 2;
}
message Interface {
@ -96,7 +95,6 @@ message TestContract {
CONTRACT = 1;
}
required Type type = 1;
required uint64 programidx = 2;
}
message Program {