mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
ABIv2 proto fuzzer implementation for a limited set of value and non-value types (arrays included).
This commit is contained in:
parent
04bad01ab1
commit
5bc1a68b40
36
test/tools/ossfuzz/abiV2FuzzerCommon.cpp
Normal file
36
test/tools/ossfuzz/abiV2FuzzerCommon.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include <test/tools/ossfuzz/abiV2FuzzerCommon.h>
|
||||
|
||||
using namespace dev::test::abiv2fuzzer;
|
||||
|
||||
SolidityCompilationFramework::SolidityCompilationFramework(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
m_evmVersion = _evmVersion;
|
||||
}
|
||||
|
||||
dev::bytes SolidityCompilationFramework::compileContract(
|
||||
std::string const& _sourceCode,
|
||||
std::string const& _contractName
|
||||
)
|
||||
{
|
||||
std::string sourceCode = _sourceCode;
|
||||
m_compiler.setSources({{"", sourceCode}});
|
||||
m_compiler.setEVMVersion(m_evmVersion);
|
||||
m_compiler.setOptimiserSettings(m_optimiserSettings);
|
||||
if (!m_compiler.compile())
|
||||
{
|
||||
langutil::SourceReferenceFormatter formatter(std::cerr);
|
||||
|
||||
for (auto const& error: m_compiler.errors())
|
||||
formatter.printExceptionInformation(
|
||||
*error,
|
||||
formatter.formatErrorInformation(*error)
|
||||
);
|
||||
std::cerr << "Compiling contract failed" << std::endl;
|
||||
}
|
||||
dev::eth::LinkerObject obj = m_compiler.runtimeObject(
|
||||
_contractName.empty() ?
|
||||
m_compiler.lastContractName() :
|
||||
_contractName
|
||||
);
|
||||
return obj.bytecode;
|
||||
}
|
35
test/tools/ossfuzz/abiV2FuzzerCommon.h
Normal file
35
test/tools/ossfuzz/abiV2FuzzerCommon.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
#include <libyul/AssemblyStack.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <liblangutil/SourceReferenceFormatter.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace test
|
||||
{
|
||||
namespace abiv2fuzzer
|
||||
{
|
||||
class SolidityCompilationFramework
|
||||
{
|
||||
public:
|
||||
explicit SolidityCompilationFramework(langutil::EVMVersion _evmVersion = {});
|
||||
|
||||
Json::Value getMethodIdentifiers()
|
||||
{
|
||||
return m_compiler.methodIdentifiers(m_compiler.lastContractName());
|
||||
}
|
||||
dev::bytes compileContract(
|
||||
std::string const& _sourceCode,
|
||||
std::string const& _contractName = {}
|
||||
);
|
||||
protected:
|
||||
dev::solidity::CompilerStack m_compiler;
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
dev::solidity::OptimiserSettings m_optimiserSettings = dev::solidity::OptimiserSettings::full();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
106
test/tools/ossfuzz/abiV2Proto.proto
Normal file
106
test/tools/ossfuzz/abiV2Proto.proto
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
// Flattened specification of array dimension
|
||||
// If is_static is false, and this array dimension is contained
|
||||
// inside another dimension e.g., x[][2] ([2] being the outer dimension)
|
||||
// then `length` for this dimension is the length of the first dynamically
|
||||
// sized array. The other (n-1) lengths are unspecified
|
||||
message ArrayDimensionInfo {
|
||||
required uint32 length = 1;
|
||||
required bool is_static = 2;
|
||||
}
|
||||
|
||||
// TODO: Add more base types
|
||||
// See https://github.com/ethereum/solidity/issues/6749
|
||||
message ArrayType {
|
||||
oneof base_type_oneof {
|
||||
IntegerType inty = 1;
|
||||
FixedByteType byty = 2;
|
||||
AddressType adty = 3;
|
||||
StructType stty = 4;
|
||||
}
|
||||
repeated ArrayDimensionInfo info = 5;
|
||||
}
|
||||
|
||||
// uint8...256, int8...256
|
||||
message IntegerType {
|
||||
required bool is_signed = 1;
|
||||
required uint32 width = 2;
|
||||
}
|
||||
|
||||
// bytes1, bytes2,..., bytes32
|
||||
message FixedByteType {
|
||||
required uint32 width = 1;
|
||||
}
|
||||
|
||||
// address, address payable
|
||||
message AddressType {
|
||||
required bool payable = 1;
|
||||
}
|
||||
|
||||
message ValueType {
|
||||
oneof value_type_oneof {
|
||||
IntegerType inty = 1;
|
||||
FixedByteType byty = 2;
|
||||
AddressType adty = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// bytes/string
|
||||
message DynamicByteArrayType {
|
||||
enum DType {
|
||||
BYTES = 0;
|
||||
STRING = 1;
|
||||
}
|
||||
required DType type = 1;
|
||||
}
|
||||
|
||||
message NonValueType {
|
||||
oneof nonvalue_type_oneof {
|
||||
DynamicByteArrayType dynbytearray = 1;
|
||||
ArrayType arrtype = 2;
|
||||
StructType stype = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message Type {
|
||||
oneof type_oneof {
|
||||
ValueType vtype = 1;
|
||||
NonValueType nvtype = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This must not reference itself either directly or indirectly
|
||||
message StructType {}
|
||||
|
||||
message VarDecl {
|
||||
required Type type = 1;
|
||||
}
|
||||
|
||||
message TestFunction {
|
||||
required VarDecl local_vars = 1;
|
||||
}
|
||||
|
||||
message Contract {
|
||||
required VarDecl state_vars = 1;
|
||||
required TestFunction testfunction = 2;
|
||||
}
|
||||
|
||||
package dev.test.abiv2fuzzer;
|
60
test/tools/ossfuzz/abiV2ProtoFuzzer.cpp
Normal file
60
test/tools/ossfuzz/abiV2ProtoFuzzer.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
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/abiV2FuzzerCommon.h>
|
||||
#include <test/tools/ossfuzz/protoToAbiV2.h>
|
||||
#include <src/libfuzzer/libfuzzer_macro.h>
|
||||
#include <fstream>
|
||||
|
||||
using namespace dev::test::abiv2fuzzer;
|
||||
using namespace dev;
|
||||
using namespace std;
|
||||
|
||||
DEFINE_PROTO_FUZZER(Contract const& _input)
|
||||
{
|
||||
string contract_source = ProtoConverter{}.contractToString(_input);
|
||||
|
||||
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
|
||||
{
|
||||
// With libFuzzer binary run this to generate the solidity source file x.sol from a proto input:
|
||||
// PROTO_FUZZER_DUMP_PATH=x.sol ./a.out proto-input
|
||||
ofstream of(dump_path);
|
||||
of << contract_source;
|
||||
}
|
||||
|
||||
// Raw runtime byte code generated by solidity
|
||||
dev::bytes byteCode;
|
||||
std::string hexEncodedInput;
|
||||
|
||||
try
|
||||
{
|
||||
// Compile contract generated by the proto fuzzer
|
||||
SolidityCompilationFramework solCompilationFramework;
|
||||
std::string contractName = ":Factory";
|
||||
byteCode = solCompilationFramework.compileContract(contract_source, contractName);
|
||||
Json::Value methodIdentifiers = solCompilationFramework.getMethodIdentifiers();
|
||||
// We always call the function test() that is defined in proto converter template
|
||||
hexEncodedInput = methodIdentifiers["test()"].asString();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cout << contract_source << endl;
|
||||
throw;
|
||||
}
|
||||
// TODO: Call evmone wrapper here
|
||||
return;
|
||||
}
|
718
test/tools/ossfuzz/protoToAbiV2.cpp
Normal file
718
test/tools/ossfuzz/protoToAbiV2.cpp
Normal file
@ -0,0 +1,718 @@
|
||||
#include <regex>
|
||||
#include <numeric>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <test/tools/ossfuzz/protoToAbiV2.h>
|
||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||
#include <libdevcore/Whiskers.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev::solidity;
|
||||
using namespace dev::test::abiv2fuzzer;
|
||||
|
||||
// Create a new variable declaration and append said variable to function parameter lists
|
||||
// of coder functions.
|
||||
// Declared name is x_<i>; parameterized name is c_<i>
|
||||
// where <i> is a monotonically increasing integer.
|
||||
void ProtoConverter::createDeclAndParamList(
|
||||
std::string const& _type,
|
||||
DataType _dataType,
|
||||
std::string& _varName,
|
||||
std::string& _paramName
|
||||
)
|
||||
{
|
||||
auto varNames = newVarNames(getNextVarCounter());
|
||||
_varName = varNames.first;
|
||||
_paramName = varNames.second;
|
||||
|
||||
// Declare array
|
||||
appendVarDeclToOutput(_type, _varName, getQualifier(_dataType));
|
||||
|
||||
// Add typed params for calling public and external functions with said type
|
||||
appendTypedParams(
|
||||
CalleeType::PUBLIC,
|
||||
isValueType(_dataType),
|
||||
_type,
|
||||
_paramName,
|
||||
((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
|
||||
);
|
||||
appendTypedParams(
|
||||
CalleeType::EXTERNAL,
|
||||
isValueType(_dataType),
|
||||
_type,
|
||||
_paramName,
|
||||
((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
|
||||
);
|
||||
}
|
||||
|
||||
void ProtoConverter::visitArrayType(std::string const& _baseType, ArrayType const& _x)
|
||||
{
|
||||
std::string type = arrayTypeAsString(_baseType, _x);
|
||||
std::string varName, paramName;
|
||||
createDeclAndParamList(type, DataType::ARRAY, varName, paramName);
|
||||
// Resize-initialize array and add checks
|
||||
resizeInitArray(_x, _baseType, varName, paramName);
|
||||
}
|
||||
|
||||
void ProtoConverter::visitType(
|
||||
DataType _dataType,
|
||||
std::string const& _type,
|
||||
std::string const& _value
|
||||
)
|
||||
{
|
||||
std::string varName, paramName;
|
||||
createDeclAndParamList(_type, _dataType, varName, paramName);
|
||||
addCheckedVarDef(_dataType, varName, paramName, _value);
|
||||
}
|
||||
|
||||
void ProtoConverter::appendVarDeclToOutput(
|
||||
std::string const& _type,
|
||||
std::string const& _varName,
|
||||
std::string const& _qualifier
|
||||
)
|
||||
{
|
||||
// One level of indentation for state variable declarations
|
||||
// Two levels of indentation for local variable declarations
|
||||
m_output << Whiskers(R"(
|
||||
<?isLocalVar> </isLocalVar><type><?qual> <qualifier></qual> <varName>;)"
|
||||
)
|
||||
("isLocalVar", !m_isStateVar)
|
||||
("type", _type)
|
||||
("qual", !_qualifier.empty())
|
||||
("qualifier", _qualifier)
|
||||
("varName", _varName)
|
||||
.render();
|
||||
}
|
||||
|
||||
void ProtoConverter::appendChecks(
|
||||
DataType _type,
|
||||
std::string const& _varName,
|
||||
std::string const& _rhs
|
||||
)
|
||||
{
|
||||
std::string check = {};
|
||||
switch (_type)
|
||||
{
|
||||
case DataType::STRING:
|
||||
check = Whiskers(R"(!bytesCompare(bytes(<varName>), <value>))")
|
||||
("varName", _varName)
|
||||
("value", _rhs)
|
||||
.render();
|
||||
break;
|
||||
case DataType::BYTES:
|
||||
check = Whiskers(R"(!bytesCompare(<varName>, <value>))")
|
||||
("varName", _varName)
|
||||
("value", _rhs)
|
||||
.render();
|
||||
break;
|
||||
case DataType::VALUE:
|
||||
check = Whiskers(R"(<varName> != <value>)")
|
||||
("varName", _varName)
|
||||
("value", _rhs)
|
||||
.render();
|
||||
break;
|
||||
case DataType::ARRAY:
|
||||
solUnimplemented("Proto ABIv2 fuzzer: Invalid data type.");
|
||||
}
|
||||
|
||||
// Each (failing) check returns a unique value to simplify debugging.
|
||||
m_checks << Whiskers(R"(
|
||||
if (<check>) return <returnVal>;)"
|
||||
)
|
||||
("check", check)
|
||||
("returnVal", std::to_string(m_returnValue++))
|
||||
.render();
|
||||
}
|
||||
|
||||
void ProtoConverter::addVarDef(std::string const& _varName, std::string const& _rhs)
|
||||
{
|
||||
std::string varDefString = Whiskers(R"(
|
||||
<varName> = <rhs>;)"
|
||||
)
|
||||
("varName", _varName)
|
||||
("rhs", _rhs)
|
||||
.render();
|
||||
|
||||
// State variables cannot be assigned in contract-scope
|
||||
// Therefore, we buffer their assignments and
|
||||
// render them in function scope later.
|
||||
if (m_isStateVar)
|
||||
m_statebuffer << varDefString;
|
||||
else
|
||||
m_output << varDefString;
|
||||
}
|
||||
|
||||
void ProtoConverter::addCheckedVarDef(
|
||||
DataType _type,
|
||||
std::string const& _varName,
|
||||
std::string const& _paramName,
|
||||
std::string const& _rhs)
|
||||
{
|
||||
addVarDef(_varName, _rhs);
|
||||
appendChecks(_type, _paramName, _rhs);
|
||||
}
|
||||
|
||||
// Runtime check for array length.
|
||||
void ProtoConverter::checkResizeOp(std::string const& _paramName, unsigned _len)
|
||||
{
|
||||
appendChecks(DataType::VALUE, _paramName + ".length", std::to_string(_len));
|
||||
}
|
||||
|
||||
/* Input(s)
|
||||
* - Unsigned integer to be hashed
|
||||
* - Width of desired uint value
|
||||
* Processing
|
||||
* - Take hash of first parameter and mask it with the max unsigned value for given bit width
|
||||
* Output
|
||||
* - string representation of uint value
|
||||
*/
|
||||
std::string ProtoConverter::uintValueAsString(unsigned _width, unsigned _counter)
|
||||
{
|
||||
solAssert(
|
||||
(_width % 8 == 0),
|
||||
"Proto ABIv2 Fuzzer: Unsigned integer width is not a multiple of 8"
|
||||
);
|
||||
return maskUnsignedIntToHex(_counter, _width/4);
|
||||
}
|
||||
|
||||
/* Input(s)
|
||||
* - counter to be hashed to derive a value for Integer type
|
||||
* - Width of desired int value
|
||||
* Processing
|
||||
* - Take hash of first parameter and mask it with the max signed value for given bit width
|
||||
* Output
|
||||
* - string representation of int value
|
||||
*/
|
||||
std::string ProtoConverter::intValueAsString(unsigned _width, unsigned _counter)
|
||||
{
|
||||
solAssert(
|
||||
(_width % 8 == 0),
|
||||
"Proto ABIv2 Fuzzer: Signed integer width is not a multiple of 8"
|
||||
);
|
||||
return maskUnsignedIntToHex(_counter, ((_width/4) - 1));
|
||||
}
|
||||
|
||||
std::string ProtoConverter::addressValueAsString(unsigned _counter)
|
||||
{
|
||||
return Whiskers(R"(address(<value>))")
|
||||
("value", uintValueAsString(160, _counter))
|
||||
.render();
|
||||
}
|
||||
|
||||
std::string ProtoConverter::fixedByteValueAsString(unsigned _width, unsigned _counter)
|
||||
{
|
||||
solAssert(
|
||||
(_width >= 1 && _width <= 32),
|
||||
"Proto ABIv2 Fuzzer: Fixed byte width is not between 1--32"
|
||||
);
|
||||
// Masked value must contain twice the number of nibble "f"'s as _width
|
||||
unsigned numMaskNibbles = _width * 2;
|
||||
// Start position of substring equals totalHexStringLength - numMaskNibbles
|
||||
// totalHexStringLength = 64 + 2 = 66
|
||||
// e.g., 0x12345678901234567890123456789012 is a total of 66 characters
|
||||
// |---------------------^-----------|
|
||||
// <--- start position---><--numMask->
|
||||
// <-----------total length --------->
|
||||
// Note: This assumes that maskUnsignedIntToHex() invokes toHex(..., HexPrefix::Add)
|
||||
unsigned startPos = 66 - numMaskNibbles;
|
||||
|
||||
// Extracts the least significant numMaskNibbles from the result of "maskUnsignedIntToHex",
|
||||
// and replaces "0x" with "hex\"...\"" string.
|
||||
// This is needed because solidity interprets a 20-byte 0x prefixed hex literal as an address
|
||||
// payable type.
|
||||
return Whiskers(R"(hex"<value>")")
|
||||
("value", maskUnsignedIntToHex(_counter, numMaskNibbles).substr(startPos, numMaskNibbles))
|
||||
.render();
|
||||
}
|
||||
|
||||
std::string ProtoConverter::integerValueAsString(bool _sign, unsigned _width, unsigned _counter)
|
||||
{
|
||||
if (_sign)
|
||||
return intValueAsString(_width, _counter);
|
||||
else
|
||||
return uintValueAsString(_width, _counter);
|
||||
}
|
||||
|
||||
std::string ProtoConverter::bytesArrayTypeAsString(DynamicByteArrayType const& _x)
|
||||
{
|
||||
switch (_x.type())
|
||||
{
|
||||
case DynamicByteArrayType::BYTES:
|
||||
return "bytes";
|
||||
case DynamicByteArrayType::STRING:
|
||||
return "string";
|
||||
}
|
||||
}
|
||||
|
||||
std::string ProtoConverter::structTypeAsString(StructType const&)
|
||||
{
|
||||
// TODO: Implement this
|
||||
return {};
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(IntegerType const& _x)
|
||||
{
|
||||
visitType(
|
||||
DataType::VALUE,
|
||||
getIntTypeAsString(_x),
|
||||
integerValueAsString(isIntSigned(_x), getIntWidth(_x), getNextCounter())
|
||||
);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(AddressType const& _x)
|
||||
{
|
||||
visitType(
|
||||
DataType::VALUE,
|
||||
getAddressTypeAsString(_x),
|
||||
addressValueAsString(getNextCounter())
|
||||
);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(FixedByteType const& _x)
|
||||
{
|
||||
visitType(
|
||||
DataType::VALUE,
|
||||
getFixedByteTypeAsString(_x),
|
||||
fixedByteValueAsString(getFixedByteWidth(_x), getNextCounter())
|
||||
);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(ValueType const& _x)
|
||||
{
|
||||
switch (_x.value_type_oneof_case())
|
||||
{
|
||||
case ValueType::kInty:
|
||||
visit(_x.inty());
|
||||
break;
|
||||
case ValueType::kByty:
|
||||
visit(_x.byty());
|
||||
break;
|
||||
case ValueType::kAdty:
|
||||
visit(_x.adty());
|
||||
break;
|
||||
case ValueType::VALUE_TYPE_ONEOF_NOT_SET:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(DynamicByteArrayType const& _x)
|
||||
{
|
||||
visitType(
|
||||
(_x.type() == DynamicByteArrayType::BYTES) ? DataType::BYTES : DataType::STRING,
|
||||
bytesArrayTypeAsString(_x),
|
||||
bytesArrayValueAsString()
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Implement struct visitor
|
||||
void ProtoConverter::visit(StructType const&)
|
||||
{
|
||||
}
|
||||
|
||||
std::string ProtoConverter::arrayDimInfoAsString(ArrayDimensionInfo const& _x)
|
||||
{
|
||||
unsigned arrLength = getArrayLengthFromFuzz(_x.length());
|
||||
if (_x.is_static())
|
||||
return Whiskers(R"([<length>])")
|
||||
("length", std::to_string(arrLength))
|
||||
.render();
|
||||
else
|
||||
return R"([])";
|
||||
}
|
||||
|
||||
void ProtoConverter::arrayDimensionsAsStringVector(
|
||||
ArrayType const& _x,
|
||||
std::vector<std::string>& _vecOfStr)
|
||||
{
|
||||
solAssert(_x.info_size() > 0, "Proto ABIv2 Fuzzer: Array dimensions empty.");
|
||||
for (auto const& dim: _x.info())
|
||||
_vecOfStr.push_back(arrayDimInfoAsString(dim));
|
||||
}
|
||||
|
||||
ProtoConverter::VecOfBoolUnsigned ProtoConverter::arrayDimensionsAsPairVector(
|
||||
ArrayType const& _x
|
||||
)
|
||||
{
|
||||
VecOfBoolUnsigned arrayDimsPairVector = {};
|
||||
for (auto const& dim: _x.info())
|
||||
arrayDimsPairVector.push_back(arrayDimInfoAsPair(dim));
|
||||
solAssert(!arrayDimsPairVector.empty(), "Proto ABIv2 Fuzzer: Array dimensions empty.");
|
||||
return arrayDimsPairVector;
|
||||
}
|
||||
|
||||
std::string ProtoConverter::getValueByBaseType(ArrayType const& _x)
|
||||
{
|
||||
switch (_x.base_type_oneof_case())
|
||||
{
|
||||
case ArrayType::kInty:
|
||||
return integerValueAsString(isIntSigned(_x.inty()), getIntWidth(_x.inty()), getNextCounter());
|
||||
case ArrayType::kByty:
|
||||
return fixedByteValueAsString(getFixedByteWidth(_x.byty()), getNextCounter());
|
||||
case ArrayType::kAdty:
|
||||
return addressValueAsString(getNextCounter());
|
||||
// TODO: Implement structs.
|
||||
case ArrayType::kStty:
|
||||
case ArrayType::BASE_TYPE_ONEOF_NOT_SET:
|
||||
solAssert(false, "Proto ABIv2 fuzzer: Invalid array base type");
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a resize operation for a given dimension of type `_type` and expression referenced
|
||||
// by `_var`. `_isStatic` is true for statically sized dimensions, false otherwise.
|
||||
// `_arrayLen` is equal to length of statically sized array dimension. For dynamically
|
||||
// sized dimension, we use `getArrayLengthFromFuzz()` and a monotonically increasing
|
||||
// counter to obtain actual length. Function returns dimension length.
|
||||
unsigned ProtoConverter::resizeDimension(
|
||||
bool _isStatic,
|
||||
unsigned _arrayLen,
|
||||
std::string const& _var,
|
||||
std::string const& _param,
|
||||
std::string const& _type
|
||||
)
|
||||
{
|
||||
unsigned length;
|
||||
if (_isStatic)
|
||||
length = _arrayLen;
|
||||
else
|
||||
{
|
||||
length = getArrayLengthFromFuzz(_arrayLen, getNextCounter());
|
||||
|
||||
// If local var, new T(l);
|
||||
// Else, l;
|
||||
std::string lhs, rhs;
|
||||
if (m_isStateVar)
|
||||
{
|
||||
lhs = _var + ".length";
|
||||
rhs = Whiskers(R"(<length>)")
|
||||
("length", std::to_string(length))
|
||||
.render();
|
||||
}
|
||||
else
|
||||
{
|
||||
lhs = _var;
|
||||
rhs = Whiskers(R"(new <type>(<length>))")
|
||||
("type", _type)
|
||||
("length", std::to_string(length))
|
||||
.render();
|
||||
}
|
||||
// If local var, x = new T(l);
|
||||
// Else, x.length = l;
|
||||
addVarDef(lhs, rhs);
|
||||
}
|
||||
|
||||
// if (c.length != l)
|
||||
checkResizeOp(_param, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
void ProtoConverter::resizeHelper(
|
||||
ArrayType const& _x,
|
||||
std::vector<std::string> _arrStrVec,
|
||||
VecOfBoolUnsigned _arrInfoVec,
|
||||
std::string const& _varName,
|
||||
std::string const& _paramName
|
||||
)
|
||||
{
|
||||
// Initialize value expressions if we have arrived at leaf node,
|
||||
// (depth-first) recurse otherwise.
|
||||
if (_arrInfoVec.empty())
|
||||
{
|
||||
// expression name is _var
|
||||
// value is a value of base type
|
||||
std::string value = getValueByBaseType(_x);
|
||||
// add assignment and check
|
||||
addCheckedVarDef(DataType::VALUE, _varName, _paramName, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& dim = _arrInfoVec.back();
|
||||
|
||||
std::string type = std::accumulate(
|
||||
_arrStrVec.begin(),
|
||||
_arrStrVec.end(),
|
||||
std::string("")
|
||||
);
|
||||
unsigned length = resizeDimension(dim.first, dim.second, _varName, _paramName, type);
|
||||
// Recurse one level dimension down.
|
||||
_arrStrVec.pop_back();
|
||||
_arrInfoVec.pop_back();
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
resizeHelper(
|
||||
_x,
|
||||
_arrStrVec,
|
||||
_arrInfoVec,
|
||||
_varName + "[" + std::to_string(i) + "]",
|
||||
_paramName + "[" + std::to_string(i) + "]"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This function takes care of properly resizing and initializing ArrayType.
|
||||
// In parallel, it adds runtime checks on array bound and values.
|
||||
void ProtoConverter::resizeInitArray(
|
||||
ArrayType const& _x,
|
||||
std::string const& _baseType,
|
||||
std::string const& _varName,
|
||||
std::string const& _paramName
|
||||
)
|
||||
{
|
||||
VecOfBoolUnsigned arrInfoVec = arrayDimensionsAsPairVector(_x);
|
||||
std::vector<std::string> arrStrVec = {_baseType};
|
||||
arrayDimensionsAsStringVector(_x, arrStrVec);
|
||||
resizeHelper(_x, arrStrVec, arrInfoVec, _varName, _paramName);
|
||||
}
|
||||
|
||||
// Returns array type from it's base type (e.g., int8) and array dimensions info contained in
|
||||
// ArrayType.
|
||||
std::string ProtoConverter::arrayTypeAsString(std::string const& _baseType, ArrayType const& _x)
|
||||
{
|
||||
std::vector<std::string> typeStringVec = {_baseType};
|
||||
arrayDimensionsAsStringVector(_x, typeStringVec);
|
||||
|
||||
return std::accumulate(
|
||||
typeStringVec.begin(),
|
||||
typeStringVec.end(),
|
||||
std::string("")
|
||||
);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(ArrayType const& _x)
|
||||
{
|
||||
// Bail out if input contains too few or too many dimensions.
|
||||
if (_x.info_size() == 0 || _x.info_size() > (int)s_maxArrayDimensions)
|
||||
return;
|
||||
|
||||
string baseType = {};
|
||||
switch (_x.base_type_oneof_case())
|
||||
{
|
||||
case ArrayType::kInty:
|
||||
baseType = getIntTypeAsString(_x.inty());
|
||||
break;
|
||||
case ArrayType::kByty:
|
||||
baseType = getFixedByteTypeAsString(_x.byty());
|
||||
break;
|
||||
case ArrayType::kAdty:
|
||||
baseType = getAddressTypeAsString(_x.adty());
|
||||
break;
|
||||
case ArrayType::kStty:
|
||||
case ArrayType::BASE_TYPE_ONEOF_NOT_SET:
|
||||
return;
|
||||
}
|
||||
visitArrayType(baseType, _x);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(NonValueType const& _x)
|
||||
{
|
||||
switch (_x.nonvalue_type_oneof_case())
|
||||
{
|
||||
case NonValueType::kDynbytearray:
|
||||
visit(_x.dynbytearray());
|
||||
break;
|
||||
case NonValueType::kArrtype:
|
||||
visit(_x.arrtype());
|
||||
break;
|
||||
case NonValueType::kStype:
|
||||
visit(_x.stype());
|
||||
break;
|
||||
case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(Type const& _x)
|
||||
{
|
||||
switch (_x.type_oneof_case())
|
||||
{
|
||||
case Type::kVtype:
|
||||
visit(_x.vtype());
|
||||
break;
|
||||
case Type::kNvtype:
|
||||
visit(_x.nvtype());
|
||||
break;
|
||||
case Type::TYPE_ONEOF_NOT_SET:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(VarDecl const& _x)
|
||||
{
|
||||
visit(_x.type());
|
||||
}
|
||||
|
||||
std::string ProtoConverter::equalityChecksAsString()
|
||||
{
|
||||
return m_checks.str();
|
||||
}
|
||||
|
||||
std::string ProtoConverter::delimiterToString(Delimiter _delimiter)
|
||||
{
|
||||
switch (_delimiter)
|
||||
{
|
||||
case Delimiter::ADD:
|
||||
return ", ";
|
||||
case Delimiter::SKIP:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/* When a new variable is declared, we can invoke this function
|
||||
* to prepare the typed param list to be passed to callee functions.
|
||||
* We independently prepare this list for "public" and "external"
|
||||
* callee functions.
|
||||
*/
|
||||
void ProtoConverter::appendTypedParams(
|
||||
CalleeType _calleeType,
|
||||
bool _isValueType,
|
||||
std::string const& _typeString,
|
||||
std::string const& _varName,
|
||||
Delimiter _delimiter
|
||||
)
|
||||
{
|
||||
switch (_calleeType)
|
||||
{
|
||||
case CalleeType::PUBLIC:
|
||||
appendTypedParamsPublic(_isValueType, _typeString, _varName, _delimiter);
|
||||
break;
|
||||
case CalleeType::EXTERNAL:
|
||||
appendTypedParamsExternal(_isValueType, _typeString, _varName, _delimiter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the qualifier "calldata" to non-value parameter of an external function.
|
||||
void ProtoConverter::appendTypedParamsExternal(
|
||||
bool _isValueType,
|
||||
std::string const& _typeString,
|
||||
std::string const& _varName,
|
||||
Delimiter _delimiter
|
||||
)
|
||||
{
|
||||
std::string qualifiedTypeString = (
|
||||
_isValueType ?
|
||||
_typeString :
|
||||
_typeString + " calldata"
|
||||
);
|
||||
m_typedParamsExternal << Whiskers(R"(<delimiter><type> <varName>)")
|
||||
("delimiter", delimiterToString(_delimiter))
|
||||
("type", qualifiedTypeString)
|
||||
("varName", _varName)
|
||||
.render();
|
||||
}
|
||||
|
||||
// Adds the qualifier "memory" to non-value parameter of an external function.
|
||||
void ProtoConverter::appendTypedParamsPublic(
|
||||
bool _isValueType,
|
||||
std::string const& _typeString,
|
||||
std::string const& _varName,
|
||||
Delimiter _delimiter
|
||||
)
|
||||
{
|
||||
std::string qualifiedTypeString = (
|
||||
_isValueType ?
|
||||
_typeString :
|
||||
_typeString + " memory"
|
||||
);
|
||||
m_typedParamsPublic << Whiskers(R"(<delimiter><type> <varName>)")
|
||||
("delimiter", delimiterToString(_delimiter))
|
||||
("type", qualifiedTypeString)
|
||||
("varName", _varName)
|
||||
.render();
|
||||
}
|
||||
|
||||
std::string ProtoConverter::typedParametersAsString(CalleeType _calleeType)
|
||||
{
|
||||
switch (_calleeType)
|
||||
{
|
||||
case CalleeType::PUBLIC:
|
||||
return m_typedParamsPublic.str();
|
||||
case CalleeType::EXTERNAL:
|
||||
return m_typedParamsExternal.str();
|
||||
}
|
||||
}
|
||||
|
||||
// Function that is called by the factory contract
|
||||
void ProtoConverter::visit(TestFunction const& _x)
|
||||
{
|
||||
m_output << R"(
|
||||
|
||||
function test() public returns (uint) {
|
||||
)";
|
||||
|
||||
// Define state variables in function scope
|
||||
m_output << m_statebuffer.str();
|
||||
|
||||
// TODO: Support more than one but less than N local variables
|
||||
visit(_x.local_vars());
|
||||
|
||||
m_output << Whiskers(R"(
|
||||
uint returnVal = this.coder_public(<parameter_names>);
|
||||
if (returnVal != 0)
|
||||
return returnVal;
|
||||
return (uint(1000) + this.coder_external(<parameter_names>));
|
||||
}
|
||||
)")
|
||||
("parameter_names", YulUtilFunctions::suffixedVariableNameList(s_varNamePrefix, 0, m_varCounter))
|
||||
.render();
|
||||
}
|
||||
|
||||
void ProtoConverter::writeHelperFunctions()
|
||||
{
|
||||
m_output << R"(
|
||||
function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
|
||||
if(a.length != b.length)
|
||||
return false;
|
||||
for (uint i = 0; i < a.length; i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
)";
|
||||
|
||||
// These are callee functions that encode from storage, decode to
|
||||
// memory/calldata and check if decoded value matches storage value
|
||||
// return true on successful match, false otherwise
|
||||
m_output << Whiskers(R"(
|
||||
function coder_public(<parameters_memory>) public view returns (uint) {
|
||||
<equality_checks>
|
||||
return 0;
|
||||
}
|
||||
|
||||
function coder_external(<parameters_calldata>) external view returns (uint) {
|
||||
<equality_checks>
|
||||
return 0;
|
||||
}
|
||||
)")
|
||||
("parameters_memory", typedParametersAsString(CalleeType::PUBLIC))
|
||||
("equality_checks", equalityChecksAsString())
|
||||
("parameters_calldata", typedParametersAsString(CalleeType::EXTERNAL))
|
||||
.render();
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(Contract const& _x)
|
||||
{
|
||||
m_output << R"(pragma solidity >=0.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Factory {
|
||||
function test() external returns (uint) {
|
||||
C c = new C();
|
||||
return c.test();
|
||||
}
|
||||
}
|
||||
|
||||
contract C {
|
||||
)";
|
||||
// TODO: Support more than one but less than N state variables
|
||||
visit(_x.state_vars());
|
||||
m_isStateVar = false;
|
||||
// Test function
|
||||
visit(_x.testfunction());
|
||||
writeHelperFunctions();
|
||||
m_output << "\n}";
|
||||
}
|
||||
|
||||
string ProtoConverter::contractToString(Contract const& _input)
|
||||
{
|
||||
visit(_input);
|
||||
return m_output.str();
|
||||
}
|
361
test/tools/ossfuzz/protoToAbiV2.h
Normal file
361
test/tools/ossfuzz/protoToAbiV2.h
Normal file
@ -0,0 +1,361 @@
|
||||
#pragma once
|
||||
|
||||
#include <libdevcore/FixedHash.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
#include <libdevcore/Whiskers.h>
|
||||
#include <test/tools/ossfuzz/abiV2Proto.pb.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
/**
|
||||
* Template of the solidity test program generated by this converter is as follows:
|
||||
*
|
||||
* pragma solidity >=0.0;
|
||||
* pragma experimental ABIEncoderV2;
|
||||
*
|
||||
* contract Factory {
|
||||
* // Factory test function. Called by EVM client
|
||||
* function test() external returns (uint) {
|
||||
* C c = new C();
|
||||
* return c.test();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* contract C {
|
||||
* // State variable
|
||||
* string x_0;
|
||||
* // Test function. Called by Factory test function
|
||||
* function test() public returns (uint) {
|
||||
* // Local variable
|
||||
* bytes x_1 = "1";
|
||||
* x_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
|
||||
* uint returnVal = this.coder_public(x_0, x_1);
|
||||
* if (returnVal != 0)
|
||||
* return returnVal;
|
||||
* // Since the return codes in the public and external coder functions are identical
|
||||
* // we offset error code by a fixed amount (1000) for differentiation.
|
||||
* return (uint(1000) + this.coder_external(x_0, x_1));
|
||||
* }
|
||||
*
|
||||
* // Utility functions
|
||||
* function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* // Public function that is called by test() function. Accepts one or more arguments and returns
|
||||
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
|
||||
* function coder_public(string memory c_0, bytes memory c_1) public view returns (uint) {
|
||||
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
||||
* return 1;
|
||||
* if (!bytesCompare(c_1, "1"))
|
||||
* return 2;
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* // External function that is called by test() function. Accepts one or more arguments and returns
|
||||
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
|
||||
* function coder_external(string calldata c_0, bytes calldata c_1) external view returns (uint) {
|
||||
* if (!stringCompare(c_0, "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
||||
* return 1;
|
||||
* if (!bytesCompare(c_1, "1"))
|
||||
* return 2;
|
||||
* return 0;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace test
|
||||
{
|
||||
namespace abiv2fuzzer
|
||||
{
|
||||
class ProtoConverter
|
||||
{
|
||||
public:
|
||||
ProtoConverter():
|
||||
m_isStateVar(true),
|
||||
m_counter(0),
|
||||
m_varCounter(0),
|
||||
m_returnValue(1)
|
||||
{}
|
||||
|
||||
ProtoConverter(ProtoConverter const&) = delete;
|
||||
|
||||
ProtoConverter(ProtoConverter&&) = delete;
|
||||
|
||||
std::string contractToString(Contract const& _input);
|
||||
|
||||
private:
|
||||
using VecOfBoolUnsigned = std::vector<std::pair<bool, unsigned>>;
|
||||
|
||||
enum class Delimiter
|
||||
{
|
||||
ADD,
|
||||
SKIP
|
||||
};
|
||||
enum class CalleeType
|
||||
{
|
||||
PUBLIC,
|
||||
EXTERNAL
|
||||
};
|
||||
enum class DataType
|
||||
{
|
||||
BYTES,
|
||||
STRING,
|
||||
VALUE,
|
||||
ARRAY
|
||||
};
|
||||
|
||||
void visit(IntegerType const&);
|
||||
|
||||
void visit(FixedByteType const&);
|
||||
|
||||
void visit(AddressType const&);
|
||||
|
||||
void visit(ArrayType const&);
|
||||
|
||||
void visit(DynamicByteArrayType const&);
|
||||
|
||||
void visit(StructType const&);
|
||||
|
||||
void visit(ValueType const&);
|
||||
|
||||
void visit(NonValueType const&);
|
||||
|
||||
void visit(Type const&);
|
||||
|
||||
void visit(VarDecl const&);
|
||||
|
||||
void visit(TestFunction const&);
|
||||
|
||||
void visit(Contract const&);
|
||||
|
||||
std::string getValueByBaseType(ArrayType const&);
|
||||
|
||||
void resizeInitArray(
|
||||
ArrayType const& _x,
|
||||
std::string const& _baseType,
|
||||
std::string const& _varName,
|
||||
std::string const& _paramName
|
||||
);
|
||||
|
||||
unsigned resizeDimension(
|
||||
bool _isStatic,
|
||||
unsigned _arrayLen,
|
||||
std::string const& _var,
|
||||
std::string const& _param,
|
||||
std::string const& _type
|
||||
);
|
||||
|
||||
void resizeHelper(
|
||||
ArrayType const& _x,
|
||||
std::vector<std::string> _arrStrVec,
|
||||
VecOfBoolUnsigned _arrInfoVec,
|
||||
std::string const& _varName,
|
||||
std::string const& _paramName
|
||||
);
|
||||
|
||||
// Utility functions
|
||||
void appendChecks(DataType _type, std::string const& _varName, std::string const& _rhs);
|
||||
|
||||
void addVarDef(std::string const& _varName, std::string const& _rhs);
|
||||
|
||||
void addCheckedVarDef(
|
||||
DataType _type,
|
||||
std::string const& _varName,
|
||||
std::string const& _paramName,
|
||||
std::string const& _rhs
|
||||
);
|
||||
|
||||
void appendTypedParams(
|
||||
CalleeType _calleeType,
|
||||
bool _isValueType,
|
||||
std::string const& _typeString,
|
||||
std::string const& _varName,
|
||||
Delimiter _delimiter
|
||||
);
|
||||
|
||||
void appendTypedParamsPublic(
|
||||
bool _isValueType,
|
||||
std::string const& _typeString,
|
||||
std::string const& _varName,
|
||||
Delimiter _delimiter = Delimiter::ADD
|
||||
);
|
||||
|
||||
void appendTypedParamsExternal(
|
||||
bool _isValueType,
|
||||
std::string const& _typeString,
|
||||
std::string const& _varName,
|
||||
Delimiter _delimiter = Delimiter::ADD
|
||||
);
|
||||
|
||||
void appendVarDeclToOutput(
|
||||
std::string const& _type,
|
||||
std::string const& _varName,
|
||||
std::string const& _qualifier
|
||||
);
|
||||
|
||||
void checkResizeOp(std::string const& _varName, unsigned _len);
|
||||
|
||||
void visitType(DataType _dataType, std::string const& _type, std::string const& _value);
|
||||
|
||||
void visitArrayType(std::string const&, ArrayType const&);
|
||||
|
||||
void createDeclAndParamList(
|
||||
std::string const& _type,
|
||||
DataType _dataType,
|
||||
std::string& _varName,
|
||||
std::string& _paramName
|
||||
);
|
||||
|
||||
std::string equalityChecksAsString();
|
||||
|
||||
std::string typedParametersAsString(CalleeType _calleeType);
|
||||
|
||||
void writeHelperFunctions();
|
||||
|
||||
// Function definitions
|
||||
// m_counter is used to derive values for typed variables
|
||||
unsigned getNextCounter()
|
||||
{
|
||||
return m_counter++;
|
||||
}
|
||||
|
||||
// m_varCounter is used to derive declared and parameterized variable names
|
||||
unsigned getNextVarCounter()
|
||||
{
|
||||
return m_varCounter++;
|
||||
}
|
||||
|
||||
// Accepts an unsigned counter and returns a pair of strings
|
||||
// First string is declared name (s_varNamePrefix<varcounter_value>)
|
||||
// Second string is parameterized name (s_paramPrefix<varcounter_value>)
|
||||
auto newVarNames(unsigned _varCounter)
|
||||
{
|
||||
return std::make_pair(
|
||||
s_varNamePrefix + std::to_string(_varCounter),
|
||||
s_paramNamePrefix + std::to_string(_varCounter)
|
||||
);
|
||||
}
|
||||
|
||||
// String and bytes literals are derived by hashing a monotonically increasing
|
||||
// counter and enclosing the said hash inside double quotes.
|
||||
std::string bytesArrayValueAsString()
|
||||
{
|
||||
return "\"" + toHex(hashUnsignedInt(getNextCounter()), HexPrefix::DontAdd) + "\"";
|
||||
}
|
||||
|
||||
std::string getQualifier(DataType _dataType)
|
||||
{
|
||||
return ((isValueType(_dataType) || m_isStateVar) ? "" : "memory");
|
||||
}
|
||||
|
||||
// Static declarations
|
||||
static std::string structTypeAsString(StructType const& _x);
|
||||
static std::string intValueAsString(unsigned _width, unsigned _counter);
|
||||
static std::string uintValueAsString(unsigned _width, unsigned _counter);
|
||||
static std::string integerValueAsString(bool _sign, unsigned _width, unsigned _counter);
|
||||
static std::string addressValueAsString(unsigned _counter);
|
||||
static std::string fixedByteValueAsString(unsigned _width, unsigned _counter);
|
||||
static std::vector<std::pair<bool, unsigned>> arrayDimensionsAsPairVector(ArrayType const& _x);
|
||||
static std::string arrayDimInfoAsString(ArrayDimensionInfo const& _x);
|
||||
static void arrayDimensionsAsStringVector(
|
||||
ArrayType const& _x,
|
||||
std::vector<std::string>&
|
||||
);
|
||||
static std::string bytesArrayTypeAsString(DynamicByteArrayType const& _x);
|
||||
static std::string arrayTypeAsString(std::string const&, ArrayType const&);
|
||||
static std::string delimiterToString(Delimiter _delimiter);
|
||||
|
||||
// Static function definitions
|
||||
static bool isValueType(DataType _dataType)
|
||||
{
|
||||
return _dataType == DataType::VALUE;
|
||||
}
|
||||
|
||||
static unsigned getIntWidth(IntegerType const& _x)
|
||||
{
|
||||
return 8 * ((_x.width() % 32) + 1);
|
||||
}
|
||||
|
||||
static bool isIntSigned(IntegerType const& _x)
|
||||
{
|
||||
return _x.is_signed();
|
||||
}
|
||||
|
||||
static std::string getIntTypeAsString(IntegerType const& _x)
|
||||
{
|
||||
return ((isIntSigned(_x) ? "int" : "uint") + std::to_string(getIntWidth(_x)));
|
||||
}
|
||||
|
||||
static unsigned getFixedByteWidth(FixedByteType const& _x)
|
||||
{
|
||||
return (_x.width() % 32) + 1;
|
||||
}
|
||||
|
||||
static std::string getFixedByteTypeAsString(FixedByteType const& _x)
|
||||
{
|
||||
return "bytes" + std::to_string(getFixedByteWidth(_x));
|
||||
}
|
||||
|
||||
static std::string getAddressTypeAsString(AddressType const& _x)
|
||||
{
|
||||
return (_x.payable() ? "address payable": "address");
|
||||
}
|
||||
|
||||
// Convert _counter to string and return its keccak256 hash
|
||||
static u256 hashUnsignedInt(unsigned _counter)
|
||||
{
|
||||
return keccak256(h256(_counter));
|
||||
}
|
||||
|
||||
static u256 maskUnsignedInt(unsigned _counter, unsigned _numMaskNibbles)
|
||||
{
|
||||
return hashUnsignedInt(_counter) & u256("0x" + std::string(_numMaskNibbles, 'f'));
|
||||
}
|
||||
|
||||
// Requires caller to pass number of nibbles (twice the number of bytes) as second argument.
|
||||
// Note: Don't change HexPrefix::Add. See comment in fixedByteValueAsString().
|
||||
static std::string maskUnsignedIntToHex(unsigned _counter, unsigned _numMaskNibbles)
|
||||
{
|
||||
return toHex(maskUnsignedInt(_counter, _numMaskNibbles), HexPrefix::Add);
|
||||
}
|
||||
|
||||
static unsigned getArrayLengthFromFuzz(unsigned _fuzz, unsigned _counter = 0)
|
||||
{
|
||||
return ((_fuzz + _counter) % s_maxArrayLength) + 1;
|
||||
}
|
||||
|
||||
static std::pair<bool, unsigned> arrayDimInfoAsPair(ArrayDimensionInfo const& _x)
|
||||
{
|
||||
return std::make_pair(_x.is_static(), getArrayLengthFromFuzz(_x.length()));
|
||||
}
|
||||
|
||||
/// Contains the test program
|
||||
std::ostringstream m_output;
|
||||
/// Temporary storage for state variable definitions
|
||||
std::ostringstream m_statebuffer;
|
||||
/// Contains a subset of the test program. This subset contains
|
||||
/// checks to be encoded in the test program
|
||||
std::ostringstream m_checks;
|
||||
/// Contains typed parameter list to be passed to callee functions
|
||||
std::ostringstream m_typedParamsExternal;
|
||||
std::ostringstream m_typedParamsPublic;
|
||||
/// Predicate that is true if we are in contract scope
|
||||
bool m_isStateVar;
|
||||
unsigned m_counter;
|
||||
unsigned m_varCounter;
|
||||
/// Monotonically increasing return value for error reporting
|
||||
unsigned m_returnValue;
|
||||
static unsigned constexpr s_maxArrayLength = 2;
|
||||
static unsigned constexpr s_maxArrayDimensions = 10;
|
||||
/// Prefixes for declared and parameterized variable names
|
||||
static auto constexpr s_varNamePrefix = "x_";
|
||||
static auto constexpr s_paramNamePrefix = "c_";
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user