Merge pull request #11682 from ethereum/fixedPointTypes

Fixed point types for isoltest
This commit is contained in:
chriseth 2021-08-13 09:37:29 +02:00 committed by GitHub
commit d7a802e4bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 175 additions and 21 deletions

View File

@ -108,6 +108,7 @@ detect_stray_source_files("${libsolidity_sources}" "libsolidity/")
set(libsolidity_util_sources set(libsolidity_util_sources
libsolidity/util/BytesUtils.cpp libsolidity/util/BytesUtils.cpp
libsolidity/util/BytesUtilsTests.cpp
libsolidity/util/BytesUtils.h libsolidity/util/BytesUtils.h
libsolidity/util/ContractABIUtils.cpp libsolidity/util/ContractABIUtils.cpp
libsolidity/util/ContractABIUtils.h libsolidity/util/ContractABIUtils.h

View File

@ -17,18 +17,13 @@
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
#include <test/libsolidity/util/BytesUtils.h> #include <test/libsolidity/util/BytesUtils.h>
#include <test/libsolidity/util/ContractABIUtils.h> #include <test/libsolidity/util/ContractABIUtils.h>
#include <test/libsolidity/util/SoltestErrors.h> #include <test/libsolidity/util/SoltestErrors.h>
#include <liblangutil/Common.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <fstream>
#include <iomanip> #include <iomanip>
#include <memory> #include <memory>
#include <regex> #include <regex>
@ -96,6 +91,22 @@ bytes BytesUtils::convertNumber(string const& _literal)
} }
} }
bytes BytesUtils::convertFixedPoint(string const& _literal, size_t& o_fractionalDigits)
{
size_t dotPos = _literal.find('.');
string valueInteger = _literal.substr(0, dotPos);
string valueFraction = _literal.substr(dotPos + 1);
o_fractionalDigits = valueFraction.length();
try
{
return util::toBigEndian(u256(valueInteger + valueFraction));
}
catch (std::exception const&)
{
BOOST_THROW_EXCEPTION(TestParserError("Number encoding invalid."));
}
}
bytes BytesUtils::convertHexNumber(string const& _literal) bytes BytesUtils::convertHexNumber(string const& _literal)
{ {
try try
@ -206,6 +217,28 @@ string BytesUtils::formatString(bytes const& _bytes, size_t _cutOff)
return os.str(); return os.str();
} }
std::string BytesUtils::formatFixedPoint(bytes const& _bytes, bool _signed, size_t _fractionalDigits)
{
string decimal;
bool negative = false;
if (_signed)
{
s256 signedValue{u2s(fromBigEndian<u256>(_bytes))};
negative = (signedValue < 0);
decimal = signedValue.str();
}
else
decimal = fromBigEndian<u256>(_bytes).str();
if (_fractionalDigits > 0)
{
size_t numDigits = decimal.length() - (negative ? 1 : 0);
if (_fractionalDigits > numDigits)
decimal.insert(negative ? 1 : 0, string(_fractionalDigits - numDigits, '0'));
decimal.insert(decimal.length() - _fractionalDigits, ".");
}
return decimal;
}
string BytesUtils::formatRawBytes( string BytesUtils::formatRawBytes(
bytes const& _bytes, bytes const& _bytes,
solidity::frontend::test::ParameterList const& _parameters, solidity::frontend::test::ParameterList const& _parameters,
@ -296,8 +329,11 @@ string BytesUtils::formatBytes(
case ABIType::String: case ABIType::String:
os << formatString(_bytes, _bytes.size() - countRightPaddedZeros(_bytes)); os << formatString(_bytes, _bytes.size() - countRightPaddedZeros(_bytes));
break; break;
case ABIType::Failure: case ABIType::UnsignedFixedPoint:
case ABIType::SignedFixedPoint:
os << formatFixedPoint(_bytes, _abiType.type == ABIType::SignedFixedPoint, _abiType.fractionalDigits);
break; break;
case ABIType::Failure:
case ABIType::None: case ABIType::None:
break; break;
} }

View File

@ -54,6 +54,10 @@ public:
/// representation of the decimal number literal. Throws if conversion fails. /// representation of the decimal number literal. Throws if conversion fails.
static bytes convertNumber(std::string const& _literal); static bytes convertNumber(std::string const& _literal);
/// Tries to convert \param _literal to an unpadded `bytes`
/// representation of the decimal number literal. Throws if conversion fails.
static bytes convertFixedPoint(std::string const& _literal, size_t& o_fractionalDigits);
/// Tries to convert \param _literal to an unpadded `bytes` /// Tries to convert \param _literal to an unpadded `bytes`
/// representation of the hex literal. Throws if conversion fails. /// representation of the hex literal. Throws if conversion fails.
static bytes convertHexNumber(std::string const& _literal); static bytes convertHexNumber(std::string const& _literal);
@ -98,6 +102,10 @@ public:
return formatString(_bytes, _bytes.size()); return formatString(_bytes, _bytes.size());
} }
/// Converts \param _bytes to a soltest-compliant and human-readable
/// decimal string representation of a byte array. Format of \param _bytes is binary.
static std::string formatFixedPoint(bytes const& _bytes, bool _signed, size_t _fractionalDigits);
/// Used to print returned bytes from function calls to the commandline. /// Used to print returned bytes from function calls to the commandline.
/// Returns a string representation of given _bytes in ranges of 32 bytes. /// Returns a string representation of given _bytes in ranges of 32 bytes.
/// If _withSignature is true, the first 4 bytes will be formatted separately. /// If _withSignature is true, the first 4 bytes will be formatted separately.

View File

@ -0,0 +1,82 @@
/*
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/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <boost/test/unit_test.hpp>
#include <test/libsolidity/util/BytesUtils.h>
#include <libsolutil/CommonData.h>
using namespace std;
using namespace solidity::util;
using namespace solidity::test;
namespace solidity::frontend::test
{
BOOST_AUTO_TEST_SUITE(BytesUtilsTest)
BOOST_AUTO_TEST_CASE(format_fixed)
{
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{0}), true, 2),
".00"
);
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{1}), true, 2),
".01"
);
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{123}), true, 2),
"1.23"
);
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{-1}), true, 2),
"-.01"
);
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{-12}), true, 2),
"-.12"
);
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{-123}), true, 2),
"-1.23"
);
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{-1234}), true, 2),
"-12.34"
);
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{-12345}), true, 2),
"-123.45"
);
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{-123456}), true, 2),
"-1234.56"
);
BOOST_CHECK_EQUAL(
BytesUtils::formatFixedPoint(toBigEndian(u256{-1234567}), true, 2),
"-12345.67"
);
}
BOOST_AUTO_TEST_SUITE_END()
}

View File

@ -20,6 +20,8 @@
#include <test/libsolidity/util/SoltestErrors.h> #include <test/libsolidity/util/SoltestErrors.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolutil/FunctionSelector.h> #include <libsolutil/FunctionSelector.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
@ -124,6 +126,21 @@ bool isFixedTupleArray(string const& _type)
return regex_match(_type, regex{"tuple\\[\\d+\\]"}); return regex_match(_type, regex{"tuple\\[\\d+\\]"});
} }
optional<ABIType> isFixedPoint(string const& type)
{
optional<ABIType> fixedPointType;
smatch matches;
if (regex_match(type, matches, regex{"(u?)fixed(\\d+)x(\\d+)"}))
{
ABIType abiType(ABIType::SignedFixedPoint);
if (matches[1].str() == "u")
abiType.type = ABIType::UnsignedFixedPoint;
abiType.fractionalDigits = static_cast<unsigned>(std::stoi(matches[3].str()));
fixedPointType = abiType;
}
return fixedPointType;
}
string functionSignatureFromABI(Json::Value const& _functionABI) string functionSignatureFromABI(Json::Value const& _functionABI)
{ {
auto inputs = _functionABI["inputs"]; auto inputs = _functionABI["inputs"];
@ -245,6 +262,8 @@ bool ContractABIUtils::appendTypesFromName(
_dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft}); _dynamicTypes.push_back(ABIType{ABIType::String, ABIType::AlignLeft});
} }
} }
else if (optional<ABIType> fixedPointType = isFixedPoint(type))
_inplaceTypes.push_back(*fixedPointType);
else if (isBytes(type)) else if (isBytes(type))
return false; return false;
else if (isFixedTupleArray(type)) else if (isFixedTupleArray(type))
@ -270,7 +289,8 @@ void ContractABIUtils::overwriteParameters(
{ {
if ( if (
_a.abiType.size != _b.abiType.size || _a.abiType.size != _b.abiType.size ||
_a.abiType.type != _b.abiType.type _a.abiType.type != _b.abiType.type ||
_a.abiType.fractionalDigits != _b.abiType.fractionalDigits
) )
{ {
_errorReporter.warning("Type or size of parameter(s) does not match."); _errorReporter.warning("Type or size of parameter(s) does not match.");

View File

@ -107,7 +107,9 @@ struct ABIType
SignedDec, SignedDec,
Hex, Hex,
HexString, HexString,
String String,
UnsignedFixedPoint,
SignedFixedPoint
}; };
enum Align enum Align
{ {
@ -125,6 +127,9 @@ struct ABIType
Type type = ABIType::None; Type type = ABIType::None;
Align align = ABIType::AlignRight; Align align = ABIType::AlignRight;
size_t size = 32; size_t size = 32;
size_t fractionalDigits = 0;
bool alignDeclared = false; bool alignDeclared = false;
}; };

View File

@ -407,11 +407,17 @@ Parameter TestFileParser::parseParameter()
if (isSigned) if (isSigned)
parsed = "-" + parsed; parsed = "-" + parsed;
parameter.rawBytes = BytesUtils::applyAlign( if (parsed.find('.') == string::npos)
parameter.alignment, parameter.rawBytes = BytesUtils::applyAlign(
parameter.abiType, parameter.alignment,
BytesUtils::convertNumber(parsed) parameter.abiType,
); BytesUtils::convertNumber(parsed)
);
else
{
parameter.abiType.type = isSigned ? ABIType::SignedFixedPoint : ABIType::UnsignedFixedPoint;
parameter.rawBytes = BytesUtils::convertFixedPoint(parsed, parameter.abiType.fractionalDigits);
}
} }
else if (accept(Token::Failure, true)) else if (accept(Token::Failure, true))
{ {
@ -667,7 +673,7 @@ string TestFileParser::Scanner::scanDecimalNumber()
{ {
string number; string number;
number += current(); number += current();
while (langutil::isDecimalDigit(peek())) while (langutil::isDecimalDigit(peek()) || '.' == peek())
{ {
advance(); advance();
number += current(); number += current();

View File

@ -19,7 +19,7 @@
#include <libsolutil/AnsiColorized.h> #include <libsolutil/AnsiColorized.h>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string.hpp>
#include <optional> #include <optional>
#include <stdexcept> #include <stdexcept>

View File

@ -16,10 +16,12 @@
#include <test/libsolidity/util/TestFileParser.h> #include <test/libsolidity/util/TestFileParser.h>
#include <test/libsolidity/util/SoltestErrors.h> #include <test/libsolidity/util/SoltestErrors.h>
#include <test/libsolidity/util/ContractABIUtils.h>
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <libsolutil/AnsiColorized.h> #include <libsolutil/AnsiColorized.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <libsolutil/JSON.h>
#include <json/json.h> #include <json/json.h>
@ -109,12 +111,6 @@ private:
bool failure = false bool failure = false
) const; ) const;
/// Formats a given _bytes applying the _abiType.
std::string formatBytesRange(
bytes const& _bytes,
ABIType const& _abiType
) const;
/// Formats a FAILURE plus additional parameters, if e.g. a revert message was returned. /// Formats a FAILURE plus additional parameters, if e.g. a revert message was returned.
std::string formatFailure( std::string formatFailure(
ErrorReporter& _errorReporter, ErrorReporter& _errorReporter,