mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1209 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1209 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <test/tools/ossfuzz/protoToAbiV2.h>
 | 
						|
 | 
						|
using namespace std;
 | 
						|
using namespace solidity;
 | 
						|
using namespace solidity::util;
 | 
						|
using namespace solidity::test::abiv2fuzzer;
 | 
						|
 | 
						|
string ProtoConverter::getVarDecl(
 | 
						|
	string const& _type,
 | 
						|
	string const& _varName,
 | 
						|
	string const& _qualifier
 | 
						|
)
 | 
						|
{
 | 
						|
	// One level of indentation for state variable declarations
 | 
						|
	// Two levels of indentation for local variable declarations
 | 
						|
	return Whiskers(R"(
 | 
						|
	<?isLocalVar>	</isLocalVar><type><?qual> <qualifier></qual> <varName>;)"
 | 
						|
		)
 | 
						|
		("isLocalVar", !m_isStateVar)
 | 
						|
		("type", _type)
 | 
						|
		("qual", !_qualifier.empty())
 | 
						|
		("qualifier", _qualifier)
 | 
						|
		("varName", _varName)
 | 
						|
		.render() +
 | 
						|
		"\n";
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(Type const& _type)
 | 
						|
{
 | 
						|
	switch (_type.type_oneof_case())
 | 
						|
	{
 | 
						|
	case Type::kVtype:
 | 
						|
		return visit(_type.vtype());
 | 
						|
	case Type::kNvtype:
 | 
						|
		return visit(_type.nvtype());
 | 
						|
	case Type::TYPE_ONEOF_NOT_SET:
 | 
						|
		return make_pair("", "");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(ValueType const& _type)
 | 
						|
{
 | 
						|
	switch (_type.value_type_oneof_case())
 | 
						|
	{
 | 
						|
	case ValueType::kBoolty:
 | 
						|
		return visit(_type.boolty());
 | 
						|
	case ValueType::kInty:
 | 
						|
		return visit(_type.inty());
 | 
						|
	case ValueType::kByty:
 | 
						|
		return visit(_type.byty());
 | 
						|
	case ValueType::kAdty:
 | 
						|
		return visit(_type.adty());
 | 
						|
	case ValueType::VALUE_TYPE_ONEOF_NOT_SET:
 | 
						|
		return make_pair("", "");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(NonValueType const& _type)
 | 
						|
{
 | 
						|
	switch (_type.nonvalue_type_oneof_case())
 | 
						|
	{
 | 
						|
	case NonValueType::kDynbytearray:
 | 
						|
		return visit(_type.dynbytearray());
 | 
						|
	case NonValueType::kArrtype:
 | 
						|
		if (ValidityVisitor().visit(_type.arrtype()))
 | 
						|
			return visit(_type.arrtype());
 | 
						|
		else
 | 
						|
			return make_pair("", "");
 | 
						|
	case NonValueType::kStype:
 | 
						|
		if (ValidityVisitor().visit(_type.stype()))
 | 
						|
			return visit(_type.stype());
 | 
						|
		else
 | 
						|
			return make_pair("", "");
 | 
						|
	case NonValueType::NONVALUE_TYPE_ONEOF_NOT_SET:
 | 
						|
		return make_pair("", "");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(BoolType const& _type)
 | 
						|
{
 | 
						|
	return processType(_type, true);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(IntegerType const& _type)
 | 
						|
{
 | 
						|
	return processType(_type, true);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(FixedByteType const& _type)
 | 
						|
{
 | 
						|
	return processType(_type, true);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(AddressType const& _type)
 | 
						|
{
 | 
						|
	return processType(_type, true);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(DynamicByteArrayType const& _type)
 | 
						|
{
 | 
						|
	return processType(_type, false);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(ArrayType const& _type)
 | 
						|
{
 | 
						|
	return processType(_type, false);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(StructType const& _type)
 | 
						|
{
 | 
						|
	return processType(_type, false);
 | 
						|
}
 | 
						|
 | 
						|
template <typename T>
 | 
						|
pair<string, string> ProtoConverter::processType(T const& _type, bool _isValueType)
 | 
						|
{
 | 
						|
	ostringstream local, global;
 | 
						|
	auto [varName, paramName] = newVarNames(getNextVarCounter(), m_isStateVar);
 | 
						|
 | 
						|
	// Add variable name to the argument list of coder function call
 | 
						|
	if (m_argsCoder.str().empty())
 | 
						|
		m_argsCoder << varName;
 | 
						|
	else
 | 
						|
		m_argsCoder << ", " << varName;
 | 
						|
 | 
						|
	string location{};
 | 
						|
	if (!m_isStateVar && !_isValueType)
 | 
						|
		location = "memory";
 | 
						|
 | 
						|
	auto varDeclBuffers = varDecl(
 | 
						|
		varName,
 | 
						|
		paramName,
 | 
						|
		_type,
 | 
						|
		_isValueType,
 | 
						|
		location
 | 
						|
	);
 | 
						|
	global << varDeclBuffers.first;
 | 
						|
	local << varDeclBuffers.second;
 | 
						|
	auto assignCheckBuffers = assignChecker(varName, paramName, _type);
 | 
						|
	global << assignCheckBuffers.first;
 | 
						|
	local << assignCheckBuffers.second;
 | 
						|
 | 
						|
	m_structCounter += m_numStructsAdded;
 | 
						|
	return make_pair(global.str(), local.str());
 | 
						|
}
 | 
						|
 | 
						|
template <typename T>
 | 
						|
pair<string, string> ProtoConverter::varDecl(
 | 
						|
	string const& _varName,
 | 
						|
	string const& _paramName,
 | 
						|
	T _type,
 | 
						|
	bool _isValueType,
 | 
						|
	string const& _location
 | 
						|
)
 | 
						|
{
 | 
						|
	ostringstream local, global;
 | 
						|
 | 
						|
	TypeVisitor tVisitor(m_structCounter);
 | 
						|
	string typeStr = tVisitor.visit(_type);
 | 
						|
	if (typeStr.empty())
 | 
						|
		return make_pair("", "");
 | 
						|
 | 
						|
	// Append struct defs
 | 
						|
	global << tVisitor.structDef();
 | 
						|
	m_numStructsAdded = tVisitor.numStructs();
 | 
						|
 | 
						|
	// variable declaration
 | 
						|
	if (m_isStateVar)
 | 
						|
		global << getVarDecl(typeStr, _varName, _location);
 | 
						|
	else
 | 
						|
		local << getVarDecl(typeStr, _varName, _location);
 | 
						|
 | 
						|
	// Add typed params for calling public and external functions with said type
 | 
						|
	appendTypedParams(
 | 
						|
		CalleeType::PUBLIC,
 | 
						|
		_isValueType,
 | 
						|
		typeStr,
 | 
						|
		_paramName,
 | 
						|
		((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
 | 
						|
	);
 | 
						|
	appendTypedParams(
 | 
						|
		CalleeType::EXTERNAL,
 | 
						|
		_isValueType,
 | 
						|
		typeStr,
 | 
						|
		_paramName,
 | 
						|
		((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
 | 
						|
	);
 | 
						|
	appendTypes(
 | 
						|
		_isValueType,
 | 
						|
		typeStr,
 | 
						|
		((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
 | 
						|
	);
 | 
						|
	appendTypedReturn(
 | 
						|
		_isValueType,
 | 
						|
		typeStr,
 | 
						|
		((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
 | 
						|
	);
 | 
						|
 | 
						|
	// Update dyn param only if necessary
 | 
						|
	if (tVisitor.isLastDynParamRightPadded())
 | 
						|
		m_isLastDynParamRightPadded = true;
 | 
						|
 | 
						|
	return make_pair(global.str(), local.str());
 | 
						|
}
 | 
						|
 | 
						|
template <typename T>
 | 
						|
pair<string, string> ProtoConverter::assignChecker(
 | 
						|
	string const& _varName,
 | 
						|
	string const& _paramName,
 | 
						|
	T _type
 | 
						|
)
 | 
						|
{
 | 
						|
	ostringstream local;
 | 
						|
	AssignCheckVisitor acVisitor(
 | 
						|
		_varName,
 | 
						|
		_paramName,
 | 
						|
		m_returnValue,
 | 
						|
		m_isStateVar,
 | 
						|
		m_counter,
 | 
						|
		m_structCounter
 | 
						|
	);
 | 
						|
	pair<string, string> assignCheckStrPair = acVisitor.visit(_type);
 | 
						|
	m_returnValue += acVisitor.errorStmts();
 | 
						|
	m_counter += acVisitor.counted();
 | 
						|
 | 
						|
	m_checks << assignCheckStrPair.second;
 | 
						|
 | 
						|
	// State variables cannot be assigned in contract-scope
 | 
						|
	// Therefore, we buffer their assignments and
 | 
						|
	// render them in function scope later.
 | 
						|
	local << assignCheckStrPair.first;
 | 
						|
	return make_pair("", local.str());
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> ProtoConverter::visit(VarDecl const& _x)
 | 
						|
{
 | 
						|
	return 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;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ProtoConverter::appendTypes(
 | 
						|
	bool _isValueType,
 | 
						|
	string const& _typeString,
 | 
						|
	Delimiter _delimiter
 | 
						|
)
 | 
						|
{
 | 
						|
	string qualifiedTypeString = (
 | 
						|
		_isValueType ?
 | 
						|
		_typeString :
 | 
						|
		_typeString + " memory"
 | 
						|
	);
 | 
						|
	m_types << Whiskers(R"(<delimiter><type>)")
 | 
						|
		("delimiter", delimiterToString(_delimiter))
 | 
						|
		("type", qualifiedTypeString)
 | 
						|
		.render();
 | 
						|
}
 | 
						|
 | 
						|
void ProtoConverter::appendTypedReturn(
 | 
						|
	bool _isValueType,
 | 
						|
	string const& _typeString,
 | 
						|
	Delimiter _delimiter
 | 
						|
)
 | 
						|
{
 | 
						|
	string qualifiedTypeString = (
 | 
						|
		_isValueType ?
 | 
						|
		_typeString :
 | 
						|
		_typeString + " memory"
 | 
						|
	);
 | 
						|
	m_typedReturn << Whiskers(R"(<delimiter><type> <varName>)")
 | 
						|
		("delimiter", delimiterToString(_delimiter))
 | 
						|
		("type", qualifiedTypeString)
 | 
						|
		("varName", "lv_" + to_string(m_varCounter - 1))
 | 
						|
		.render();
 | 
						|
}
 | 
						|
 | 
						|
// 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();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDefs)
 | 
						|
{
 | 
						|
	// TODO: Support more than one but less than N local variables
 | 
						|
	auto localVarBuffers = visit(_x.local_vars());
 | 
						|
 | 
						|
	string structTypeDecl = localVarBuffers.first;
 | 
						|
	string localVarDefs = localVarBuffers.second;
 | 
						|
 | 
						|
	ostringstream testBuffer;
 | 
						|
 | 
						|
	string testFunction = Whiskers(R"(
 | 
						|
	function test() public returns (uint) {
 | 
						|
		<?calldata>return test_calldata_coding();</calldata>
 | 
						|
		<?returndata>return test_returndata_coding();</returndata>
 | 
						|
	})")
 | 
						|
	("calldata", m_test == Contract_Test::Contract_Test_CALLDATA_CODER)
 | 
						|
	("returndata", m_test == Contract_Test::Contract_Test_RETURNDATA_CODER)
 | 
						|
	.render();
 | 
						|
 | 
						|
	string functionDeclCalldata = "function test_calldata_coding() internal returns (uint)";
 | 
						|
	string functionDeclReturndata = "function test_returndata_coding() internal returns (uint)";
 | 
						|
 | 
						|
	testBuffer << Whiskers(R"(<structTypeDecl>
 | 
						|
<testFunction>
 | 
						|
<?calldata>
 | 
						|
	<functionDeclCalldata> {
 | 
						|
<storageVarDefs>
 | 
						|
<localVarDefs>
 | 
						|
<calldataTestCode>
 | 
						|
	}
 | 
						|
<calldataHelperFuncs>
 | 
						|
</calldata>
 | 
						|
<?returndata>
 | 
						|
	<functionDeclReturndata> {
 | 
						|
<returndataTestCode>
 | 
						|
	}
 | 
						|
 | 
						|
<?varsPresent>
 | 
						|
	function coder_returndata_external() external returns (<return_types>) {
 | 
						|
<storageVarDefs>
 | 
						|
<localVarDefs>
 | 
						|
		return (<return_values>);
 | 
						|
	}
 | 
						|
</varsPresent>
 | 
						|
</returndata>)")
 | 
						|
		("testFunction", testFunction)
 | 
						|
		("calldata", m_test == Contract_Test::Contract_Test_CALLDATA_CODER)
 | 
						|
		("returndata", m_test == Contract_Test::Contract_Test_RETURNDATA_CODER)
 | 
						|
		("calldataHelperFuncs", calldataHelperFunctions())
 | 
						|
		("varsPresent", !m_types.str().empty())
 | 
						|
		("structTypeDecl", structTypeDecl)
 | 
						|
		("functionDeclCalldata", functionDeclCalldata)
 | 
						|
		("functionDeclReturndata", functionDeclReturndata)
 | 
						|
		("storageVarDefs", _storageVarDefs)
 | 
						|
		("localVarDefs", localVarDefs)
 | 
						|
		("calldataTestCode", testCallDataFunction(_x.invalid_encoding_length()))
 | 
						|
		("returndataTestCode", testReturnDataFunction())
 | 
						|
		("return_types", m_types.str())
 | 
						|
		("return_values", m_argsCoder.str())
 | 
						|
		.render();
 | 
						|
	return testBuffer.str();
 | 
						|
}
 | 
						|
 | 
						|
string ProtoConverter::testCallDataFunction(unsigned _invalidLength)
 | 
						|
{
 | 
						|
	return Whiskers(R"(
 | 
						|
		uint returnVal = this.coder_calldata_public(<argumentNames>);
 | 
						|
		if (returnVal != 0)
 | 
						|
			return returnVal;
 | 
						|
 | 
						|
		returnVal = this.coder_calldata_external(<argumentNames>);
 | 
						|
		if (returnVal != 0)
 | 
						|
			return uint(200000) + returnVal;
 | 
						|
 | 
						|
		<?atLeastOneVar>
 | 
						|
		bytes memory argumentEncoding = abi.encode(<argumentNames>);
 | 
						|
 | 
						|
		returnVal = checkEncodedCall(
 | 
						|
			this.coder_calldata_public.selector,
 | 
						|
			argumentEncoding,
 | 
						|
			<invalidLengthFuzz>,
 | 
						|
			<isRightPadded>
 | 
						|
		);
 | 
						|
		if (returnVal != 0)
 | 
						|
			return returnVal;
 | 
						|
 | 
						|
		returnVal = checkEncodedCall(
 | 
						|
			this.coder_calldata_external.selector,
 | 
						|
			argumentEncoding,
 | 
						|
			<invalidLengthFuzz>,
 | 
						|
			<isRightPadded>
 | 
						|
		);
 | 
						|
		if (returnVal != 0)
 | 
						|
			return uint(200000) + returnVal;
 | 
						|
		</atLeastOneVar>
 | 
						|
		return 0;
 | 
						|
		)")
 | 
						|
		("argumentNames", m_argsCoder.str())
 | 
						|
		("invalidLengthFuzz", std::to_string(_invalidLength))
 | 
						|
		("isRightPadded", isLastDynParamRightPadded() ? "true" : "false")
 | 
						|
		("atLeastOneVar", m_varCounter > 0)
 | 
						|
		.render();
 | 
						|
}
 | 
						|
 | 
						|
string ProtoConverter::testReturnDataFunction()
 | 
						|
{
 | 
						|
	return Whiskers(R"(
 | 
						|
<?varsPresent>
 | 
						|
		(<varDecl>) = this.coder_returndata_external();
 | 
						|
<equality_checks>
 | 
						|
</varsPresent>
 | 
						|
		return 0;
 | 
						|
		)")
 | 
						|
		("varsPresent", !m_typedReturn.str().empty())
 | 
						|
		("varDecl", m_typedReturn.str())
 | 
						|
		("equality_checks", m_checks.str())
 | 
						|
		.render();
 | 
						|
}
 | 
						|
 | 
						|
string ProtoConverter::calldataHelperFunctions()
 | 
						|
{
 | 
						|
	stringstream calldataHelperFuncs;
 | 
						|
	calldataHelperFuncs << R"(
 | 
						|
	/// Accepts function selector, correct argument encoding, and length of
 | 
						|
	/// invalid encoding and returns the correct and incorrect abi encoding
 | 
						|
	/// for calling the function specified by the function selector.
 | 
						|
	function createEncoding(
 | 
						|
		bytes4 funcSelector,
 | 
						|
		bytes memory argumentEncoding,
 | 
						|
		uint invalidLengthFuzz,
 | 
						|
		bool isRightPadded
 | 
						|
	) internal pure returns (bytes memory, bytes memory)
 | 
						|
	{
 | 
						|
		bytes memory validEncoding = new bytes(4 + argumentEncoding.length);
 | 
						|
		// Ensure that invalidEncoding crops at least 32 bytes (padding length
 | 
						|
		// is at most 31 bytes) if `isRightPadded` is true.
 | 
						|
		// This is because shorter bytes/string values (whose encoding is right
 | 
						|
		// padded) can lead to successful decoding when fewer than 32 bytes have
 | 
						|
		// been cropped in the worst case. In other words, if `isRightPadded` is
 | 
						|
		// true, then
 | 
						|
		//  0 <= invalidLength <= argumentEncoding.length - 32
 | 
						|
		// otherwise
 | 
						|
		//  0 <= invalidLength <= argumentEncoding.length - 1
 | 
						|
		uint invalidLength;
 | 
						|
		if (isRightPadded)
 | 
						|
			invalidLength = invalidLengthFuzz % (argumentEncoding.length - 31);
 | 
						|
		else
 | 
						|
			invalidLength = invalidLengthFuzz % argumentEncoding.length;
 | 
						|
		bytes memory invalidEncoding = new bytes(4 + invalidLength);
 | 
						|
		for (uint i = 0; i < 4; i++)
 | 
						|
			validEncoding[i] = invalidEncoding[i] = funcSelector[i];
 | 
						|
		for (uint i = 0; i < argumentEncoding.length; i++)
 | 
						|
			validEncoding[i+4] = argumentEncoding[i];
 | 
						|
		for (uint i = 0; i < invalidLength; i++)
 | 
						|
			invalidEncoding[i+4] = argumentEncoding[i];
 | 
						|
		return (validEncoding, invalidEncoding);
 | 
						|
	}
 | 
						|
 | 
						|
	/// Accepts function selector, correct argument encoding, and an invalid
 | 
						|
	/// encoding length as input. Returns a non-zero value if either call with
 | 
						|
	/// correct encoding fails or call with incorrect encoding succeeds.
 | 
						|
	/// Returns zero if both calls meet expectation.
 | 
						|
	function checkEncodedCall(
 | 
						|
		bytes4 funcSelector,
 | 
						|
		bytes memory argumentEncoding,
 | 
						|
		uint invalidLengthFuzz,
 | 
						|
		bool isRightPadded
 | 
						|
	) public returns (uint)
 | 
						|
	{
 | 
						|
		(bytes memory validEncoding, bytes memory invalidEncoding) = createEncoding(
 | 
						|
			funcSelector,
 | 
						|
			argumentEncoding,
 | 
						|
			invalidLengthFuzz,
 | 
						|
			isRightPadded
 | 
						|
		);
 | 
						|
		(bool success, bytes memory returnVal) = address(this).call(validEncoding);
 | 
						|
		uint returnCode = abi.decode(returnVal, (uint));
 | 
						|
		// Return non-zero value if call fails for correct encoding
 | 
						|
		if (success == false || returnCode != 0)
 | 
						|
			return 400000;
 | 
						|
		(success, ) = address(this).call(invalidEncoding);
 | 
						|
		// Return non-zero value if call succeeds for incorrect encoding
 | 
						|
		if (success == true)
 | 
						|
			return 400001;
 | 
						|
		return 0;
 | 
						|
	})";
 | 
						|
 | 
						|
	// 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
 | 
						|
	calldataHelperFuncs << Whiskers(R"(
 | 
						|
	function coder_calldata_public(<parameters_memory>) public pure returns (uint) {
 | 
						|
<equality_checks>
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	function coder_calldata_external(<parameters_calldata>) external pure returns (uint) {
 | 
						|
<equality_checks>
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	)")
 | 
						|
	("parameters_memory", typedParametersAsString(CalleeType::PUBLIC))
 | 
						|
	("equality_checks", equalityChecksAsString())
 | 
						|
	("parameters_calldata", typedParametersAsString(CalleeType::EXTERNAL))
 | 
						|
	.render();
 | 
						|
 | 
						|
	return calldataHelperFuncs.str();
 | 
						|
}
 | 
						|
 | 
						|
string ProtoConverter::commonHelperFunctions()
 | 
						|
{
 | 
						|
	stringstream helperFuncs;
 | 
						|
	helperFuncs << R"(
 | 
						|
	/// Compares bytes, returning true if they are equal and false otherwise.
 | 
						|
	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;
 | 
						|
	}
 | 
						|
	)";
 | 
						|
 | 
						|
	return helperFuncs.str();
 | 
						|
}
 | 
						|
 | 
						|
void ProtoConverter::visit(Contract const& _x)
 | 
						|
{
 | 
						|
	string pragmas = R"(pragma solidity >=0.0;
 | 
						|
pragma experimental ABIEncoderV2;)";
 | 
						|
 | 
						|
	// Record test spec
 | 
						|
	m_test = _x.test();
 | 
						|
 | 
						|
	// TODO: Support more than one but less than N state variables
 | 
						|
	auto storageBuffers = visit(_x.state_vars());
 | 
						|
	string storageVarDecls = storageBuffers.first;
 | 
						|
	string storageVarDefs = storageBuffers.second;
 | 
						|
	m_isStateVar = false;
 | 
						|
	string testFunction = visit(_x.testfunction(), storageVarDefs);
 | 
						|
	/* Structure of contract body
 | 
						|
	 * - Storage variable declarations
 | 
						|
	 * - Struct definitions
 | 
						|
	 * - Test function
 | 
						|
	 *     - Storage variable assignments
 | 
						|
	 *     - Local variable definitions and assignments
 | 
						|
	 *     - Test code proper (calls public and external functions)
 | 
						|
	 * - Helper functions
 | 
						|
	 */
 | 
						|
	ostringstream contractBody;
 | 
						|
	contractBody << storageVarDecls
 | 
						|
	             << testFunction
 | 
						|
	             << commonHelperFunctions();
 | 
						|
	m_output << Whiskers(R"(<pragmas>
 | 
						|
<contractStart>
 | 
						|
<contractBody>
 | 
						|
<contractEnd>)")
 | 
						|
		("pragmas", pragmas)
 | 
						|
		("contractStart", "contract C {")
 | 
						|
		("contractBody", contractBody.str())
 | 
						|
		("contractEnd", "}")
 | 
						|
		.render();
 | 
						|
}
 | 
						|
 | 
						|
string ProtoConverter::contractToString(Contract const& _input)
 | 
						|
{
 | 
						|
	visit(_input);
 | 
						|
	return m_output.str();
 | 
						|
}
 | 
						|
 | 
						|
/// Type visitor
 | 
						|
string TypeVisitor::visit(BoolType const&)
 | 
						|
{
 | 
						|
	m_baseType = "bool";
 | 
						|
	return m_baseType;
 | 
						|
}
 | 
						|
 | 
						|
string TypeVisitor::visit(IntegerType const& _type)
 | 
						|
{
 | 
						|
	m_baseType = getIntTypeAsString(_type);
 | 
						|
	return m_baseType;
 | 
						|
}
 | 
						|
 | 
						|
string TypeVisitor::visit(FixedByteType const& _type)
 | 
						|
{
 | 
						|
	m_baseType = getFixedByteTypeAsString(_type);
 | 
						|
	return m_baseType;
 | 
						|
}
 | 
						|
 | 
						|
string TypeVisitor::visit(AddressType const& _type)
 | 
						|
{
 | 
						|
	m_baseType = getAddressTypeAsString(_type);
 | 
						|
	return m_baseType;
 | 
						|
}
 | 
						|
 | 
						|
string TypeVisitor::visit(ArrayType const& _type)
 | 
						|
{
 | 
						|
	if (!ValidityVisitor().visit(_type))
 | 
						|
		return "";
 | 
						|
 | 
						|
	string baseType = visit(_type.t());
 | 
						|
	solAssert(!baseType.empty(), "");
 | 
						|
	string arrayBraces = _type.is_static() ?
 | 
						|
	                     string("[") +
 | 
						|
	                     to_string(getStaticArrayLengthFromFuzz(_type.length())) +
 | 
						|
	                     string("]") :
 | 
						|
	                     string("[]");
 | 
						|
	m_baseType += arrayBraces;
 | 
						|
 | 
						|
	// If we don't know yet if the array will be dynamically encoded,
 | 
						|
	// check again. If we already know that it will be, there's no
 | 
						|
	// need to do anything.
 | 
						|
	if (!m_isLastDynParamRightPadded)
 | 
						|
		m_isLastDynParamRightPadded = DynParamVisitor().visit(_type);
 | 
						|
 | 
						|
	return baseType + arrayBraces;
 | 
						|
}
 | 
						|
 | 
						|
string TypeVisitor::visit(DynamicByteArrayType const& _type)
 | 
						|
{
 | 
						|
	m_isLastDynParamRightPadded = true;
 | 
						|
	m_baseType = bytesArrayTypeAsString(_type);
 | 
						|
	return m_baseType;
 | 
						|
}
 | 
						|
 | 
						|
void TypeVisitor::structDefinition(StructType const& _type)
 | 
						|
{
 | 
						|
	// Return an empty string if struct is empty
 | 
						|
	solAssert(ValidityVisitor().visit(_type), "");
 | 
						|
 | 
						|
	// Reset field counter and indentation
 | 
						|
	unsigned wasFieldCounter = m_structFieldCounter;
 | 
						|
	unsigned wasIndentation = m_indentation;
 | 
						|
 | 
						|
	m_indentation = 1;
 | 
						|
	m_structFieldCounter = 0;
 | 
						|
 | 
						|
	// Commence struct declaration
 | 
						|
	string structDef = lineString(
 | 
						|
		"struct " +
 | 
						|
		string(s_structNamePrefix) +
 | 
						|
		to_string(m_structCounter) +
 | 
						|
		" {"
 | 
						|
	);
 | 
						|
 | 
						|
	// Increase indentation for struct fields
 | 
						|
	m_indentation++;
 | 
						|
	for (auto const& t: _type.t())
 | 
						|
	{
 | 
						|
		string type{};
 | 
						|
 | 
						|
		if (!ValidityVisitor().visit(t))
 | 
						|
			continue;
 | 
						|
 | 
						|
		TypeVisitor tVisitor(m_structCounter + 1);
 | 
						|
		type = tVisitor.visit(t);
 | 
						|
		m_structCounter += tVisitor.numStructs();
 | 
						|
		m_structDef << tVisitor.structDef();
 | 
						|
 | 
						|
		solAssert(!type.empty(), "");
 | 
						|
 | 
						|
		structDef += lineString(
 | 
						|
			Whiskers(R"(<type> <member>;)")
 | 
						|
				("type", type)
 | 
						|
				("member", "m" + to_string(m_structFieldCounter++))
 | 
						|
				.render()
 | 
						|
		);
 | 
						|
	}
 | 
						|
	m_indentation--;
 | 
						|
	structDef += lineString("}");
 | 
						|
	m_structCounter++;
 | 
						|
	m_structDef << structDef;
 | 
						|
	m_indentation = wasIndentation;
 | 
						|
	m_structFieldCounter = wasFieldCounter;
 | 
						|
}
 | 
						|
 | 
						|
string TypeVisitor::visit(StructType const& _type)
 | 
						|
{
 | 
						|
	if (ValidityVisitor().visit(_type))
 | 
						|
	{
 | 
						|
		// Add struct definition
 | 
						|
		structDefinition(_type);
 | 
						|
		// Set last dyn param if struct contains a dyn param e.g., bytes, array etc.
 | 
						|
		m_isLastDynParamRightPadded = DynParamVisitor().visit(_type);
 | 
						|
		// If top-level struct is a non-emtpy struct, assign the name S<suffix>
 | 
						|
		m_baseType = s_structTypeName + to_string(m_structStartCounter);
 | 
						|
	}
 | 
						|
	else
 | 
						|
		m_baseType = {};
 | 
						|
 | 
						|
	return m_baseType;
 | 
						|
}
 | 
						|
 | 
						|
/// AssignCheckVisitor implementation
 | 
						|
pair<string, string> AssignCheckVisitor::visit(BoolType const& _type)
 | 
						|
{
 | 
						|
	string value = ValueGetterVisitor(counter()).visit(_type);
 | 
						|
	return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> AssignCheckVisitor::visit(IntegerType const& _type)
 | 
						|
{
 | 
						|
	string value = ValueGetterVisitor(counter()).visit(_type);
 | 
						|
	return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> AssignCheckVisitor::visit(FixedByteType const& _type)
 | 
						|
{
 | 
						|
	string value = ValueGetterVisitor(counter()).visit(_type);
 | 
						|
	return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> AssignCheckVisitor::visit(AddressType const& _type)
 | 
						|
{
 | 
						|
	string value = ValueGetterVisitor(counter()).visit(_type);
 | 
						|
	return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> AssignCheckVisitor::visit(DynamicByteArrayType const& _type)
 | 
						|
{
 | 
						|
	string value = ValueGetterVisitor(counter()).visit(_type);
 | 
						|
	DataType dataType = _type.type() == DynamicByteArrayType::BYTES ? DataType::BYTES :	DataType::STRING;
 | 
						|
	return assignAndCheckStringPair(m_varName, m_paramName, value, value, dataType);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> AssignCheckVisitor::visit(ArrayType const& _type)
 | 
						|
{
 | 
						|
	if (!ValidityVisitor().visit(_type))
 | 
						|
		return make_pair("", "");
 | 
						|
 | 
						|
	// Obtain type of array to be resized and initialized
 | 
						|
	string typeStr{};
 | 
						|
 | 
						|
	unsigned wasStructCounter = m_structCounter;
 | 
						|
	TypeVisitor tVisitor(m_structCounter);
 | 
						|
	typeStr = tVisitor.visit(_type);
 | 
						|
 | 
						|
	pair<string, string> resizeBuffer;
 | 
						|
	string lengthStr;
 | 
						|
	unsigned length;
 | 
						|
 | 
						|
	// Resize dynamic arrays
 | 
						|
	if (!_type.is_static())
 | 
						|
	{
 | 
						|
		length = getDynArrayLengthFromFuzz(_type.length(), counter());
 | 
						|
		lengthStr = to_string(length);
 | 
						|
		if (m_stateVar)
 | 
						|
		{
 | 
						|
			// Dynamic storage arrays are resized via the empty push() operation
 | 
						|
			resizeBuffer.first = Whiskers(R"(<indentation>for (uint i = 0; i < <length>; i++) <arrayRef>.push();)")
 | 
						|
				("indentation", indentation())
 | 
						|
				("length", lengthStr)
 | 
						|
				("arrayRef", m_varName)
 | 
						|
				.render() + "\n";
 | 
						|
			// Add a dynamic check on the resized length
 | 
						|
			resizeBuffer.second = checkString(m_paramName + ".length", lengthStr, DataType::VALUE);
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			// Resizing memory arrays via the new operator
 | 
						|
			string resizeOp = Whiskers(R"(new <fullTypeStr>(<length>))")
 | 
						|
				("fullTypeStr", typeStr)
 | 
						|
				("length", lengthStr)
 | 
						|
				.render();
 | 
						|
			resizeBuffer = assignAndCheckStringPair(
 | 
						|
				m_varName,
 | 
						|
				m_paramName + ".length",
 | 
						|
				resizeOp,
 | 
						|
				lengthStr,
 | 
						|
				DataType::VALUE
 | 
						|
				);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		length = getStaticArrayLengthFromFuzz(_type.length());
 | 
						|
		lengthStr = to_string(length);
 | 
						|
		// Add check on length
 | 
						|
		resizeBuffer.second = checkString(m_paramName + ".length", lengthStr, DataType::VALUE);
 | 
						|
	}
 | 
						|
 | 
						|
	// Add assignCheckBuffer and check statements
 | 
						|
	pair<string, string> assignCheckBuffer;
 | 
						|
	string wasVarName = m_varName;
 | 
						|
	string wasParamName = m_paramName;
 | 
						|
	for (unsigned i = 0; i < length; i++)
 | 
						|
	{
 | 
						|
		m_varName = wasVarName + "[" + to_string(i) + "]";
 | 
						|
		m_paramName = wasParamName + "[" + to_string(i) + "]";
 | 
						|
		pair<string, string> assign = visit(_type.t());
 | 
						|
		assignCheckBuffer.first += assign.first;
 | 
						|
		assignCheckBuffer.second += assign.second;
 | 
						|
		if (i < length - 1)
 | 
						|
			m_structCounter = wasStructCounter;
 | 
						|
	}
 | 
						|
 | 
						|
	// Since struct visitor won't be called for zero-length
 | 
						|
	// arrays, struct counter will not get incremented. Therefore,
 | 
						|
	// we need to manually force a recursive struct visit.
 | 
						|
	if (length == 0 && TypeVisitor().arrayOfStruct(_type))
 | 
						|
		visit(_type.t());
 | 
						|
 | 
						|
	m_varName = wasVarName;
 | 
						|
	m_paramName = wasParamName;
 | 
						|
 | 
						|
	// Compose resize and initialization assignment and check
 | 
						|
	return make_pair(
 | 
						|
		resizeBuffer.first + assignCheckBuffer.first,
 | 
						|
		resizeBuffer.second + assignCheckBuffer.second
 | 
						|
	);
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> AssignCheckVisitor::visit(StructType const& _type)
 | 
						|
{
 | 
						|
	if (!ValidityVisitor().visit(_type))
 | 
						|
		return make_pair("", "");
 | 
						|
 | 
						|
	pair<string, string> assignCheckBuffer;
 | 
						|
	unsigned i = 0;
 | 
						|
 | 
						|
	// Increment struct counter
 | 
						|
	m_structCounter++;
 | 
						|
 | 
						|
	string wasVarName = m_varName;
 | 
						|
	string wasParamName = m_paramName;
 | 
						|
 | 
						|
	for (auto const& t: _type.t())
 | 
						|
	{
 | 
						|
		m_varName = wasVarName + ".m" + to_string(i);
 | 
						|
		m_paramName = wasParamName + ".m" + to_string(i);
 | 
						|
		pair<string, string> assign = visit(t);
 | 
						|
		// If type is not well formed continue without
 | 
						|
		// updating state.
 | 
						|
		if (assign.first.empty() && assign.second.empty())
 | 
						|
			continue;
 | 
						|
		assignCheckBuffer.first += assign.first;
 | 
						|
		assignCheckBuffer.second += assign.second;
 | 
						|
		i++;
 | 
						|
	}
 | 
						|
	m_varName = wasVarName;
 | 
						|
	m_paramName = wasParamName;
 | 
						|
	return assignCheckBuffer;
 | 
						|
}
 | 
						|
 | 
						|
pair<string, string> AssignCheckVisitor::assignAndCheckStringPair(
 | 
						|
	string const& _varRef,
 | 
						|
	string const& _checkRef,
 | 
						|
	string const& _assignValue,
 | 
						|
	string const& _checkValue,
 | 
						|
	DataType _type
 | 
						|
)
 | 
						|
{
 | 
						|
	return make_pair(assignString(_varRef, _assignValue), checkString(_checkRef, _checkValue, _type));
 | 
						|
}
 | 
						|
 | 
						|
string AssignCheckVisitor::assignString(string const& _ref, string const& _value)
 | 
						|
{
 | 
						|
	string assignStmt = Whiskers(R"(<ref> = <value>;)")
 | 
						|
		("ref", _ref)
 | 
						|
		("value", _value)
 | 
						|
		.render();
 | 
						|
	return indentation() + assignStmt + "\n";
 | 
						|
}
 | 
						|
 | 
						|
string AssignCheckVisitor::checkString(string const& _ref, string const& _value, DataType _type)
 | 
						|
{
 | 
						|
	string checkPred;
 | 
						|
	switch (_type)
 | 
						|
	{
 | 
						|
	case DataType::STRING:
 | 
						|
		checkPred = Whiskers(R"(!bytesCompare(bytes(<varName>), <value>))")
 | 
						|
			("varName", _ref)
 | 
						|
			("value", _value)
 | 
						|
			.render();
 | 
						|
		break;
 | 
						|
	case DataType::BYTES:
 | 
						|
		checkPred = Whiskers(R"(!bytesCompare(<varName>, <value>))")
 | 
						|
			("varName", _ref)
 | 
						|
			("value", _value)
 | 
						|
			.render();
 | 
						|
		break;
 | 
						|
	case DataType::VALUE:
 | 
						|
		checkPred = Whiskers(R"(<varName> != <value>)")
 | 
						|
			("varName", _ref)
 | 
						|
			("value", _value)
 | 
						|
			.render();
 | 
						|
		break;
 | 
						|
	case DataType::ARRAY:
 | 
						|
		solUnimplemented("Proto ABIv2 fuzzer: Invalid data type.");
 | 
						|
	}
 | 
						|
	string checkStmt = Whiskers(R"(if (<checkPred>) return <errCode>;)")
 | 
						|
		("checkPred", checkPred)
 | 
						|
		("errCode", to_string(m_errorCode++))
 | 
						|
		.render();
 | 
						|
	return indentation() + checkStmt + "\n";
 | 
						|
}
 | 
						|
 | 
						|
/// ValueGetterVisitor
 | 
						|
string ValueGetterVisitor::visit(BoolType const&)
 | 
						|
{
 | 
						|
	return counter() % 2 ? "true" : "false";
 | 
						|
}
 | 
						|
 | 
						|
string ValueGetterVisitor::visit(IntegerType const& _type)
 | 
						|
{
 | 
						|
	return integerValueAsString(
 | 
						|
		_type.is_signed(),
 | 
						|
		getIntWidth(_type),
 | 
						|
		counter()
 | 
						|
	);
 | 
						|
}
 | 
						|
 | 
						|
string ValueGetterVisitor::visit(FixedByteType const& _type)
 | 
						|
{
 | 
						|
	return fixedByteValueAsString(
 | 
						|
		getFixedByteWidth(_type),
 | 
						|
		counter()
 | 
						|
	);
 | 
						|
}
 | 
						|
 | 
						|
string ValueGetterVisitor::visit(AddressType const&)
 | 
						|
{
 | 
						|
	return addressValueAsString(counter());
 | 
						|
}
 | 
						|
 | 
						|
string ValueGetterVisitor::visit(DynamicByteArrayType const& _type)
 | 
						|
{
 | 
						|
	return bytesArrayValueAsString(
 | 
						|
		counter(),
 | 
						|
		getDataTypeOfDynBytesType(_type) == DataType::BYTES
 | 
						|
	);
 | 
						|
}
 | 
						|
 | 
						|
std::string ValueGetterVisitor::integerValueAsString(bool _sign, unsigned _width, unsigned _counter)
 | 
						|
{
 | 
						|
	if (_sign)
 | 
						|
		return intValueAsString(_width, _counter);
 | 
						|
	else
 | 
						|
		return uintValueAsString(_width, _counter);
 | 
						|
}
 | 
						|
 | 
						|
/* 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 ValueGetterVisitor::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 ValueGetterVisitor::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 ValueGetterVisitor::croppedString(
 | 
						|
	unsigned _numBytes,
 | 
						|
	unsigned _counter,
 | 
						|
	bool _isHexLiteral
 | 
						|
)
 | 
						|
{
 | 
						|
	solAssert(
 | 
						|
		_numBytes > 0 && _numBytes <= 32,
 | 
						|
		"Proto ABIv2 fuzzer: Too short or too long a cropped string"
 | 
						|
	);
 | 
						|
 | 
						|
	// Number of masked nibbles is twice the number of bytes for a
 | 
						|
	// hex literal of _numBytes bytes. For a string literal, each nibble
 | 
						|
	// is treated as a character.
 | 
						|
	unsigned numMaskNibbles = _isHexLiteral ? _numBytes * 2 : _numBytes;
 | 
						|
 | 
						|
	// 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().
 | 
						|
	return maskUnsignedIntToHex(
 | 
						|
		_counter,
 | 
						|
		numMaskNibbles
 | 
						|
	).substr(startPos, numMaskNibbles);
 | 
						|
}
 | 
						|
 | 
						|
std::string ValueGetterVisitor::hexValueAsString(
 | 
						|
	unsigned _numBytes,
 | 
						|
	unsigned _counter,
 | 
						|
	bool _isHexLiteral,
 | 
						|
	bool _decorate
 | 
						|
)
 | 
						|
{
 | 
						|
	solAssert(_numBytes > 0 && _numBytes <= 32,
 | 
						|
	          "Proto ABIv2 fuzzer: Invalid hex length"
 | 
						|
	);
 | 
						|
 | 
						|
	// If _decorate is set, then we return a hex"" or a "" string.
 | 
						|
	if (_numBytes == 0)
 | 
						|
		return Whiskers(R"(<?decorate><?isHex>hex</isHex>""</decorate>)")
 | 
						|
			("decorate", _decorate)
 | 
						|
			("isHex", _isHexLiteral)
 | 
						|
			.render();
 | 
						|
 | 
						|
	// This is needed because solidity interprets a 20-byte 0x prefixed hex literal as an address
 | 
						|
	// payable type.
 | 
						|
	return Whiskers(R"(<?decorate><?isHex>hex</isHex>"</decorate><value><?decorate>"</decorate>)")
 | 
						|
		("decorate", _decorate)
 | 
						|
		("isHex", _isHexLiteral)
 | 
						|
		("value", croppedString(_numBytes, _counter, _isHexLiteral))
 | 
						|
		.render();
 | 
						|
}
 | 
						|
 | 
						|
std::string ValueGetterVisitor::fixedByteValueAsString(unsigned _width, unsigned _counter)
 | 
						|
{
 | 
						|
	solAssert(
 | 
						|
		(_width >= 1 && _width <= 32),
 | 
						|
		"Proto ABIv2 Fuzzer: Fixed byte width is not between 1--32"
 | 
						|
	);
 | 
						|
	return hexValueAsString(_width, _counter, /*isHexLiteral=*/true);
 | 
						|
}
 | 
						|
 | 
						|
std::string ValueGetterVisitor::addressValueAsString(unsigned _counter)
 | 
						|
{
 | 
						|
	return Whiskers(R"(address(<value>))")
 | 
						|
		("value", uintValueAsString(160, _counter))
 | 
						|
		.render();
 | 
						|
}
 | 
						|
 | 
						|
std::string ValueGetterVisitor::variableLengthValueAsString(
 | 
						|
	unsigned _numBytes,
 | 
						|
	unsigned _counter,
 | 
						|
	bool _isHexLiteral
 | 
						|
)
 | 
						|
{
 | 
						|
	// TODO: Move this to caller
 | 
						|
//	solAssert(_numBytes >= 0 && _numBytes <= s_maxDynArrayLength,
 | 
						|
//	          "Proto ABIv2 fuzzer: Invalid hex length"
 | 
						|
//	);
 | 
						|
	if (_numBytes == 0)
 | 
						|
		return Whiskers(R"(<?isHex>hex</isHex>"")")
 | 
						|
			("isHex", _isHexLiteral)
 | 
						|
			.render();
 | 
						|
 | 
						|
	unsigned numBytesRemaining = _numBytes;
 | 
						|
	// Stores the literal
 | 
						|
	string output{};
 | 
						|
	// If requested value is shorter than or exactly 32 bytes,
 | 
						|
	// the literal is the return value of hexValueAsString.
 | 
						|
	if (numBytesRemaining <= 32)
 | 
						|
		output = hexValueAsString(
 | 
						|
			numBytesRemaining,
 | 
						|
			_counter,
 | 
						|
			_isHexLiteral,
 | 
						|
			/*decorate=*/false
 | 
						|
		);
 | 
						|
		// If requested value is longer than 32 bytes, the literal
 | 
						|
		// is obtained by duplicating the return value of hexValueAsString
 | 
						|
		// until we reach a value of the requested size.
 | 
						|
	else
 | 
						|
	{
 | 
						|
		// Create a 32-byte value to be duplicated and
 | 
						|
		// update number of bytes to be appended.
 | 
						|
		// Stores the cached literal that saves us
 | 
						|
		// (expensive) calls to keccak256.
 | 
						|
		string cachedString = hexValueAsString(
 | 
						|
			/*numBytes=*/32,
 | 
						|
			             _counter,
 | 
						|
			             _isHexLiteral,
 | 
						|
			/*decorate=*/false
 | 
						|
		);
 | 
						|
		output = cachedString;
 | 
						|
		numBytesRemaining -= 32;
 | 
						|
 | 
						|
		// Append bytes from cachedString until
 | 
						|
		// we create a value of desired length.
 | 
						|
		unsigned numAppendedBytes;
 | 
						|
		while (numBytesRemaining > 0)
 | 
						|
		{
 | 
						|
			// We append at most 32 bytes at a time
 | 
						|
			numAppendedBytes = numBytesRemaining >= 32 ? 32 : numBytesRemaining;
 | 
						|
			output += cachedString.substr(
 | 
						|
				0,
 | 
						|
				// Double the substring length for hex literals since each
 | 
						|
				// character is actually half a byte (or a nibble).
 | 
						|
				_isHexLiteral ? numAppendedBytes * 2 : numAppendedBytes
 | 
						|
			);
 | 
						|
			numBytesRemaining -= numAppendedBytes;
 | 
						|
		}
 | 
						|
		solAssert(
 | 
						|
			numBytesRemaining == 0,
 | 
						|
			"Proto ABIv2 fuzzer: Logic flaw in variable literal creation"
 | 
						|
		);
 | 
						|
	}
 | 
						|
 | 
						|
	if (_isHexLiteral)
 | 
						|
		solAssert(
 | 
						|
			output.size() == 2 * _numBytes,
 | 
						|
			"Proto ABIv2 fuzzer: Generated hex literal is of incorrect length"
 | 
						|
		);
 | 
						|
	else
 | 
						|
		solAssert(
 | 
						|
			output.size() == _numBytes,
 | 
						|
			"Proto ABIv2 fuzzer: Generated string literal is of incorrect length"
 | 
						|
		);
 | 
						|
 | 
						|
	// Decorate output
 | 
						|
	return Whiskers(R"(<?isHexLiteral>hex</isHexLiteral>"<value>")")
 | 
						|
		("isHexLiteral", _isHexLiteral)
 | 
						|
		("value", output)
 | 
						|
		.render();
 | 
						|
}
 | 
						|
 | 
						|
string ValueGetterVisitor::bytesArrayValueAsString(unsigned _counter, bool _isHexLiteral)
 | 
						|
{
 | 
						|
	return variableLengthValueAsString(
 | 
						|
		getVarLength(_counter),
 | 
						|
		_counter,
 | 
						|
		_isHexLiteral
 | 
						|
	);
 | 
						|
}
 |