Use "tuple" for struct types in ABI JSON.

Only use tuple as a type in the ABI (and remove all "anonymous struct" references too)
This commit is contained in:
chriseth 2017-09-01 13:37:40 +02:00 committed by Alex Beregszaszi
parent 70d70e7816
commit c5063d3155
4 changed files with 52 additions and 27 deletions

View File

@ -68,13 +68,12 @@ The following non-fixed-size types exist:
- ``<type>[]``: a variable-length array of the given fixed-length type.
Types can be combined to anonymous structs by enclosing a finite non-negative number
Types can be combined to a tuple by enclosing a finite non-negative number
of them inside parentheses, separated by commas:
- ``(T1,T2,...,Tn)``: anonymous struct (ordered tuple) consisting of the types ``T1``, ..., ``Tn``, ``n >= 0``
It is possible to form structs of structs, arrays of structs and so on.
- ``(T1,T2,...,Tn)``: tuple consisting of the types ``T1``, ..., ``Tn``, ``n >= 0``
It is possible to form tuples of tuples, arrays of tuples and so on.
Formal Specification of the Encoding
====================================
@ -133,7 +132,7 @@ on the type of ``X`` being
``enc(X) = enc((X[0], ..., X[k-1]))``
i.e. it is encoded as if it were an anonymous struct with ``k`` elements
i.e. it is encoded as if it were a tuple with ``k`` elements
of the same type.
- ``T[]`` where ``X`` has ``k`` elements (``k`` is assumed to be of type ``uint256``):
@ -176,7 +175,7 @@ and the return values ``v_1, ..., v_k`` of ``f`` are encoded as
``enc((v_1, ..., v_k))``
i.e. the values are combined into an anonymous struct and encoded.
i.e. the values are combined into a tuple and encoded.
Examples
========
@ -357,16 +356,17 @@ would result in the JSON:
"outputs": []
}]
Use of Structs in Types
-----------------------
Handling tuple types
--------------------
If structs are part of the type, we still want to know the name of the components. Because of that,
If tuples are part of the type, we still want to know the name of the components. Because of that,
the json structure gets arbitrarily nested in the following way:
An object with members ``name``, ``type`` and potentially ``components`` describes a typed variable.
The canonical type is determined until a struct type is reached and the string description up
to that point is stored in ``type``, i.e. it will be a sequence of ``[]`` and ``[k]`` with
integers ``k``. The components of the struct are then stored in the member ``components``,
The canonical type is determined until a tuple type is reached and the string description up
to that point is stored in ``type`` prefix with the word ``tuple``, i.e. it will be ``tuple`` followed by
a sequence of ``[]`` and ``[k]`` with
integers ``k``. The components of the tuple are then stored in the member ``components``,
which is of array type and has the same structure as the top-level object except that
``indexed`` is not allowed there.
@ -391,7 +391,7 @@ would result in the JSON:
"inputs": [
{
"name": "s",
"type": "",
"type": "tuple",
"components": [
{
"name": "a",
@ -403,7 +403,7 @@ would result in the JSON:
},
{
"name": "c",
"type": "[]",
"type": "tuple[]",
"components": [
{
"name": "x",
@ -419,7 +419,7 @@ would result in the JSON:
},
{
"name": "t",
"type": "",
"type": "tuple",
"components": [
{
"name": "x",

View File

@ -147,7 +147,7 @@ Json::Value ABI::formatType(string const& _name, Type const& _type, bool _forLib
}
else if (StructType const* structType = dynamic_cast<StructType const*>(&_type))
{
ret["type"] = string();
ret["type"] = "tuple";
ret["components"] = Json::arrayValue;
for (auto const& member: structType->members(nullptr))
{

View File

@ -973,11 +973,11 @@ BOOST_AUTO_TEST_CASE(return_structs)
}
],
"name" : "sub",
"type" : "[]"
"type" : "tuple[]"
}
],
"name" : "s",
"type" : ""
"type" : "tuple"
}
],
"payable" : false,
@ -1015,7 +1015,7 @@ BOOST_AUTO_TEST_CASE(return_structs_with_contracts)
}
],
"name": "s",
"type": ""
"type": "tuple"
},
{
"name": "c",
@ -1052,7 +1052,7 @@ BOOST_AUTO_TEST_CASE(event_structs)
],
"indexed": false,
"name": "t",
"type": ""
"type": "tuple"
},
{
"components": [
@ -1068,7 +1068,7 @@ BOOST_AUTO_TEST_CASE(event_structs)
}
],
"name": "sub",
"type": "[]"
"type": "tuple[]"
},
{
"name": "b",
@ -1077,7 +1077,7 @@ BOOST_AUTO_TEST_CASE(event_structs)
],
"indexed": false,
"name": "s",
"type": ""
"type": "tuple"
}
],
"name": "E",
@ -1115,7 +1115,7 @@ BOOST_AUTO_TEST_CASE(structs_in_libraries)
}
],
"name": "sub",
"type": "[]"
"type": "tuple[]"
},
{
"name": "b",
@ -1123,7 +1123,7 @@ BOOST_AUTO_TEST_CASE(structs_in_libraries)
}
],
"name": "s",
"type": ""
"type": "tuple"
}
],
"name": "g",

View File

@ -601,7 +601,6 @@ BOOST_AUTO_TEST_CASE(enum_external_type)
BOOST_AUTO_TEST_CASE(external_structs)
{
ASTPointer<SourceUnit> sourceUnit;
char const* text = R"(
contract Test {
enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
@ -614,7 +613,7 @@ BOOST_AUTO_TEST_CASE(external_structs)
function i(Nested[]) external {}
}
)";
ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed");
SourceUnit const* sourceUnit = parseAndAnalyse(text);
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
@ -627,7 +626,33 @@ BOOST_AUTO_TEST_CASE(external_structs)
}
}
// TODO: Add a test that checks the signature of library functions taking structs
BOOST_AUTO_TEST_CASE(external_structs_in_libraries)
{
char const* text = R"(
library Test {
enum ActionChoices { GoLeft, GoRight, GoStraight, Sit }
struct Empty {}
struct Nested { X[2][] a; mapping(uint => uint) m; uint y; }
struct X { bytes32 x; Test t; Empty[] e; }
function f(ActionChoices, uint, Empty) external {}
function g(Test, Nested) external {}
function h(function(Nested) external returns (uint)[]) external {}
function i(Nested[]) external {}
}
)";
SourceUnit const* sourceUnit = parseAndAnalyse(text);
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
auto functions = contract->definedFunctions();
BOOST_REQUIRE(!functions.empty());
BOOST_CHECK_EQUAL("f(Test.ActionChoices,uint256,Test.Empty)", functions[0]->externalSignature());
BOOST_CHECK_EQUAL("g(Test,Test.Nested)", functions[1]->externalSignature());
BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature());
BOOST_CHECK_EQUAL("i(Test.Nested[])", functions[3]->externalSignature());
}
}
BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion)
{