mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
abiv2 proto fuzzer: Fuzz return data coding
This commit is contained in:
parent
efeee15d32
commit
9772cc44a0
@ -92,8 +92,13 @@ message TestFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message Contract {
|
message Contract {
|
||||||
|
enum Test {
|
||||||
|
CALLDATA_CODER = 1;
|
||||||
|
RETURNDATA_CODER = 2;
|
||||||
|
}
|
||||||
required VarDecl state_vars = 1;
|
required VarDecl state_vars = 1;
|
||||||
required TestFunction testfunction = 2;
|
required TestFunction testfunction = 2;
|
||||||
|
required Test test = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
package dev.test.abiv2fuzzer;
|
package dev.test.abiv2fuzzer;
|
@ -114,7 +114,14 @@ template <typename T>
|
|||||||
pair<string, string> ProtoConverter::processType(T const& _type, bool _isValueType)
|
pair<string, string> ProtoConverter::processType(T const& _type, bool _isValueType)
|
||||||
{
|
{
|
||||||
ostringstream local, global;
|
ostringstream local, global;
|
||||||
auto [varName, paramName] = newVarNames(getNextVarCounter());
|
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{};
|
string location{};
|
||||||
if (!m_isStateVar && !_isValueType)
|
if (!m_isStateVar && !_isValueType)
|
||||||
location = "memory";
|
location = "memory";
|
||||||
@ -177,6 +184,16 @@ pair<string, string> ProtoConverter::varDecl(
|
|||||||
_paramName,
|
_paramName,
|
||||||
((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
|
((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
|
// Update dyn param only if necessary
|
||||||
if (tVisitor.isLastDynParamRightPadded())
|
if (tVisitor.isLastDynParamRightPadded())
|
||||||
@ -259,6 +276,41 @@ void ProtoConverter::appendTypedParams(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// Adds the qualifier "calldata" to non-value parameter of an external function.
|
||||||
void ProtoConverter::appendTypedParamsExternal(
|
void ProtoConverter::appendTypedParamsExternal(
|
||||||
bool _isValueType,
|
bool _isValueType,
|
||||||
@ -310,7 +362,6 @@ std::string ProtoConverter::typedParametersAsString(CalleeType _calleeType)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test function to be called externally.
|
|
||||||
string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDefs)
|
string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDefs)
|
||||||
{
|
{
|
||||||
// TODO: Support more than one but less than N local variables
|
// TODO: Support more than one but less than N local variables
|
||||||
@ -320,38 +371,76 @@ string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDe
|
|||||||
string localVarDefs = localVarBuffers.second;
|
string localVarDefs = localVarBuffers.second;
|
||||||
|
|
||||||
ostringstream testBuffer;
|
ostringstream testBuffer;
|
||||||
string functionDecl = "function test() public returns (uint)";
|
|
||||||
|
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>
|
testBuffer << Whiskers(R"(<structTypeDecl>
|
||||||
<functionDecl> {
|
<testFunction>
|
||||||
|
<?calldata>
|
||||||
|
<functionDeclCalldata> {
|
||||||
<storageVarDefs>
|
<storageVarDefs>
|
||||||
<localVarDefs>
|
<localVarDefs>
|
||||||
<testCode>
|
<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)
|
("structTypeDecl", structTypeDecl)
|
||||||
("functionDecl", functionDecl)
|
("functionDeclCalldata", functionDeclCalldata)
|
||||||
|
("functionDeclReturndata", functionDeclReturndata)
|
||||||
("storageVarDefs", _storageVarDefs)
|
("storageVarDefs", _storageVarDefs)
|
||||||
("localVarDefs", localVarDefs)
|
("localVarDefs", localVarDefs)
|
||||||
("testCode", testCode(_x.invalid_encoding_length()))
|
("calldataTestCode", testCallDataFunction(_x.invalid_encoding_length()))
|
||||||
|
("returndataTestCode", testReturnDataFunction())
|
||||||
|
("return_types", m_types.str())
|
||||||
|
("return_values", m_argsCoder.str())
|
||||||
.render();
|
.render();
|
||||||
return testBuffer.str();
|
return testBuffer.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
string ProtoConverter::testCode(unsigned _invalidLength)
|
string ProtoConverter::testCallDataFunction(unsigned _invalidLength)
|
||||||
{
|
{
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
uint returnVal = this.coder_public(<parameterNames>);
|
uint returnVal = this.coder_calldata_public(<argumentNames>);
|
||||||
if (returnVal != 0)
|
if (returnVal != 0)
|
||||||
return returnVal;
|
return returnVal;
|
||||||
|
|
||||||
returnVal = this.coder_external(<parameterNames>);
|
returnVal = this.coder_calldata_external(<argumentNames>);
|
||||||
if (returnVal != 0)
|
if (returnVal != 0)
|
||||||
return uint(200000) + returnVal;
|
return uint(200000) + returnVal;
|
||||||
|
|
||||||
<?atLeastOneVar>
|
<?atLeastOneVar>
|
||||||
bytes memory argumentEncoding = abi.encode(<parameterNames>);
|
bytes memory argumentEncoding = abi.encode(<argumentNames>);
|
||||||
|
|
||||||
returnVal = checkEncodedCall(
|
returnVal = checkEncodedCall(
|
||||||
this.coder_public.selector,
|
this.coder_calldata_public.selector,
|
||||||
argumentEncoding,
|
argumentEncoding,
|
||||||
<invalidLengthFuzz>,
|
<invalidLengthFuzz>,
|
||||||
<isRightPadded>
|
<isRightPadded>
|
||||||
@ -360,7 +449,7 @@ string ProtoConverter::testCode(unsigned _invalidLength)
|
|||||||
return returnVal;
|
return returnVal;
|
||||||
|
|
||||||
returnVal = checkEncodedCall(
|
returnVal = checkEncodedCall(
|
||||||
this.coder_external.selector,
|
this.coder_calldata_external.selector,
|
||||||
argumentEncoding,
|
argumentEncoding,
|
||||||
<invalidLengthFuzz>,
|
<invalidLengthFuzz>,
|
||||||
<isRightPadded>
|
<isRightPadded>
|
||||||
@ -370,26 +459,32 @@ string ProtoConverter::testCode(unsigned _invalidLength)
|
|||||||
</atLeastOneVar>
|
</atLeastOneVar>
|
||||||
return 0;
|
return 0;
|
||||||
)")
|
)")
|
||||||
("parameterNames", dev::suffixedVariableNameList(s_varNamePrefix, 0, m_varCounter))
|
("argumentNames", m_argsCoder.str())
|
||||||
("invalidLengthFuzz", std::to_string(_invalidLength))
|
("invalidLengthFuzz", std::to_string(_invalidLength))
|
||||||
("isRightPadded", isLastDynParamRightPadded() ? "true" : "false")
|
("isRightPadded", isLastDynParamRightPadded() ? "true" : "false")
|
||||||
("atLeastOneVar", m_varCounter > 0)
|
("atLeastOneVar", m_varCounter > 0)
|
||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
string ProtoConverter::helperFunctions()
|
string ProtoConverter::testReturnDataFunction()
|
||||||
{
|
{
|
||||||
stringstream helperFuncs;
|
return Whiskers(R"(
|
||||||
helperFuncs << R"(
|
<?varsPresent>
|
||||||
function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
|
(<varDecl>) = this.coder_returndata_external();
|
||||||
if(a.length != b.length)
|
<equality_checks>
|
||||||
return false;
|
</varsPresent>
|
||||||
for (uint i = 0; i < a.length; i++)
|
return 0;
|
||||||
if (a[i] != b[i])
|
)")
|
||||||
return false;
|
("varsPresent", !m_typedReturn.str().empty())
|
||||||
return true;
|
("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
|
/// Accepts function selector, correct argument encoding, and length of
|
||||||
/// invalid encoding and returns the correct and incorrect abi encoding
|
/// invalid encoding and returns the correct and incorrect abi encoding
|
||||||
/// for calling the function specified by the function selector.
|
/// for calling the function specified by the function selector.
|
||||||
@ -452,19 +547,18 @@ string ProtoConverter::helperFunctions()
|
|||||||
if (success == true)
|
if (success == true)
|
||||||
return 400001;
|
return 400001;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
})";
|
||||||
)";
|
|
||||||
|
|
||||||
// These are callee functions that encode from storage, decode to
|
// These are callee functions that encode from storage, decode to
|
||||||
// memory/calldata and check if decoded value matches storage value
|
// memory/calldata and check if decoded value matches storage value
|
||||||
// return true on successful match, false otherwise
|
// return true on successful match, false otherwise
|
||||||
helperFuncs << Whiskers(R"(
|
calldataHelperFuncs << Whiskers(R"(
|
||||||
function coder_public(<parameters_memory>) public pure returns (uint) {
|
function coder_calldata_public(<parameters_memory>) public pure returns (uint) {
|
||||||
<equality_checks>
|
<equality_checks>
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function coder_external(<parameters_calldata>) external pure returns (uint) {
|
function coder_calldata_external(<parameters_calldata>) external pure returns (uint) {
|
||||||
<equality_checks>
|
<equality_checks>
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -473,6 +567,25 @@ string ProtoConverter::helperFunctions()
|
|||||||
("equality_checks", equalityChecksAsString())
|
("equality_checks", equalityChecksAsString())
|
||||||
("parameters_calldata", typedParametersAsString(CalleeType::EXTERNAL))
|
("parameters_calldata", typedParametersAsString(CalleeType::EXTERNAL))
|
||||||
.render();
|
.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();
|
return helperFuncs.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,6 +594,9 @@ void ProtoConverter::visit(Contract const& _x)
|
|||||||
string pragmas = R"(pragma solidity >=0.0;
|
string pragmas = R"(pragma solidity >=0.0;
|
||||||
pragma experimental ABIEncoderV2;)";
|
pragma experimental ABIEncoderV2;)";
|
||||||
|
|
||||||
|
// Record test spec
|
||||||
|
m_test = _x.test();
|
||||||
|
|
||||||
// TODO: Support more than one but less than N state variables
|
// TODO: Support more than one but less than N state variables
|
||||||
auto storageBuffers = visit(_x.state_vars());
|
auto storageBuffers = visit(_x.state_vars());
|
||||||
string storageVarDecls = storageBuffers.first;
|
string storageVarDecls = storageBuffers.first;
|
||||||
@ -499,7 +615,7 @@ pragma experimental ABIEncoderV2;)";
|
|||||||
ostringstream contractBody;
|
ostringstream contractBody;
|
||||||
contractBody << storageVarDecls
|
contractBody << storageVarDecls
|
||||||
<< testFunction
|
<< testFunction
|
||||||
<< helperFunctions();
|
<< commonHelperFunctions();
|
||||||
m_output << Whiskers(R"(<pragmas>
|
m_output << Whiskers(R"(<pragmas>
|
||||||
<contractStart>
|
<contractStart>
|
||||||
<contractBody>
|
<contractBody>
|
||||||
@ -1085,4 +1201,4 @@ string ValueGetterVisitor::bytesArrayValueAsString(unsigned _counter, bool _isHe
|
|||||||
_counter,
|
_counter,
|
||||||
_isHexLiteral
|
_isHexLiteral
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -19,84 +19,118 @@
|
|||||||
/**
|
/**
|
||||||
* Template of the solidity test program generated by this converter is as follows:
|
* Template of the solidity test program generated by this converter is as follows:
|
||||||
*
|
*
|
||||||
* pragma solidity >=0.0;
|
* pragma solidity >=0.0;
|
||||||
* pragma experimental ABIEncoderV2;
|
* pragma experimental ABIEncoderV2;
|
||||||
*
|
*
|
||||||
* contract C {
|
* contract C {
|
||||||
* // State variable
|
* // State variable
|
||||||
* string x_0;
|
* string sv_0;
|
||||||
* // Test function that is called by the VM.
|
* // Test function that is called by the VM.
|
||||||
* function test() public returns (uint) {
|
* // There are 2 variations of this function: one returns
|
||||||
* // Local variable
|
* // the output of test_calldata_coding() and the other
|
||||||
* bytes x_1 = "1";
|
* // returns the output of test_returndata_coding(). The
|
||||||
* x_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
|
* // proto field called Test decides which one of the two
|
||||||
* uint returnVal = this.coder_public(x_0, x_1);
|
* // are chosen for creating a test case.
|
||||||
* if (returnVal != 0)
|
* function test() public returns (uint) {
|
||||||
* return returnVal;
|
* // The protobuf field "Contract.test" decides which of
|
||||||
* // Since the return codes in the public and external coder functions are identical
|
* // the two internal functions "test_calldata_coding()"
|
||||||
* // we offset error code by a fixed amount (200000) for differentiation.
|
* // and "test_returndata_coding()" are called. Here,
|
||||||
* returnVal = this.coder_external(x_0, x_1);
|
* // we assume that the protobuf field equals "CALLDATA_CODER"
|
||||||
* if (returnVal != 0)
|
* return this.test_calldata_coding()
|
||||||
* return 200000 + returnVal;
|
* }
|
||||||
* // Encode parameters
|
|
||||||
* bytes memory argumentEncoding = abi.encode(<parameter_names>);
|
|
||||||
* returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>);
|
|
||||||
* // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data.
|
|
||||||
* if (returnVal != 0)
|
|
||||||
* return returnVal;
|
|
||||||
*
|
*
|
||||||
* returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>);
|
* // The following function is generated if the protobuf field
|
||||||
* // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data.
|
* // "Contract.test" is equal to "RETURNDATA_CODER".
|
||||||
* // Offset return value to distinguish between failures originating from coder_public and coder_external.
|
* function test_returndata_coding() internal returns (uint) {
|
||||||
* if (returnVal != 0)
|
* string memory lv_0, bytes memory lv_1 = test_returndata_external();
|
||||||
* return uint(200000) + returnVal;
|
* if (lv_0 != 044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d)
|
||||||
* // Return zero if all checks pass.
|
* return 1;
|
||||||
* return 0;
|
* if (lv_1 != "1")
|
||||||
* }
|
* return 2;
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
*
|
*
|
||||||
* /// Accepts function selector, correct argument encoding, and an invalid encoding length as input.
|
* // The following function is generated if the protobuf field
|
||||||
* /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding
|
* // "Contract.test" is equal to "RETURNDATA_CODER".
|
||||||
* /// succeeds. Returns zero if both calls meet expectation.
|
* function test_returndata_external() external returns (string memory, bytes memory)
|
||||||
* function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
* {
|
||||||
* public returns (uint) {
|
* sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
|
||||||
* ...
|
* bytes memory lv_0 = "1";
|
||||||
* }
|
* return (sv_0, lv_0);
|
||||||
|
* }
|
||||||
*
|
*
|
||||||
* /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns
|
* // The following function is generated if the protobuf field
|
||||||
* /// the correct and incorrect abi encoding for calling the function specified by the function selector.
|
* // "Contract.test" is equal to "CALLDATA_CODER".
|
||||||
* function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
* function test_calldata_coding() internal returns (uint) {
|
||||||
* internal pure returns (bytes memory, bytes memory) {
|
* // Local variable
|
||||||
* ...
|
* bytes lv_1 = "1";
|
||||||
* }
|
* sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
|
||||||
|
* uint returnVal = this.coder_public(sv_0, lv_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 (200000) for differentiation.
|
||||||
|
* returnVal = this.coder_external(sv_0, lv_1);
|
||||||
|
* if (returnVal != 0)
|
||||||
|
* return 200000 + returnVal;
|
||||||
|
* // Encode parameters
|
||||||
|
* bytes memory argumentEncoding = abi.encode(<parameter_names>);
|
||||||
|
* returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||||
|
* // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data.
|
||||||
|
* if (returnVal != 0)
|
||||||
|
* return returnVal;
|
||||||
*
|
*
|
||||||
* /// Compares two dynamically sized bytes arrays for equality.
|
* returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||||
* function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
|
* // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data.
|
||||||
* ...
|
* // Offset return value to distinguish between failures originating from coder_public and coder_external.
|
||||||
* }
|
* if (returnVal != 0)
|
||||||
|
* return uint(200000) + returnVal;
|
||||||
|
* // Return zero if all checks pass.
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
*
|
*
|
||||||
* // Public function that is called by test() function. Accepts one or more arguments and returns
|
* /// Accepts function selector, correct argument encoding, and an invalid encoding length as input.
|
||||||
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
|
* /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding
|
||||||
* function coder_public(string memory c_0, bytes memory c_1) public pure returns (uint) {
|
* /// succeeds. Returns zero if both calls meet expectation.
|
||||||
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
* function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
||||||
* return 1;
|
* public returns (uint) {
|
||||||
* if (!bytesCompare(c_1, "1"))
|
* ...
|
||||||
* return 2;
|
* }
|
||||||
* return 0;
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* // External function that is called by test() function. Accepts one or more arguments and returns
|
* /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns
|
||||||
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
|
* /// the correct and incorrect abi encoding for calling the function specified by the function selector.
|
||||||
* function coder_external(string calldata c_0, bytes calldata c_1) external pure returns (uint) {
|
* function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
||||||
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
* internal pure returns (bytes memory, bytes memory) {
|
||||||
* return 1;
|
* ...
|
||||||
* if (!bytesCompare(c_1, "1"))
|
* }
|
||||||
* return 2;
|
*
|
||||||
* return 0;
|
* /// Compares two dynamically sized bytes arrays for equality.
|
||||||
* }
|
* 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 pure 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 pure returns (uint) {
|
||||||
|
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
||||||
|
* return 1;
|
||||||
|
* if (!bytesCompare(c_1, "1"))
|
||||||
|
* return 2;
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
namespace test
|
namespace test
|
||||||
@ -242,6 +276,20 @@ private:
|
|||||||
Delimiter _delimiter = Delimiter::ADD
|
Delimiter _delimiter = Delimiter::ADD
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Append types to typed stream used by returndata coders.
|
||||||
|
void appendTypes(
|
||||||
|
bool _isValueType,
|
||||||
|
std::string const& _typeString,
|
||||||
|
Delimiter _delimiter
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Append typed return value.
|
||||||
|
void appendTypedReturn(
|
||||||
|
bool _isValueType,
|
||||||
|
std::string const& _typeString,
|
||||||
|
Delimiter _delimiter
|
||||||
|
);
|
||||||
|
|
||||||
/// Returns a Solidity variable declaration statement
|
/// Returns a Solidity variable declaration statement
|
||||||
/// @param _type: string containing Solidity type of the
|
/// @param _type: string containing Solidity type of the
|
||||||
/// variable to be declared.
|
/// variable to be declared.
|
||||||
@ -262,11 +310,17 @@ private:
|
|||||||
/// Return comma separated typed function parameters as string
|
/// Return comma separated typed function parameters as string
|
||||||
std::string typedParametersAsString(CalleeType _calleeType);
|
std::string typedParametersAsString(CalleeType _calleeType);
|
||||||
|
|
||||||
/// Return Solidity helper functions as string
|
/// Return commonly used Solidity helper functions as string
|
||||||
std::string helperFunctions();
|
std::string commonHelperFunctions();
|
||||||
|
|
||||||
/// Return top-level test code as string
|
/// Return helper functions used to test calldata coding
|
||||||
std::string testCode(unsigned _invalidLength);
|
std::string calldataHelperFunctions();
|
||||||
|
|
||||||
|
/// Return top-level calldata coder test function as string
|
||||||
|
std::string testCallDataFunction(unsigned _invalidLength);
|
||||||
|
|
||||||
|
/// Return top-level returndata coder test function as string
|
||||||
|
std::string testReturnDataFunction();
|
||||||
|
|
||||||
/// Return the next variable count that is used for
|
/// Return the next variable count that is used for
|
||||||
/// variable naming.
|
/// variable naming.
|
||||||
@ -276,15 +330,30 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return a pair of names for Solidity variable and the same variable when
|
/// Return a pair of names for Solidity variable and the same variable when
|
||||||
/// passed as a function parameter.
|
/// passed either as a function parameter or used to store the tuple
|
||||||
static std::pair<std::string, std::string> newVarNames(unsigned _varCounter)
|
/// returned from a function.
|
||||||
|
/// @param _varCounter: name suffix
|
||||||
|
/// @param _stateVar: predicate that is true for state variables, false otherwise
|
||||||
|
std::pair<std::string, std::string> newVarNames(unsigned _varCounter, bool _stateVar)
|
||||||
{
|
{
|
||||||
|
std::string varName = _stateVar ? s_stateVarNamePrefix : s_localVarNamePrefix;
|
||||||
return std::make_pair(
|
return std::make_pair(
|
||||||
s_varNamePrefix + std::to_string(_varCounter),
|
varName + std::to_string(_varCounter),
|
||||||
s_paramNamePrefix + std::to_string(_varCounter)
|
paramName() + std::to_string(_varCounter)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string paramName()
|
||||||
|
{
|
||||||
|
switch (m_test)
|
||||||
|
{
|
||||||
|
case Contract_Test::Contract_Test_CALLDATA_CODER:
|
||||||
|
return s_paramNamePrefix;
|
||||||
|
case Contract_Test::Contract_Test_RETURNDATA_CODER:
|
||||||
|
return s_localVarNamePrefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if the last dynamically encoded Solidity type is right
|
/// Checks if the last dynamically encoded Solidity type is right
|
||||||
/// padded, returning true if it is and false otherwise.
|
/// padded, returning true if it is and false otherwise.
|
||||||
bool isLastDynParamRightPadded()
|
bool isLastDynParamRightPadded()
|
||||||
@ -303,6 +372,12 @@ private:
|
|||||||
/// Contains typed parameter list to be passed to callee functions
|
/// Contains typed parameter list to be passed to callee functions
|
||||||
std::ostringstream m_typedParamsExternal;
|
std::ostringstream m_typedParamsExternal;
|
||||||
std::ostringstream m_typedParamsPublic;
|
std::ostringstream m_typedParamsPublic;
|
||||||
|
/// Contains type stream to be used in returndata coder function
|
||||||
|
/// signature
|
||||||
|
std::ostringstream m_types;
|
||||||
|
std::ostringstream m_typedReturn;
|
||||||
|
/// Argument names to be passed to coder functions
|
||||||
|
std::ostringstream m_argsCoder;
|
||||||
/// Predicate that is true if we are in contract scope
|
/// Predicate that is true if we are in contract scope
|
||||||
bool m_isStateVar;
|
bool m_isStateVar;
|
||||||
unsigned m_counter;
|
unsigned m_counter;
|
||||||
@ -316,9 +391,12 @@ private:
|
|||||||
/// Struct counter
|
/// Struct counter
|
||||||
unsigned m_structCounter;
|
unsigned m_structCounter;
|
||||||
unsigned m_numStructsAdded;
|
unsigned m_numStructsAdded;
|
||||||
|
/// Enum stating abiv2 coder to be tested
|
||||||
|
Contract_Test m_test;
|
||||||
/// Prefixes for declared and parameterized variable names
|
/// Prefixes for declared and parameterized variable names
|
||||||
static auto constexpr s_varNamePrefix = "x_";
|
static auto constexpr s_localVarNamePrefix = "lv_";
|
||||||
static auto constexpr s_paramNamePrefix = "c_";
|
static auto constexpr s_stateVarNamePrefix = "sv_";
|
||||||
|
static auto constexpr s_paramNamePrefix = "p_";
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Visitor interface for Solidity protobuf types.
|
/// Visitor interface for Solidity protobuf types.
|
||||||
@ -857,4 +935,4 @@ public:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user