[isoltest] Updates type retrieval from ABI and its formatting.

This commit is contained in:
Erik Kundt
2019-08-05 16:15:07 +02:00
parent 38285b33d7
commit f914415fb2
7 changed files with 472 additions and 168 deletions
+255 -38
View File
@@ -17,12 +17,18 @@
#include <test/libsolidity/util/ContractABIUtils.h>
#include <test/libsolidity/util/SoltestErrors.h>
#include <liblangutil/Common.h>
#include <boost/algorithm/string.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/bind.hpp>
#include <boost/range/algorithm_ext/for_each.hpp>
#include <fstream>
#include <memory>
#include <numeric>
#include <regex>
#include <stdexcept>
@@ -33,55 +39,266 @@ using namespace dev::solidity::test;
using namespace std;
using namespace soltest;
dev::solidity::test::ParameterList ContractABIUtils::parametersFromJson(
Json::Value const& _contractABI,
string const& _functionName
) const
namespace
{
ParameterList abiParams;
using ParameterList = dev::solidity::test::ParameterList;
size_t arraySize(string const& _arrayType)
{
auto leftBrack = _arrayType.find("[");
auto rightBrack = _arrayType.rfind("]");
soltestAssert(
leftBrack != string::npos &&
rightBrack != string::npos &&
rightBrack == _arrayType.size() - 1 &&
leftBrack < rightBrack,
""
);
string size = _arrayType.substr(leftBrack + 1, rightBrack - leftBrack - 1);
return static_cast<size_t>(stoi(size));
}
bool isBool(string const& _type)
{
return _type == "bool";
}
bool isUint(string const& _type)
{
return regex_match(_type, regex{"uint\\d*"});
}
bool isInt(string const& _type)
{
return regex_match(_type, regex{"int\\d*"});
}
bool isFixedBytes(string const& _type)
{
return regex_match(_type, regex{"bytes\\d+"});
}
bool isBytes(string const& _type)
{
return regex_match(_type, regex{"\\bbytes\\b"});
}
bool isString(string const& _type)
{
return _type == "string";
}
bool isFixedBoolArray(string const& _type)
{
return regex_match(_type, regex{"bool\\[\\d+\\]"});
}
bool isFixedUintArray(string const& _type)
{
return regex_match(_type, regex{"uint\\d*\\[\\d+\\]"});
}
bool isFixedIntArray(string const& _type)
{
return regex_match(_type, regex{"int\\d*\\[\\d+\\]"});
}
bool isFixedStringArray(string const& _type)
{
return regex_match(_type, regex{"string\\[\\d+\\]"});
}
bool isTuple(string const& _type)
{
return _type == "tuple";
}
bool isFixedTupleArray(string const& _type)
{
return regex_match(_type, regex{"tuple\\[\\d+\\]"});
}
string functionSignatureFromABI(Json::Value const& _functionABI)
{
auto inputs = _functionABI["inputs"];
string signature = {_functionABI["name"].asString() + "("};
size_t parameterCount = 0;
for (auto const& input: inputs)
{
parameterCount++;
signature += input["type"].asString();
if (parameterCount < inputs.size())
signature += ",";
}
return signature + ")";
}
}
boost::optional<dev::solidity::test::ParameterList> ContractABIUtils::parametersFromJsonOutputs(
ErrorReporter& _errorReporter,
Json::Value const& _contractABI,
string const& _functionSignature
)
{
if (!_contractABI)
return boost::none;
for (auto const& function: _contractABI)
if (function["name"] == _functionName)
if (_functionSignature == functionSignatureFromABI(function))
{
ParameterList inplaceTypeParams;
ParameterList dynamicTypeParams;
ParameterList finalParams;
for (auto const& output: function["outputs"])
{
auto types = fromTypeName(output["type"].asString());
for (auto const& type: types)
abiParams.push_back(Parameter{bytes(), "", type, FormatInfo{}});
}
string type = output["type"].asString();
return abiParams;
ABITypes inplaceTypes;
ABITypes dynamicTypes;
if (appendTypesFromName(output, inplaceTypes, dynamicTypes))
{
for (auto const& type: inplaceTypes)
inplaceTypeParams.push_back(Parameter{bytes(), "", type, FormatInfo{}});
for (auto const& type: dynamicTypes)
dynamicTypeParams.push_back(Parameter{bytes(), "", type, FormatInfo{}});
}
else
{
_errorReporter.warning(
"Could not convert \"" + type +
"\" to internal ABI type representation. Falling back to default encoding."
);
return boost::none;
}
finalParams += inplaceTypeParams;
inplaceTypeParams.clear();
}
return boost::optional<ParameterList>(finalParams + dynamicTypeParams);
}
return boost::none;
}
std::vector<ABIType> ContractABIUtils::fromTypeName(string const& _type) const
bool ContractABIUtils::appendTypesFromName(
Json::Value const& _functionOutput,
ABITypes& _inplaceTypes,
ABITypes& _dynamicTypes,
bool _isCompoundType
)
{
static regex s_boolType{"(bool)"};
static regex s_uintType{"(uint\\d*)"};
static regex s_intType{"(int\\d*)"};
static regex s_bytesType{"(bytes\\d+)"};
static regex s_dynBytesType{"(\\bbytes\\b)"};
static regex s_stringType{"(string)"};
string type = _functionOutput["type"].asString();
if (isBool(type))
_inplaceTypes.push_back(ABIType{ABIType::Boolean});
else if (isUint(type))
_inplaceTypes.push_back(ABIType{ABIType::UnsignedDec});
else if (isInt(type))
_inplaceTypes.push_back(ABIType{ABIType::SignedDec});
else if (isFixedBytes(type))
_inplaceTypes.push_back(ABIType{ABIType::Hex});
else if (isString(type))
{
_inplaceTypes.push_back(ABIType{ABIType::Hex});
vector<ABIType> abiTypes;
if (regex_match(_type, s_boolType))
abiTypes.push_back(ABIType{ABIType::Boolean, ABIType::AlignRight, 32});
else if (regex_match(_type, s_uintType))
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
else if (regex_match(_type, s_intType))
abiTypes.push_back(ABIType{ABIType::SignedDec, ABIType::AlignRight, 32});
else if (regex_match(_type, s_bytesType))
abiTypes.push_back(ABIType{ABIType::Hex, ABIType::AlignRight, 32});
else if (regex_match(_type, s_dynBytesType))
{
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
abiTypes.push_back(ABIType{ABIType::HexString, ABIType::AlignLeft, 32});
if (_isCompoundType)
_dynamicTypes.push_back(ABIType{ABIType::Hex});
_dynamicTypes.push_back(ABIType{ABIType::UnsignedDec});
_dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft});
}
else if (regex_match(_type, s_stringType))
else if (isTuple(type))
{
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
abiTypes.push_back(ABIType{ABIType::UnsignedDec, ABIType::AlignRight, 32});
abiTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft, 32});
ABITypes inplaceTypes;
ABITypes dynamicTypes;
for (auto const& component: _functionOutput["components"])
appendTypesFromName(component, inplaceTypes, dynamicTypes, true);
_dynamicTypes += inplaceTypes + dynamicTypes;
}
else if (isFixedBoolArray(type))
_inplaceTypes += vector<ABIType>(arraySize(type), ABIType{ABIType::Boolean});
else if (isFixedUintArray(type))
_inplaceTypes += vector<ABIType>(arraySize(type), ABIType{ABIType::UnsignedDec});
else if (isFixedIntArray(type))
_inplaceTypes += vector<ABIType>(arraySize(type), ABIType{ABIType::SignedDec});
else if (isFixedStringArray(type))
{
_inplaceTypes.push_back(ABIType{ABIType::Hex});
_dynamicTypes += vector<ABIType>(arraySize(type), ABIType{ABIType::Hex});
for (size_t i = 0; i < arraySize(type); i++)
{
_dynamicTypes.push_back(ABIType{ABIType::UnsignedDec});
_dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft});
}
}
else if (isBytes(type))
return false;
else if (isFixedTupleArray(type))
return false;
else
return false;
return true;
}
void ContractABIUtils::overwriteParameters(
ErrorReporter& _errorReporter,
dev::solidity::test::ParameterList& _targetParameters,
dev::solidity::test::ParameterList const& _sourceParameters
)
{
boost::for_each(
_sourceParameters,
_targetParameters,
boost::bind<void>(
[&](Parameter _a, Parameter& _b) -> void
{
if (
_a.abiType.size != _b.abiType.size ||
_a.abiType.type != _b.abiType.type
)
{
_errorReporter.warning("Type or size of parameter(s) does not match.");
_b = _a;
}
},
_1,
_2
)
);
}
dev::solidity::test::ParameterList ContractABIUtils::preferredParameters(
ErrorReporter& _errorReporter,
dev::solidity::test::ParameterList const& _targetParameters,
dev::solidity::test::ParameterList const& _sourceParameters,
bytes const& _bytes
)
{
if (_targetParameters.size() != _sourceParameters.size())
{
auto sizeFold = [](size_t const _a, Parameter const& _b) { return _a + _b.abiType.size; };
size_t encodingSize = accumulate(_targetParameters.begin(), _targetParameters.end(), size_t{0}, sizeFold);
_errorReporter.warning(
"Encoding does not match byte range. The call returned " +
to_string(_bytes.size()) + " bytes, but " +
to_string(encodingSize) + " bytes were expected."
);
return _sourceParameters;
}
else
abiTypes.push_back(ABIType{ABIType::None, ABIType::AlignRight, 0});
return abiTypes;
return _targetParameters;
}