TestFileParser: Adding new keyword wei for expressing function value

This commit is contained in:
Djordje Mijovic 2020-02-18 12:57:48 +01:00
parent 357c936243
commit dd9009eba6
16 changed files with 117 additions and 50 deletions

View File

@ -7,7 +7,7 @@ Compiler Features:
Bugfixes:
* isoltest: Added new keyword `wei` to express function value in semantic tests
### 0.6.3 (2020-02-18)
@ -31,7 +31,6 @@ Bugfixes:
* Type Checker: Make invalid calls to uncallable types fatal errors instead of regular.
### 0.6.2 (2020-01-27)
Language Features:

View File

@ -133,7 +133,7 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
else
{
if (test.call().isConstructor)
deploy("", test.call().value, test.call().arguments.rawBytes(), libraries);
deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries);
else
soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
constructed = true;
@ -151,7 +151,7 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
{
bytes output;
if (test.call().useCallWithoutSignature)
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value);
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
else
{
soltestAssert(
@ -161,7 +161,7 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
output = callContractFunctionWithValueNoEncoding(
test.call().signature,
test.call().value,
test.call().value.value,
test.call().arguments.rawBytes()
);
}

View File

@ -0,0 +1,8 @@
contract C {
function f() public payable returns (uint) {
return msg.value;
}
}
// ----
// f(), 1 ether -> 1000000000000000000
// f(), 1 wei -> 1

View File

@ -9,4 +9,4 @@ contract C {
// EVMVersion: >=istanbul
// compileViaYul: also
// ----
// f(), 254 ether -> 254
// f(), 254 wei -> 254

View File

@ -6,7 +6,8 @@ contract A {
// x() -> 0
// ()
// x() -> 1
// (), 1 ether
// (), 1 wei
// x() -> 2
// x(), 1 wei -> FAILURE
// (): hex"00" -> FAILURE
// (), 1 ether: hex"00" -> FAILURE

View File

@ -21,7 +21,7 @@ contract C {
// EVMVersion: >=byzantium
// revertStrings: debug
// ----
// (), 10 ether ->
// (), 10 wei ->
// g() -> 10
// f() -> FAILURE, hex"08c379a0", 0x20, 10, "no_receive"
// h() -> FAILURE

View File

@ -30,7 +30,8 @@ contract C {
}
// ----
// d() ->
// e(), 1 ether -> 1
// e(), 1 wei -> 1
// e(), 1 ether -> 1000000000000000000
// f(uint256): 3 -> 3, 3
// g() -> 2, 3
// h(uint256,uint256): 1, -2 -> 3

View File

@ -11,7 +11,7 @@ contract C {
}
}
// ----
// constructor(), 2 ether: 3 ->
// constructor(), 2 wei: 3 ->
// state() -> 3
// balance() -> 2
// update(uint256): 4

View File

@ -16,8 +16,8 @@ contract A {
// data() -> 2
// externalData() -> 0x20, 2, left(0x42ef)
// balance() -> 0
// (), 1 ether
// (), 1 wei
// balance() -> 1
// (), 2 ether: hex"fefe"
// (), 2 wei: hex"fefe"
// balance() -> 2
// externalData() -> 0x20, 2, left(0xfefe)

View File

@ -225,7 +225,7 @@ string BytesUtils::formatRawBytes(
{
bytes byteRange{it, it + static_cast<long>(parameter.abiType.size)};
os << _linePrefix << byteRange;
os << _linePrefix << formatBytes(byteRange, parameter.abiType);
if (&parameter != &parameters.back())
os << endl;

View File

@ -50,6 +50,7 @@ namespace solidity::frontend::test
T(Identifier, "identifier", 0) \
/* type keywords */ \
K(Ether, "ether", 0) \
K(Wei, "wei", 0) \
K(Hex, "hex", 0) \
K(Boolean, "boolean", 0) \
/* special keywords */ \
@ -229,6 +230,32 @@ struct FunctionCallArgs
}
};
/// Units that can be used to express function value
enum class FunctionValueUnit
{
Wei,
Ether
};
/// Holds value along with unit it was expressed in originally.
/// Value should be always converted to wei, no meter on which unit it was originally
struct FunctionValue
{
u256 value;
FunctionValueUnit unit = FunctionValueUnit::Wei;
};
inline bool operator==(FunctionValue const& _a, FunctionValue const& _b)
{
return _a.value == _b.value;
}
inline std::ostream& operator<<(std::ostream& _os, FunctionValue const& _v)
{
_os << _v.value << (_v.unit == FunctionValueUnit::Wei ? " wei" : " ether");
return _os;
}
/**
* Represents a function call read from an input stream. It contains the signature, the
* arguments, an optional ether value and an expected execution result.
@ -237,8 +264,10 @@ struct FunctionCall
{
/// Signature of the function call, e.g. `f(uint256, uint256)`.
std::string signature;
/// Optional `ether` value that can be send with the call.
u256 value;
/// Optional value that can be sent with the call.
/// Value is expressed in wei, smallest unit of ether
/// Value has a field unit which represents denomination on which value was expressed originally
FunctionValue value;
/// Object that holds all function parameters in their `bytes`
/// representations given by the contract ABI.
FunctionCallArgs arguments;

View File

@ -88,6 +88,7 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature();
if (accept(Token::Comma, true))
call.value = parseFunctionCallValue();
if (accept(Token::Colon, true))
call.arguments = parseFunctionCallArguments();
@ -199,13 +200,19 @@ pair<string, bool> TestFileParser::parseFunctionSignature()
return {signature, !hasName};
}
u256 TestFileParser::parseFunctionCallValue()
FunctionValue TestFileParser::parseFunctionCallValue()
{
try
{
u256 value{parseDecimalNumber()};
expect(Token::Ether);
return value;
u256 value{ parseDecimalNumber() };
Token token = m_scanner.currentToken();
if (token != Token::Ether && token != Token::Wei)
throw Error(Error::Type::ParserError, "Invalid value unit provided. Coins can be wei or ether.");
m_scanner.scanNextToken();
FunctionValueUnit unit = token == Token::Wei ? FunctionValueUnit::Wei : FunctionValueUnit::Ether;
return { (unit == FunctionValueUnit::Wei ? u256(1) : exp256(u256(10), u256(18))) * value, unit };
}
catch (std::exception const&)
{
@ -467,6 +474,7 @@ void TestFileParser::Scanner::scanNextToken()
if (_literal == "true") return TokenDesc{Token::Boolean, _literal};
if (_literal == "false") return TokenDesc{Token::Boolean, _literal};
if (_literal == "ether") return TokenDesc{Token::Ether, _literal};
if (_literal == "wei") return TokenDesc{Token::Wei, _literal};
if (_literal == "left") return TokenDesc{Token::Left, _literal};
if (_literal == "library") return TokenDesc{Token::Library, _literal};
if (_literal == "right") return TokenDesc{Token::Right, _literal};

View File

@ -39,6 +39,7 @@ namespace solidity::frontend::test
* // f(uint256, uint256): 1, 1 # Signature and comma-separated list of arguments #
* // -> 1, 1 # Expected result value #
* // g(), 2 ether # (Optional) Ether to be send with the call #
* // g(), 1 wei # (Optional) Wei to be sent with the call #
* // -> 2, 3
* // h(uint256), 1 ether: 42
* // -> FAILURE # If REVERT or other EVM failure was detected #
@ -134,7 +135,7 @@ private:
/// Parses the optional ether value that can be passed alongside the
/// function call arguments. Throws an InvalidEtherValueEncoding exception
/// if given value cannot be converted to `u256`.
u256 parseFunctionCallValue();
FunctionValue parseFunctionCallValue();
/// Parses a comma-separated list of arguments passed with a function call.
/// Does not check for a potential mismatch between the signature and the number

View File

@ -51,7 +51,7 @@ void testFunctionCall(
bool _failure = true,
bytes _arguments = bytes{},
bytes _expectations = bytes{},
u256 _value = 0,
FunctionValue _value = { 0 },
string _argumentComment = "",
string _expectationComment = "",
vector<string> _rawArguments = vector<string>{},
@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(call_arguments_comments_success)
false,
fmt::encodeArgs(1, 1),
fmt::encodeArgs(),
0,
{0},
" Comment on the parameters. ",
" This call should not return a value, but still succeed. "
);
@ -154,7 +154,7 @@ BOOST_AUTO_TEST_CASE(call_arguments_comments_success)
false,
fmt::encodeArgs(),
fmt::encodeArgs(1),
0,
{0},
" Comment on no parameters. ",
" This comment should be parsed. "
);
@ -176,7 +176,7 @@ BOOST_AUTO_TEST_CASE(simple_single_line_call_comment_success)
false,
fmt::encodeArgs(1),
fmt::encodeArgs(),
0,
{0},
"",
" f(uint256) does not return a value. "
);
@ -275,7 +275,7 @@ BOOST_AUTO_TEST_CASE(call_comments)
false,
fmt::encodeArgs(),
fmt::encodeArgs(1),
0,
{0},
" Parameter comment ",
" Expectation comment "
);
@ -286,7 +286,7 @@ BOOST_AUTO_TEST_CASE(call_comments)
false,
fmt::encodeArgs(),
fmt::encodeArgs(1),
0,
{0},
" Parameter comment ",
" Expectation comment "
);
@ -295,7 +295,7 @@ BOOST_AUTO_TEST_CASE(call_comments)
BOOST_AUTO_TEST_CASE(call_arguments)
{
char const* source = R"(
// f(uint256), 314 ether: 5 # optional ether value #
// f(uint256), 314 wei: 5 # optional wei value #
// -> 4
)";
auto const calls = parse(source);
@ -307,7 +307,27 @@ BOOST_AUTO_TEST_CASE(call_arguments)
false,
fmt::encodeArgs(5),
fmt::encodeArgs(4),
314,
{314},
" optional wei value "
);
}
BOOST_AUTO_TEST_CASE(call_arguments_ether)
{
char const* source = R"(
// f(uint256), 1 ether: 5 # optional ether value #
// -> 4
)";
auto const calls = parse(source);
BOOST_REQUIRE_EQUAL(calls.size(), 1);
testFunctionCall(
calls.at(0),
Mode::MultiLine,
"f(uint256)",
false,
fmt::encodeArgs(5),
fmt::encodeArgs(4),
{exp256(u256(10), u256(18)) , FunctionValueUnit::Ether},
" optional ether value "
);
}
@ -521,7 +541,7 @@ BOOST_AUTO_TEST_CASE(call_arguments_tuple_of_tuples)
false,
fmt::encodeArgs(),
fmt::encodeArgs(),
0,
{0},
" f(S memory s, uint256 b) "
);
}
@ -565,7 +585,7 @@ BOOST_AUTO_TEST_CASE(call_arguments_mismatch)
false,
fmt::encodeArgs(1, 2),
fmt::encodeArgs(1),
0,
{0},
" This only throws at runtime "
);
}
@ -594,7 +614,7 @@ BOOST_AUTO_TEST_CASE(call_multiple_arguments)
BOOST_AUTO_TEST_CASE(call_multiple_arguments_mixed_format)
{
char const* source = R"(
// test(uint256, uint256), 314 ether:
// test(uint256, uint256), 314 wei:
// 1, -2
// -> -1, 2
)";
@ -607,7 +627,7 @@ BOOST_AUTO_TEST_CASE(call_multiple_arguments_mixed_format)
false,
fmt::encodeArgs(1, -2),
fmt::encodeArgs(-1, 2),
314
{314}
);
}
@ -668,7 +688,7 @@ BOOST_AUTO_TEST_CASE(call_raw_arguments)
false,
fmt::encodeArgs(1, -2, -3),
fmt::encodeArgs(),
0,
{0},
"",
"",
{"1", "-2", "-3"}
@ -899,7 +919,7 @@ BOOST_AUTO_TEST_CASE(constructor)
false,
{},
{},
0,
{0},
"",
"",
{},
@ -921,7 +941,7 @@ BOOST_AUTO_TEST_CASE(library)
false,
{},
{},
0,
{0},
"",
"",
{},

View File

@ -63,8 +63,8 @@ string TestFunctionCall::format(
/// Formats the function signature. This is the same independent from the display-mode.
stream << _linePrefix << newline << ws << m_call.signature;
if (m_call.value > u256(0))
stream << comma << ws << m_call.value << ws << ether;
if (m_call.value.value > u256(0))
stream << comma << ws << m_call.value.value << ws << ether;
if (!m_call.arguments.rawBytes().empty())
{
string output = formatRawParameters(m_call.arguments.parameters, _linePrefix);

View File

@ -40,7 +40,7 @@ BOOST_AUTO_TEST_CASE(format_unsigned_singleline)
Parameter param{expectedBytes, "1", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(uint8)", 0, arguments, expectations};
FunctionCall call{"f(uint8)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(format_unsigned_singleline_signed_encoding)
Parameter param{expectedBytes, "1", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(uint8)", 0, arguments, expectations};
FunctionCall call{"f(uint8)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(format_unsigned_multiline)
Parameter result{expectedBytes, "1", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{result}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{}, string{}};
FunctionCall call{"f(uint8)", 0, arguments, expectations};
FunctionCall call{"f(uint8)", {0}, arguments, expectations};
call.omitsArrow = false;
call.displayMode = FunctionCall::DisplayMode::MultiLine;
TestFunctionCall test{call};
@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(format_multiple_unsigned_singleline)
Parameter param{expectedBytes, "1", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param, param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param, param}, string{}};
FunctionCall call{"f(uint8, uint8)", 0, arguments, expectations};
FunctionCall call{"f(uint8, uint8)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(format_signed_singleline)
Parameter param{expectedBytes, "-1", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(int8)", 0, arguments, expectations};
FunctionCall call{"f(int8)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(format_hex_singleline)
Parameter param{expectedBytes, "0x31", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(bytes32)", 0, arguments, expectations};
FunctionCall call{"f(bytes32)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE(format_hex_string_singleline)
Parameter param{expectedBytes, "hex\"4200ef\"", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(string)", 0, arguments, expectations};
FunctionCall call{"f(string)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE(format_bool_true_singleline)
Parameter param{expectedBytes, "true", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(bool)", 0, arguments, expectations};
FunctionCall call{"f(bool)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE(format_bool_false_singleline)
Parameter param{expectedBytes, "false", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(bool)", 0, arguments, expectations};
FunctionCall call{"f(bool)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -200,7 +200,7 @@ BOOST_AUTO_TEST_CASE(format_bool_left_singleline)
Parameter param{expectedBytes, "left(false)", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(bool)", 0, arguments, expectations};
FunctionCall call{"f(bool)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(format_hex_number_right_singleline)
Parameter param{expectedBytes, "right(0x42)", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(bool)", 0, arguments, expectations};
FunctionCall call{"f(bool)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -229,7 +229,7 @@ BOOST_AUTO_TEST_CASE(format_empty_byte_range)
Parameter param{expectedBytes, "1", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{param}, false, string{}};
FunctionCallArgs arguments{vector<Parameter>{}, string{}};
FunctionCall call{"f()", 0, arguments, expectations};
FunctionCall call{"f()", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};
@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(format_failure_singleline)
Parameter param{expectedBytes, "1", abiType, FormatInfo{}};
FunctionCallExpectations expectations{vector<Parameter>{}, true, string{}};
FunctionCallArgs arguments{vector<Parameter>{param}, string{}};
FunctionCall call{"f(uint8)", 0, arguments, expectations};
FunctionCall call{"f(uint8)", {0}, arguments, expectations};
call.omitsArrow = false;
TestFunctionCall test{call};