aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.circleci/config.yml12
-rw-r--r--Changelog.md4
-rw-r--r--docs/bugs.json17
-rw-r--r--docs/bugs.rst8
-rw-r--r--docs/bugs_by_version.json62
-rw-r--r--docs/contracts.rst81
-rw-r--r--docs/control-structures.rst6
-rw-r--r--docs/frequently-asked-questions.rst29
-rw-r--r--docs/miscellaneous.rst1
-rw-r--r--docs/security-considerations.rst3
-rw-r--r--docs/solidity-by-example.rst2
-rw-r--r--docs/types.rst8
-rw-r--r--docs/units-and-global-variables.rst11
-rw-r--r--libdevcore/StringUtils.cpp5
-rw-r--r--libdevcore/StringUtils.h3
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp7
-rw-r--r--libsolidity/analysis/TypeChecker.cpp101
-rw-r--r--libsolidity/analysis/TypeChecker.h5
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp2
-rw-r--r--libsolidity/ast/AST.cpp2
-rw-r--r--libsolidity/ast/Types.cpp30
-rw-r--r--libsolidity/ast/Types.h7
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp42
-rwxr-xr-xtest/buglistTests.js45
-rw-r--r--test/buglist_test_vectors.md69
-rw-r--r--test/libdevcore/StringUtils.cpp1
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp469
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp31
-rw-r--r--test/libsolidity/ViewPureChecker.cpp21
-rw-r--r--test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol7
-rw-r--r--test/libsolidity/syntaxTests/events/event_array_v2.sol6
-rw-r--r--test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol7
-rw-r--r--test/libsolidity/syntaxTests/events/event_nested_array_v2.sol6
-rw-r--r--test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol8
-rw-r--r--test/libsolidity/syntaxTests/events/event_struct_v2.sol7
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/573_similar_name_longer_than_80_not_suggested.sol7
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/574_similar_name_shorter_than_80_suggested.sol7
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_calldata.sol8
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_invalid_arg_count.sol12
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory.sol7
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory_v2.sol10
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nontuple.sol11
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_simple.sol5
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_singletontuple.sol6
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_storage.sol8
45 files changed, 1121 insertions, 85 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index e3596d2b..4514626b 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -179,6 +179,17 @@ jobs:
name: Check spelling
command: ~/.local/bin/codespell -S "*.enc,.git" -I ./scripts/codespell_whitelist.txt
+ test_buglist:
+ docker:
+ - image: circleci/node
+ environment:
+ TERM: xterm
+ steps:
+ - checkout
+ - run:
+ name: Test buglist
+ command: ./test/buglistTests.js
+
test_x86_linux:
docker:
- image: buildpack-deps:artful
@@ -252,6 +263,7 @@ workflows:
build_all:
jobs:
- test_check_spelling: *build_on_tags
+ - test_buglist: *build_on_tags
- build_emscripten: *build_on_tags
- test_emscripten_solcjs:
<<: *build_on_tags
diff --git a/Changelog.md b/Changelog.md
index 8a16d900..5a421fa5 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -71,12 +71,14 @@ Breaking Changes:
* View Pure Checker: Strictly enfore state mutability. This was already the case in the experimental 0.5.0 mode.
Language Features:
+ * Genreal: Add ``staticcall`` to ``address``.
* General: Allow appending ``calldata`` keyword to types, to explicitly specify data location for arguments of external functions.
* General: Support ``pop()`` for storage arrays.
* General: Scoping rules now follow the C99-style.
* General: Allow ``enum``s in interfaces.
* General: Allow ``mapping`` storage pointers as arguments and return values in all internal functions.
* General: Allow ``struct``s in interfaces.
+ * General: Provide access to the ABI decoder through ``abi.decode(bytes memory data, (...))``.
Compiler Features:
* C API (``libsolc``): Export the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods.
@@ -98,6 +100,7 @@ Bugfixes:
* References Resolver: Enforce ``storage`` as data location for mappings.
* References Resolver: Properly handle invalid references used together with ``_slot`` and ``_offset``.
* References Resolver: Report error instead of assertion fail when FunctionType has an undeclared type as parameter.
+ * References Resolver: Fix high CPU usage when using large variable names issue. Only suggest similar name if identifiers shorter than 80 characters.
* Type Checker: Default data location for type conversions (e.g. from literals) is memory and not storage.
* Type Checker: Disallow assignments to mappings within tuple assignments as well.
* Type Checker: Disallow packed encoding of arrays of structs.
@@ -106,6 +109,7 @@ Bugfixes:
* Type Checker: Fix crashes in erroneous tuple assignments in which the type of the right hand side cannot be determined.
* Type Checker: Fix freeze for negative fixed-point literals very close to ``0``, such as ``-1e-100``.
* Type Checker: Report error when using structs in events without experimental ABIEncoderV2. This used to crash or log the wrong values.
+ * Type Checker: Report error when using indexed structs in events with experimental ABIEncoderV2. This used to log wrong values.
* Type System: Allow arbitrary exponents for literals with a mantissa of zero.
### 0.4.24 (2018-05-16)
diff --git a/docs/bugs.json b/docs/bugs.json
index b464be18..839ea128 100644
--- a/docs/bugs.json
+++ b/docs/bugs.json
@@ -1,4 +1,21 @@
[
+ {
+ "name": "EventStructWrongData",
+ "summary": "Using structs in events logged wrong data.",
+ "description": "If a struct is used in an event, the address of the struct is logged instead of the actual data.",
+ "introduced": "0.4.17",
+ "fixed": "0.5.0",
+ "severity": "very low"
+ },
+ {
+ "name": "NestedArrayFunctionCallDecoder",
+ "summary": "Calling functions that return multi-dimensional fixed-size arrays can result in memory corruption.",
+ "description": "If Solidity code calls a function that returns a multi-dimensional fixed-size array, array elements are incorrectly interpreted as memory pointers and thus can cause memory corruption if the return values are accessed. Calling functions with multi-dimensional fixed-size arrays is unaffected as is returning fixed-size arrays from function calls. The regular expression only checks if such functions are present, not if they are called, which is required for the contract to be affected.",
+ "introduced": "0.1.4",
+ "fixed": "0.4.22",
+ "severity": "medium",
+ "check": {"regex-source": "returns[^;{]*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\]\\s*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\][^{;]*[;{]"}
+ },
{
"name": "OneOfTwoConstructorsSkipped",
"summary": "If a contract has both a new-style constructor (using the constructor keyword) and an old-style constructor (a function with the same name as the contract) at the same time, one of them will be ignored.",
diff --git a/docs/bugs.rst b/docs/bugs.rst
index 7629830d..6f315a65 100644
--- a/docs/bugs.rst
+++ b/docs/bugs.rst
@@ -56,6 +56,14 @@ conditions
is an object that can contain a boolean value ``optimizer``, which
means that the optimizer has to be switched on to enable the bug.
If no conditions are given, assume that the bug is present.
+check
+ This field contains JavaScript regular expressions that are to be matched
+ against the source code ("source-regex") to find out if the
+ smart contract contains the bug or not. If there is no match,
+ then the bug is very likely not present. If there is a match,
+ the bug might be present. For improved accuracy, the regular
+ expression should be applied to the source code after stripping
+ comments.
.. literalinclude:: bugs.json
:language: js
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index 2fe1d226..560b6fa9 100644
--- a/docs/bugs_by_version.json
+++ b/docs/bugs_by_version.json
@@ -69,6 +69,7 @@
},
"0.1.4": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -86,6 +87,7 @@
},
"0.1.5": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -103,6 +105,7 @@
},
"0.1.6": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -121,6 +124,7 @@
},
"0.1.7": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -139,6 +143,7 @@
},
"0.2.0": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -157,6 +162,7 @@
},
"0.2.1": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -175,6 +181,7 @@
},
"0.2.2": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@@ -193,6 +200,7 @@
},
"0.3.0": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -211,6 +219,7 @@
},
"0.3.1": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -228,6 +237,7 @@
},
"0.3.2": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -245,6 +255,7 @@
},
"0.3.3": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -261,6 +272,7 @@
},
"0.3.4": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -277,6 +289,7 @@
},
"0.3.5": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -293,6 +306,7 @@
},
"0.3.6": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -307,6 +321,7 @@
},
"0.4.0": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -321,6 +336,7 @@
},
"0.4.1": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -335,6 +351,7 @@
},
"0.4.10": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -345,6 +362,7 @@
},
"0.4.11": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -354,6 +372,7 @@
},
"0.4.12": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput"
@@ -362,6 +381,7 @@
},
"0.4.13": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput"
@@ -370,6 +390,7 @@
},
"0.4.14": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue"
],
@@ -377,32 +398,43 @@
},
"0.4.15": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector"
],
"released": "2017-08-08"
},
"0.4.16": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector"
],
"released": "2017-08-24"
},
"0.4.17": {
"bugs": [
+ "EventStructWrongData",
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector"
],
"released": "2017-09-21"
},
"0.4.18": {
- "bugs": [],
+ "bugs": [
+ "EventStructWrongData",
+ "NestedArrayFunctionCallDecoder"
+ ],
"released": "2017-10-18"
},
"0.4.19": {
- "bugs": [],
+ "bugs": [
+ "EventStructWrongData",
+ "NestedArrayFunctionCallDecoder"
+ ],
"released": "2017-11-30"
},
"0.4.2": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -415,29 +447,41 @@
"released": "2016-09-17"
},
"0.4.20": {
- "bugs": [],
+ "bugs": [
+ "EventStructWrongData",
+ "NestedArrayFunctionCallDecoder"
+ ],
"released": "2018-02-14"
},
"0.4.21": {
- "bugs": [],
+ "bugs": [
+ "EventStructWrongData",
+ "NestedArrayFunctionCallDecoder"
+ ],
"released": "2018-03-07"
},
"0.4.22": {
"bugs": [
+ "EventStructWrongData",
"OneOfTwoConstructorsSkipped"
],
"released": "2018-04-16"
},
"0.4.23": {
- "bugs": [],
+ "bugs": [
+ "EventStructWrongData"
+ ],
"released": "2018-04-19"
},
"0.4.24": {
- "bugs": [],
+ "bugs": [
+ "EventStructWrongData"
+ ],
"released": "2018-05-16"
},
"0.4.3": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -450,6 +494,7 @@
},
"0.4.4": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -461,6 +506,7 @@
},
"0.4.5": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -473,6 +519,7 @@
},
"0.4.6": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -484,6 +531,7 @@
},
"0.4.7": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -494,6 +542,7 @@
},
"0.4.8": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@@ -504,6 +553,7 @@
},
"0.4.9": {
"bugs": [
+ "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
diff --git a/docs/contracts.rst b/docs/contracts.rst
index ca15d814..669a374f 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -26,8 +26,8 @@ Creating contracts programmatically on Ethereum is best done via using the JavaS
As of today it has a method called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_
to facilitate contract creation.
-When a contract is created, its constructor (a function declared with the
-``constructor`` keyword) is executed once.
+When a contract is created, its constructor_ (a function declared with the ``constructor`` keyword) is executed once.
+
A constructor is optional. Only one constructor is allowed, and this means
overloading is not supported.
@@ -261,7 +261,37 @@ it is evaluated as a state variable. If it is accessed externally
}
}
-The next example is a bit more complex:
+If you have a `public` state variable of array type, then you can only retrieve
+single elements of the array via the generated getter function. This mechanism
+exists to avoid high gas costs when returning an entire array. You can use
+arguments to specify which individual element to return, for example
+``data(0)``. If you want to return an entire array in one call, then you need
+to write a function, for example:
+
+::
+
+ pragma solidity ^0.4.0;
+ contract arrayExample {
+ // public state variable
+ uint[] public myArray;
+
+ // Getter function generated by the compiler
+ /*
+ function myArray(uint i) returns (uint) {
+ return myArray[i];
+ }
+ */
+
+ // function that returns entire array
+ function getArray() returns (uint[] memory) {
+ return myArray;
+ }
+ }
+
+Now you can use ``getArray()`` to retrieve the entire array, instead of
+``myArray(i)``, which returns a single element per call.
+
+The next example is more complex:
::
@@ -276,16 +306,16 @@ The next example is a bit more complex:
mapping (uint => mapping(bool => Data[])) public data;
}
-It will generate a function of the following form::
+It generates a function of the following form. The mapping in the struct is omitted
+because there is no good way to provide the key for the mapping:
+
+::
function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) {
a = data[arg1][arg2][arg3].a;
b = data[arg1][arg2][arg3].b;
}
-Note that the mapping in the struct is omitted because there
-is no good way to provide the key for the mapping.
-
.. index:: ! function;modifier
.. _modifiers:
@@ -842,13 +872,14 @@ Solidity supports multiple inheritance by copying code including polymorphism.
All function calls are virtual, which means that the most derived function
is called, except when the contract name is explicitly given.
-When a contract inherits from multiple contracts, only a single
+When a contract inherits from other contracts, only a single
contract is created on the blockchain, and the code from all the base contracts
is copied into the created contract.
The general inheritance system is very similar to
`Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_,
-especially concerning multiple inheritance.
+especially concerning multiple inheritance, but there are also
+some :ref:`differences <multi-inheritance>`.
Details are given in the following example.
@@ -993,12 +1024,26 @@ virtual method lookup.
.. index:: ! constructor
+.. _constructor:
+
Constructors
============
-A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation.
-Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the
-default constructor: ``contructor() public {}``.
+A constructor is an optional function declared with the ``constructor`` keyword
+which is executed upon contract creation, and where you can run contract
+initialisation code.
+
+Before the constructor code is executed, state variables are initialised to
+their specified value if you initialise them inline, or zero if you do not.
+
+After the final code of the contract is returned. The final deployment of
+the code costs additional gas linear to the length of the code. If you did not
+supply enough gas to initiate the state variables declared in the constructor,
+then an "out of gas" exception is generated.
+
+Constructor functions can be either ``public`` or ``internal``. If there is no
+constructor, the contract will assume the default constructor, which is
+equivalent to ``constructor() public {}``. For example:
::
@@ -1064,6 +1109,8 @@ contracts' constructors, it will be abstract.
.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization
+.. _multi-inheritance:
+
Multiple Inheritance and Linearization
======================================
@@ -1076,7 +1123,13 @@ disallows some inheritance graphs. Especially, the order in
which the base classes are given in the ``is`` directive is
important: You have to list the direct base contracts
in the order from "most base-like" to "most derived".
-Note that this order is different from the one used in Python.
+Note that this order is the reverse of the one used in Python.
+
+Another simplifying way to explain this is that when a function is called that
+is defined multiple times in different contracts, the given bases
+are searched from right to left (left to right in Python) in a depth-first manner,
+stopping at the first match. If a base contract has already been searched, it is skipped.
+
In the following code, Solidity will give the
error "Linearization of inheritance graph impossible".
@@ -1094,6 +1147,8 @@ The reason for this is that ``C`` requests ``X`` to override ``A``
requests to override ``X``, which is a contradiction that
cannot be resolved.
+
+
Inheriting Different Kinds of Members of the Same Name
======================================================
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index def75132..d0e58908 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -412,11 +412,11 @@ The deprecated keyword ``throw`` can also be used as an alternative to ``revert(
From version 0.4.13 the ``throw`` keyword is deprecated and will be phased out in the future.
When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are rethrown) automatically. Exceptions to this rule are ``send``
-and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case
+and the low-level functions ``call``, ``delegatecall``, ``callcode`` and ``staticcall`` -- those return ``false`` in case
of an exception instead of "bubbling up".
.. warning::
- The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
+ The low-level ``call``, ``delegatecall``, ``callcode`` and ``staticcall`` will return success if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
Catching exceptions is not yet possible.
@@ -455,7 +455,7 @@ A ``require``-style exception is generated in the following situations:
#. Calling ``throw``.
#. Calling ``require`` with an argument that evaluates to ``false``.
-#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
+#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall``, ``callcode`` or ``staticcall`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
#. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly").
#. If you perform an external function call targeting a contract that contains no code.
#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function).
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
index 2c5f8661..d2b7de9c 100644
--- a/docs/frequently-asked-questions.rst
+++ b/docs/frequently-asked-questions.rst
@@ -238,13 +238,6 @@ The key point is that the calling contract needs to know about the function it i
See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_
and `pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_.
-Get contract to do something when it is first mined
-===================================================
-
-Use the constructor. Anything inside it will be executed when the contract is first mined.
-
-See `replicator.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/50_replicator.sol>`_.
-
How do you create 2-dimensional arrays?
=======================================
@@ -410,28 +403,6 @@ Is it possible to return an array of strings (``string[]``) from a Solidity func
Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic array itself).
-If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that?
-===========================================================================================================================
-
-The automatic :ref:`getter function<getter-functions>` for a public state variable of array type only returns
-individual elements. If you want to return the complete array, you have to
-manually write a function to do that.
-
-
-What could have happened if an account has storage value(s) but no code? Example: http://test.ether.camp/account/5f740b3a43fbb99724ce93a879805f4dc89178b5
-==========================================================================================================================================================
-
-The last thing a constructor does is returning the code of the contract.
-The gas costs for this depend on the length of the code and it might be
-that the supplied gas is not enough. This situation is the only one
-where an "out of gas" exception does not revert changes to the state,
-i.e. in this case the initialisation of the state variables.
-
-https://github.com/ethereum/wiki/wiki/Subtleties
-
-After a successful CREATE operation's sub-execution, if the operation returns x, 5 * len(x) gas is subtracted from the remaining gas before the contract is created. If the remaining gas is less than 5 * len(x), then no gas is subtracted, the code of the created contract becomes the empty string, but this is not treated as an exceptional condition - no reverts happen.
-
-
What does the following strange check do in the Custom Token contract?
======================================================================
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 7225144a..87041be6 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -322,6 +322,7 @@ The following is the order of precedence for operators, listed in order of evalu
Global Variables
================
+- ``abi.decode(bytes encodedData, (...)) returns (...)``: :ref:`ABI <ABI>`-decodes the provided data. The types are given in parentheses as second argument. Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))``
- ``abi.encode(...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments
- ``abi.encodePacked(...) returns (bytes)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments
diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst
index 5bb3d81d..066a31ea 100644
--- a/docs/security-considerations.rst
+++ b/docs/security-considerations.rst
@@ -171,7 +171,8 @@ before they interact with your contract.
Note that ``.send()`` does **not** throw an exception if the call stack is
depleted but rather returns ``false`` in that case. The low-level functions
-``.call()``, ``.callcode()`` and ``.delegatecall()`` behave in the same way.
+``.call()``, ``.callcode()``, ``.delegatecall()`` and ``.staticcall()`` behave
+in the same way.
tx.origin
=========
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index adf00546..72b3581b 100644
--- a/docs/solidity-by-example.rst
+++ b/docs/solidity-by-example.rst
@@ -907,7 +907,7 @@ Each message includes the following information:
* The smart contract's address, used to prevent cross-contract replay attacks.
* The total amount of Ether that is owed the recipient so far.
-A payment channel is closed just once, at the of a series of transfers.
+A payment channel is closed just once, at the end of a series of transfers.
Because of this, only one of the messages sent will be redeemed. This is why
each message specifies a cumulative total amount of Ether owed, rather than the
amount of the individual micropayment. The recipient will naturally choose to
diff --git a/docs/types.rst b/docs/types.rst
index 6024cbb7..03fd36d9 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -91,7 +91,7 @@ Operators:
defined in the latter. Generally, in floating point almost the entire space is used to represent the number, while only a small number of bits define
where the decimal point is.
-.. index:: address, balance, send, call, callcode, delegatecall, transfer
+.. index:: address, balance, send, call, callcode, delegatecall, staticcall, transfer
.. _address:
@@ -146,7 +146,7 @@ Send is the low-level counterpart of ``transfer``. If the execution fails, the c
to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better:
use a pattern where the recipient withdraws the money.
-* ``call``, ``callcode`` and ``delegatecall``
+* ``call``, ``callcode``, ``delegatecall`` and ``staticcall``
Furthermore, to interface with contracts that do not adhere to the ABI,
or to get more direct control over the encoding,
@@ -189,7 +189,9 @@ Lastly, these modifiers can be combined. Their order does not matter::
In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values.
-All three functions ``call``, ``delegatecall`` and ``callcode`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
+Since byzantium ``staticcall`` can be used as well. This is basically the same as ``call``, but will revert, if the called function modifies the state in any way.
+
+All four functions ``call``, ``delegatecall``, ``callcode`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``.
diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst
index 6eae2804..28c9e6ab 100644
--- a/docs/units-and-global-variables.rst
+++ b/docs/units-and-global-variables.rst
@@ -96,13 +96,14 @@ Block and Transaction Properties
.. index:: abi, encoding, packed
-ABI Encoding Functions
-----------------------
+ABI Encoding and Decoding Functions
+-----------------------------------
+- ``abi.decode(bytes encodedData, (...)) returns (...)``: ABI-decodes the given data, while the types are given in parentheses as second argument. Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))``
- ``abi.encode(...) returns (bytes)``: ABI-encodes the given arguments
- ``abi.encodePacked(...) returns (bytes)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: ABI-encodes the given arguments starting from the second and prepends the given four-byte selector
-- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)```
+- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)```
.. note::
These encoding functions can be used to craft data for function calls without actually
@@ -152,7 +153,7 @@ Mathematical and Cryptographic Functions
It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net.
-.. index:: balance, send, transfer, call, callcode, delegatecall
+.. index:: balance, send, transfer, call, callcode, delegatecall, staticcall
.. _address_related:
Address Related
@@ -170,6 +171,8 @@ Address Related
issue low-level ``CALLCODE`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
``<address>.delegatecall(bytes memory) returns (bool)``:
issue low-level ``DELEGATECALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
+``<address>.staticcall(bytes memory) returns (bool)``:
+ issue low-level ``STATICCALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
For more information, see the section on :ref:`address`.
diff --git a/libdevcore/StringUtils.cpp b/libdevcore/StringUtils.cpp
index 01bdc675..50bf7cce 100644
--- a/libdevcore/StringUtils.cpp
+++ b/libdevcore/StringUtils.cpp
@@ -29,13 +29,16 @@
using namespace std;
using namespace dev;
-bool dev::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance)
+bool dev::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance, size_t _lenThreshold)
{
if (_str1 == _str2)
return true;
size_t n1 = _str1.size();
size_t n2 = _str2.size();
+ if (_lenThreshold > 0 && n1 * n2 > _lenThreshold)
+ return false;
+
size_t distance = stringDistance(_str1, _str2);
// if distance is not greater than _maxDistance, and distance is strictly less than length of both names, they can be considered similar
diff --git a/libdevcore/StringUtils.h b/libdevcore/StringUtils.h
index f05a426b..b02b9d12 100644
--- a/libdevcore/StringUtils.h
+++ b/libdevcore/StringUtils.h
@@ -30,7 +30,8 @@ namespace dev
{
// Calculates the Damerau–Levenshtein distance between _str1 and _str2 and returns true if that distance is not greater than _maxDistance
-bool stringWithinDistance(std::string const& _str1, std::string const& _str2, size_t _maxDistance);
+// if _lenThreshold > 0 and the product of the strings length is greater than _lenThreshold, the function will return false
+bool stringWithinDistance(std::string const& _str1, std::string const& _str2, size_t _maxDistance, size_t _lenThreshold = 0);
// Calculates the Damerau–Levenshtein distance between _str1 and _str2
size_t stringDistance(std::string const& _str1, std::string const& _str2);
// Return a string having elements of suggestions as quoted, alternative suggestions. e.g. "a", "b" or "c"
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index 347daaf8..5f980788 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -138,19 +138,22 @@ vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _n
vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
{
static size_t const MAXIMUM_EDIT_DISTANCE = 2;
+ // because the function below has quadratic runtime - it will not magically improve once a better algorithm is discovered ;)
+ // since 80 is the suggested line length limit, we use 80^2 as length threshold
+ static size_t const MAXIMUM_LENGTH_THRESHOLD = 80 * 80;
vector<ASTString> similar;
for (auto const& declaration: m_declarations)
{
string const& declarationName = declaration.first;
- if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
+ if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD))
similar.push_back(declarationName);
}
for (auto const& declaration: m_invisibleDeclarations)
{
string const& declarationName = declaration.first;
- if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
+ if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD))
similar.push_back(declarationName);
}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 8b609a31..5033fd63 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -525,6 +525,75 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
);
}
+TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2)
+{
+ vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
+ if (arguments.size() != 2)
+ m_errorReporter.typeError(
+ _functionCall.location(),
+ "This function takes two arguments, but " +
+ toString(arguments.size()) +
+ " were provided."
+ );
+ if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
+ m_errorReporter.typeError(
+ arguments.front()->location(),
+ "Invalid type for argument in function call. "
+ "Invalid implicit conversion from " +
+ type(*arguments.front())->toString() +
+ " to bytes memory requested."
+ );
+
+ TypePointer returnType = make_shared<TupleType>();
+
+ if (arguments.size() < 2)
+ return returnType;
+
+ // The following is a rather syntactic restriction, but we check it here anyway:
+ // The second argument has to be a tuple expression containing type names.
+ TupleExpression const* tupleExpression = dynamic_cast<TupleExpression const*>(arguments[1].get());
+ if (!tupleExpression)
+ {
+ m_errorReporter.typeError(
+ arguments[1]->location(),
+ "The second argument to \"abi.decode\" has to be a tuple of types."
+ );
+ return returnType;
+ }
+
+ vector<TypePointer> components;
+ for (auto const& typeArgument: tupleExpression->components())
+ {
+ solAssert(typeArgument, "");
+ if (TypeType const* argTypeType = dynamic_cast<TypeType const*>(type(*typeArgument).get()))
+ {
+ TypePointer actualType = argTypeType->actualType();
+ solAssert(actualType, "");
+ // We force memory because the parser currently cannot handle
+ // data locations. Furthermore, storage can be a little dangerous and
+ // calldata is not really implemented anyway.
+ actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType);
+ solAssert(
+ !actualType->dataStoredIn(DataLocation::CallData) &&
+ !actualType->dataStoredIn(DataLocation::Storage),
+ ""
+ );
+ if (!actualType->fullEncodingType(false, _abiEncoderV2, false))
+ m_errorReporter.typeError(
+ typeArgument->location(),
+ "Decoding type " + actualType->toString(false) + " not supported."
+ );
+ components.push_back(actualType);
+ }
+ else
+ {
+ m_errorReporter.typeError(typeArgument->location(), "Argument has to be a type name.");
+ components.push_back(make_shared<TupleType>());
+ }
+ }
+ return make_shared<TupleType>(components);
+}
+
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
{
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
@@ -826,7 +895,17 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters())
{
if (var->isIndexed())
+ {
numIndexed++;
+ if (
+ _eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
+ dynamic_cast<ReferenceType const*>(type(*var).get())
+ )
+ m_errorReporter.typeError(
+ var->location(),
+ "Indexed reference types cannot yet be used with ABIEncoderV2."
+ );
+ }
if (!type(*var)->canLiveOutsideStorage())
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (!type(*var)->interfaceType(false))
@@ -1290,7 +1369,8 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
if (
kind == FunctionType::Kind::BareCall ||
kind == FunctionType::Kind::BareCallCode ||
- kind == FunctionType::Kind::BareDelegateCall
+ kind == FunctionType::Kind::BareDelegateCall ||
+ kind == FunctionType::Kind::BareStaticCall
)
m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used.");
else if (kind == FunctionType::Kind::Send)
@@ -1685,6 +1765,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
return false;
}
+ if (functionType->kind() == FunctionType::Kind::BareStaticCall && !m_evmVersion.hasStaticCall())
+ m_errorReporter.typeError(_functionCall.location(), "\"staticcall\" is not supported by the VM version.");
+
auto returnTypes =
allowDynamicTypes ?
functionType->returnParameterTypes() :
@@ -1727,7 +1810,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
}
- if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
+ bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
+
+ if (functionType->kind() == FunctionType::Kind::ABIDecode)
+ _functionCall.annotation().type = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
+ else if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
{
solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
m_errorReporter.typeError(
@@ -1761,7 +1848,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
else if (
functionType->kind() == FunctionType::Kind::BareCall ||
functionType->kind() == FunctionType::Kind::BareCallCode ||
- functionType->kind() == FunctionType::Kind::BareDelegateCall
+ functionType->kind() == FunctionType::Kind::BareDelegateCall ||
+ functionType->kind() == FunctionType::Kind::BareStaticCall
)
{
if (arguments.empty())
@@ -1782,8 +1870,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
else if (isPositionalCall)
{
- bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
-
for (size_t i = 0; i < arguments.size(); ++i)
{
auto const& argType = type(*arguments[i]);
@@ -1796,7 +1882,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
errored = true;
}
- if (!errored && !argType->fullEncodingType(false, abiEncodeV2, !functionType->padArguments()))
+ if (!errored && !argType->fullEncodingType(false, abiEncoderV2, !functionType->padArguments()))
m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded.");
}
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
@@ -1811,7 +1897,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (
functionType->kind() == FunctionType::Kind::BareCall ||
functionType->kind() == FunctionType::Kind::BareCallCode ||
- functionType->kind() == FunctionType::Kind::BareDelegateCall
+ functionType->kind() == FunctionType::Kind::BareDelegateCall ||
+ functionType->kind() == FunctionType::Kind::BareStaticCall
)
msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.";
else if (
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index ed316f9c..4be0d1e4 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -91,6 +91,11 @@ private:
// and reports an error, if not.
void checkExpressionAssignment(Type const& _type, Expression const& _expression);
+ /// Performs type checks for ``abi.decode(bytes memory, (...))`` and returns the
+ /// return type (which is basically the second argument) if successful. It returns
+ /// the empty tuple type or error.
+ TypePointer typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2);
+
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
virtual void endVisit(UsingForDirective const& _usingFor) override;
virtual bool visit(StructDefinition const& _struct) override;
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index d936ada0..e92ad2fa 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -295,7 +295,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{
// we can ignore the kind of magic and only look at the name of the member
set<string> static const pureMembers{
- "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash"
+ "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode", "data", "sig", "blockhash"
};
if (!pureMembers.count(member))
mutability = StateMutability::View;
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index a376e55d..635ab024 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -397,7 +397,7 @@ SourceUnit const& Scopable::sourceUnit() const
{
ASTNode const* s = scope();
solAssert(s, "");
- // will not always be a declaratoion
+ // will not always be a declaration
while (dynamic_cast<Scopable const*>(s) && dynamic_cast<Scopable const*>(s)->scope())
s = dynamic_cast<Scopable const*>(s)->scope();
return dynamic_cast<SourceUnit const&>(*s);
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 68c201a8..b1cd15b4 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -621,6 +621,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
{"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareDelegateCall, false)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
+ {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
};
else
@@ -2522,6 +2523,7 @@ string FunctionType::richIdentifier() const
case Kind::BareCall: id += "barecall"; break;
case Kind::BareCallCode: id += "barecallcode"; break;
case Kind::BareDelegateCall: id += "baredelegatecall"; break;
+ case Kind::BareStaticCall: id += "barestaticcall"; break;
case Kind::Creation: id += "creation"; break;
case Kind::Send: id += "send"; break;
case Kind::Transfer: id += "transfer"; break;
@@ -2553,6 +2555,7 @@ string FunctionType::richIdentifier() const
case Kind::ABIEncodePacked: id += "abiencodepacked"; break;
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
+ case Kind::ABIDecode: id += "abidecode"; break;
default: solAssert(false, "Unknown function location."); break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
@@ -2704,6 +2707,7 @@ unsigned FunctionType::sizeOnStack() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
+ case Kind::BareStaticCall:
case Kind::Internal:
case Kind::ArrayPush:
case Kind::ArrayPop:
@@ -2771,6 +2775,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
+ case Kind::BareStaticCall:
{
MemberList::MemberMap members;
if (m_kind == Kind::External)
@@ -2910,6 +2915,7 @@ bool FunctionType::isBareCall() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
+ case Kind::BareStaticCall:
case Kind::ECRecover:
case Kind::SHA256:
case Kind::RIPEMD160:
@@ -2923,6 +2929,17 @@ string FunctionType::externalSignature() const
{
solAssert(m_declaration != nullptr, "External signature of function needs declaration");
solAssert(!m_declaration->name().empty(), "Fallback function has no signature.");
+ switch (kind())
+ {
+ case Kind::Internal:
+ case Kind::External:
+ case Kind::CallCode:
+ case Kind::DelegateCall:
+ case Kind::Event:
+ break;
+ default:
+ solAssert(false, "Invalid function type for requesting external signature.");
+ }
bool const inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
FunctionTypePointer external = interfaceFunctionType();
@@ -2959,7 +2976,8 @@ bool FunctionType::isPure() const
m_kind == Kind::ABIEncode ||
m_kind == Kind::ABIEncodePacked ||
m_kind == Kind::ABIEncodeWithSelector ||
- m_kind == Kind::ABIEncodeWithSignature;
+ m_kind == Kind::ABIEncodeWithSignature ||
+ m_kind == Kind::ABIDecode;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@@ -3052,6 +3070,7 @@ bool FunctionType::padArguments() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
+ case Kind::BareStaticCall:
case Kind::SHA256:
case Kind::RIPEMD160:
case Kind::KECCAK256:
@@ -3315,6 +3334,15 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
FunctionType::Kind::ABIEncodeWithSignature,
true,
StateMutability::Pure
+ )},
+ {"decode", make_shared<FunctionType>(
+ TypePointers(),
+ TypePointers(),
+ strings{},
+ strings{},
+ FunctionType::Kind::ABIDecode,
+ true,
+ StateMutability::Pure
)}
});
default:
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 34f862c3..0b1b5d6d 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -904,6 +904,7 @@ public:
BareCall, ///< CALL without function hash
BareCallCode, ///< CALLCODE without function hash
BareDelegateCall, ///< DELEGATECALL without function hash
+ BareStaticCall, ///< STATICCALL without function hash
Creation, ///< external call using CREATE
Send, ///< CALL, but without data and gas
Transfer, ///< CALL, but without data and throws on error
@@ -934,7 +935,8 @@ public:
ABIEncodePacked,
ABIEncodeWithSelector,
ABIEncodeWithSignature,
- GasLeft ///< gasleft()
+ ABIDecode,
+ GasLeft, ///< gasleft()
};
virtual Category category() const override { return Category::Function; }
@@ -1050,7 +1052,7 @@ public:
/// @returns true iff the function type is equal to the given type, ignoring state mutability differences.
bool equalExcludingStateMutability(FunctionType const& _other) const;
- /// @returns true if the ABI is used for this call (only meaningful for external calls)
+ /// @returns true if the ABI is NOT used for this call (only meaningful for external calls)
bool isBareCall() const;
Kind const& kind() const { return m_kind; }
StateMutability stateMutability() const { return m_stateMutability; }
@@ -1089,6 +1091,7 @@ public:
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
+ case FunctionType::Kind::BareStaticCall:
return true;
default:
return false;
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 412a7255..53a06090 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -571,6 +571,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
+ case FunctionType::Kind::BareStaticCall:
_functionCall.expression().accept(*this);
appendExternalFunctionCall(function, arguments);
break;
@@ -1070,6 +1071,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// stack now: <memory pointer>
break;
}
+ case FunctionType::Kind::ABIDecode:
+ {
+ arguments.front()->accept(*this);
+ TypePointer firstArgType = arguments.front()->annotation().type;
+ TypePointers const& targetTypes = dynamic_cast<TupleType const&>(*_functionCall.annotation().type).components();
+ if (
+ *firstArgType == ArrayType(DataLocation::CallData) ||
+ *firstArgType == ArrayType(DataLocation::CallData, true)
+ )
+ utils().abiDecode(targetTypes, false);
+ else
+ {
+ utils().convertType(*firstArgType, ArrayType(DataLocation::Memory));
+ m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
+ m_context << Instruction::SWAP1 << Instruction::MLOAD;
+ // stack now: <mem_pos> <length>
+
+ utils().abiDecode(targetTypes, true);
+ }
+ break;
+ }
case FunctionType::Kind::GasLeft:
m_context << Instruction::GAS;
break;
@@ -1143,18 +1165,19 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
solAssert(false, "event not found");
// no-op, because the parent node will do the job
break;
- case FunctionType::Kind::External:
- case FunctionType::Kind::Creation:
case FunctionType::Kind::DelegateCall:
+ _memberAccess.expression().accept(*this);
+ m_context << funType->externalIdentifier();
+ break;
case FunctionType::Kind::CallCode:
+ case FunctionType::Kind::External:
+ case FunctionType::Kind::Creation:
case FunctionType::Kind::Send:
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
+ case FunctionType::Kind::BareStaticCall:
case FunctionType::Kind::Transfer:
- _memberAccess.expression().accept(*this);
- m_context << funType->externalIdentifier();
- break;
case FunctionType::Kind::Log0:
case FunctionType::Kind::Log1:
case FunctionType::Kind::Log2:
@@ -1252,7 +1275,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
);
m_context << Instruction::BALANCE;
}
- else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member))
+ else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member))
utils().convertType(
*_memberAccess.expression().annotation().type,
IntegerType(160, IntegerType::Modifier::Address),
@@ -1804,10 +1827,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
auto funKind = _functionType.kind();
- bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
+
+ solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
+
+ bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall;
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
- bool useStaticCall = _functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
+ bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (_functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata();
unsigned retSize = 0;
diff --git a/test/buglistTests.js b/test/buglistTests.js
new file mode 100755
index 00000000..6b7df2f2
--- /dev/null
+++ b/test/buglistTests.js
@@ -0,0 +1,45 @@
+#!/usr/bin/env node
+
+"use strict";
+
+var fs = require('fs')
+var bugs = JSON.parse(fs.readFileSync(__dirname + '/../docs/bugs.json', 'utf8'))
+
+var bugsByName = {}
+for (var i in bugs)
+{
+ if (bugs[i].name in bugsByName)
+ {
+ throw "Duplicate bug name: " + bugs[i].name
+ }
+ bugsByName[bugs[i].name] = bugs[i]
+}
+
+var tests = fs.readFileSync(__dirname + '/buglist_test_vectors.md', 'utf8')
+
+var testVectorParser = /\s*#\s+(\S+)\s+## buggy\n([^#]*)## fine\n([^#]*)/g
+
+var result;
+while ((result = testVectorParser.exec(tests)) !== null)
+{
+ var name = result[1]
+ var buggy = result[2].split('\n--\n')
+ var fine = result[3].split('\n--\n')
+ console.log("Testing " + name + " with " + buggy.length + " buggy and " + fine.length + " fine instances")
+
+ var regex = RegExp(bugsByName[name].check['regex-source'])
+ for (var i in buggy)
+ {
+ if (!regex.exec(buggy[i]))
+ {
+ throw "Bug " + name + ": Buggy source does not match: " + buggy[i]
+ }
+ }
+ for (var i in fine)
+ {
+ if (regex.exec(fine[i]))
+ {
+ throw "Bug " + name + ": Non-buggy source matches: " + fine[i]
+ }
+ }
+}
diff --git a/test/buglist_test_vectors.md b/test/buglist_test_vectors.md
new file mode 100644
index 00000000..ce95403b
--- /dev/null
+++ b/test/buglist_test_vectors.md
@@ -0,0 +1,69 @@
+# NestedArrayFunctionCallDecoder
+
+## buggy
+
+function f() pure returns (uint[2][2]) { }
+
+--
+
+function f() returns (uint[2][2] a) { }
+
+--
+
+function f() returns (uint x, uint[200][2] a) { }
+
+--
+
+function f() returns (uint[200][2] a, uint x) { }
+
+--
+
+function f() returns (uint[200][2] a, uint x);
+
+--
+
+function f() returns (
+ uint
+ [
+ 200
+ ]
+ [2]
+ a, uint x);
+
+--
+
+function f() returns (
+ uint
+ [
+ ContractName.ConstantName
+ ]
+ [2]
+ a, uint x);
+
+## fine
+
+function f() returns (uint[2]) { }
+
+--
+
+function f() public pure returns (uint[2][] a) { }
+
+--
+
+function f() public pure returns (uint[ 2 ] [ ] a) { }
+
+--
+
+function f() public pure returns (uint x, uint[] a) { }
+
+--
+
+function f(uint[2][2]) { }
+
+--
+
+function f() m(uint[2][2]) { }
+
+--
+
+function f() returns (uint, uint) { uint[2][2] memory x; }
diff --git a/test/libdevcore/StringUtils.cpp b/test/libdevcore/StringUtils.cpp
index 94f1b753..76c11b82 100644
--- a/test/libdevcore/StringUtils.cpp
+++ b/test/libdevcore/StringUtils.cpp
@@ -48,6 +48,7 @@ BOOST_AUTO_TEST_CASE(test_similarity)
BOOST_CHECK_EQUAL(stringWithinDistance("abc", "abcdef", 2), false);
BOOST_CHECK_EQUAL(stringWithinDistance("abcd", "wxyz", 2), false);
BOOST_CHECK_EQUAL(stringWithinDistance("", "", 2), true);
+ BOOST_CHECK_EQUAL(stringWithinDistance("YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY", "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZ", 2, 6400), false);
}
BOOST_AUTO_TEST_CASE(test_dldistance)
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index af2b3485..2bf20126 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -3592,6 +3592,19 @@ BOOST_AUTO_TEST_CASE(default_fallback_throws)
)YY";
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("f()"), encodeArgs(0));
+
+ if (dev::test::Options::get().evmVersion().hasStaticCall())
+ {
+ char const* sourceCode = R"YY(
+ contract A {
+ function f() public returns (bool) {
+ return address(this).staticcall("");
+ }
+ }
+ )YY";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(0));
+ }
}
BOOST_AUTO_TEST_CASE(short_data_calls_fallback)
@@ -3937,6 +3950,209 @@ BOOST_AUTO_TEST_CASE(event_really_really_lots_of_data_from_storage)
BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)")));
}
+BOOST_AUTO_TEST_CASE(event_struct_memory_v2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ struct S { uint a; }
+ event E(S);
+ function createEvent(uint x) public {
+ emit E(S(x));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 x(42);
+ callContractFunction("createEvent(uint256)", x);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(x));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E((uint256))")));
+}
+
+BOOST_AUTO_TEST_CASE(event_struct_storage_v2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ struct S { uint a; }
+ event E(S);
+ S s;
+ function createEvent(uint x) public {
+ s.a = x;
+ emit E(s);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 x(42);
+ callContractFunction("createEvent(uint256)", x);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(x));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E((uint256))")));
+}
+
+BOOST_AUTO_TEST_CASE(event_dynamic_array_memory)
+{
+ char const* sourceCode = R"(
+ contract C {
+ event E(uint[]);
+ function createEvent(uint x) public {
+ uint[] memory arr = new uint[](3);
+ arr[0] = x;
+ arr[1] = x + 1;
+ arr[2] = x + 2;
+ emit E(arr);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 x(42);
+ callContractFunction("createEvent(uint256)", x);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 3, x, x + 1, x + 2));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[])")));
+}
+
+BOOST_AUTO_TEST_CASE(event_dynamic_array_memory_v2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ event E(uint[]);
+ function createEvent(uint x) public {
+ uint[] memory arr = new uint[](3);
+ arr[0] = x;
+ arr[1] = x + 1;
+ arr[2] = x + 2;
+ emit E(arr);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 x(42);
+ callContractFunction("createEvent(uint256)", x);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 3, x, x + 1, x + 2));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[])")));
+}
+
+BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_memory_v2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ event E(uint[][]);
+ function createEvent(uint x) public {
+ uint[][] memory arr = new uint[][](2);
+ arr[0] = new uint[](2);
+ arr[1] = new uint[](2);
+ arr[0][0] = x;
+ arr[0][1] = x + 1;
+ arr[1][0] = x + 2;
+ arr[1][1] = x + 3;
+ emit E(arr);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 x(42);
+ callContractFunction("createEvent(uint256)", x);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[][])")));
+}
+
+BOOST_AUTO_TEST_CASE(event_dynamic_array_storage)
+{
+ char const* sourceCode = R"(
+ contract C {
+ event E(uint[]);
+ uint[] arr;
+ function createEvent(uint x) public {
+ arr.length = 3;
+ arr[0] = x;
+ arr[1] = x + 1;
+ arr[2] = x + 2;
+ emit E(arr);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 x(42);
+ callContractFunction("createEvent(uint256)", x);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 3, x, x + 1, x + 2));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[])")));
+}
+
+BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ event E(uint[]);
+ uint[] arr;
+ function createEvent(uint x) public {
+ arr.length = 3;
+ arr[0] = x;
+ arr[1] = x + 1;
+ arr[2] = x + 2;
+ emit E(arr);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 x(42);
+ callContractFunction("createEvent(uint256)", x);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 3, x, x + 1, x + 2));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[])")));
+}
+
+BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ event E(uint[][]);
+ uint[][] arr;
+ function createEvent(uint x) public {
+ arr.length = 2;
+ arr[0].length = 2;
+ arr[1].length = 2;
+ arr[0][0] = x;
+ arr[0][1] = x + 1;
+ arr[1][0] = x + 2;
+ arr[1][1] = x + 3;
+ emit E(arr);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ u256 x(42);
+ callContractFunction("createEvent(uint256)", x);
+ BOOST_REQUIRE_EQUAL(m_logs.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
+ BOOST_CHECK(m_logs[0].data == encodeArgs(0x20, 2, 0x40, 0xa0, 2, x, x + 1, 2, x + 2, x + 3));
+ BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1);
+ BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(uint256[][])")));
+}
+
BOOST_AUTO_TEST_CASE(event_indexed_string)
{
char const* sourceCode = R"(
@@ -4215,6 +4431,49 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall)
BOOST_CHECK_EQUAL(balanceAt(c_senderAddress), 50 + 11);
}
+BOOST_AUTO_TEST_CASE(generic_staticcall)
+{
+ if (dev::test::Options::get().evmVersion().hasStaticCall())
+ {
+ char const* sourceCode = R"**(
+ contract A {
+ uint public x;
+ constructor() public { x = 42; }
+ function pureFunction(uint256 p) public pure returns (uint256) { return p; }
+ function viewFunction(uint256 p) public view returns (uint256) { return p + x; }
+ function nonpayableFunction(uint256 p) public returns (uint256) { x = p; return x; }
+ function assertFunction(uint256 p) public view returns (uint256) { assert(x == p); return x; }
+ }
+ contract C {
+ function f(address a) public view returns (bool)
+ {
+ return a.staticcall(abi.encodeWithSignature("pureFunction(uint256)", 23));
+ }
+ function g(address a) public view returns (bool)
+ {
+ return a.staticcall(abi.encodeWithSignature("viewFunction(uint256)", 23));
+ }
+ function h(address a) public view returns (bool)
+ {
+ return a.staticcall(abi.encodeWithSignature("nonpayableFunction(uint256)", 23));
+ }
+ function i(address a, uint256 v) public view returns (bool)
+ {
+ return a.staticcall(abi.encodeWithSignature("assertFunction(uint256)", v));
+ }
+ }
+ )**";
+ compileAndRun(sourceCode, 0, "A");
+ u160 const c_addressA = m_contractAddress;
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f(address)", c_addressA), encodeArgs(true));
+ ABI_CHECK(callContractFunction("g(address)", c_addressA), encodeArgs(true));
+ ABI_CHECK(callContractFunction("h(address)", c_addressA), encodeArgs(false));
+ ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 42), encodeArgs(true));
+ ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 23), encodeArgs(false));
+ }
+}
+
BOOST_AUTO_TEST_CASE(library_call_in_homestead)
{
char const* sourceCode = R"(
@@ -12216,6 +12475,19 @@ BOOST_AUTO_TEST_CASE(bare_call_invalid_address)
compileAndRun(sourceCode, 0, "C");
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1)));
ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(1)));
+
+ if (dev::test::Options::get().evmVersion().hasStaticCall())
+ {
+ char const* sourceCode = R"YY(
+ contract C {
+ function f() external returns (bool) {
+ return address(0x4242).staticcall("");
+ }
+ }
+ )YY";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1)));
+ }
}
BOOST_AUTO_TEST_CASE(delegatecall_return_value)
@@ -13195,6 +13467,203 @@ BOOST_AUTO_TEST_CASE(senders_balance)
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(27)));
}
+BOOST_AUTO_TEST_CASE(abi_decode_trivial)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(bytes memory data) public pure returns (uint) {
+ return abi.decode(data, (uint));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f(bytes)", 0x20, 0x20, 33), encodeArgs(u256(33)));
+}
+
+BOOST_AUTO_TEST_CASE(abi_encode_decode_simple)
+{
+ char const* sourceCode = R"XX(
+ contract C {
+ function f() public pure returns (uint, bytes memory) {
+ bytes memory arg = "abcdefg";
+ return abi.decode(abi.encode(uint(33), arg), (uint, bytes));
+ }
+ }
+ )XX";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f()"),
+ encodeArgs(33, 0x40, 7, "abcdefg")
+ );
+}
+
+BOOST_AUTO_TEST_CASE(abi_decode_simple)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(bytes memory data) public pure returns (uint, bytes memory) {
+ return abi.decode(data, (uint, bytes));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"),
+ encodeArgs(33, 0x40, 7, "abcdefg")
+ );
+}
+
+BOOST_AUTO_TEST_CASE(abi_decode_v2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ struct S { uint a; uint[] b; }
+ function f() public pure returns (S memory) {
+ S memory s;
+ s.a = 8;
+ s.b = new uint[](3);
+ s.b[0] = 9;
+ s.b[1] = 10;
+ s.b[2] = 11;
+ return abi.decode(abi.encode(s), (S));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f()"),
+ encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(abi_decode_simple_storage)
+{
+ char const* sourceCode = R"(
+ contract C {
+ bytes data;
+ function f(bytes memory _data) public returns (uint, bytes memory) {
+ data = _data;
+ return abi.decode(data, (uint, bytes));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"),
+ encodeArgs(33, 0x40, 7, "abcdefg")
+ );
+}
+
+BOOST_AUTO_TEST_CASE(abi_decode_v2_storage)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ bytes data;
+ struct S { uint a; uint[] b; }
+ function f() public returns (S memory) {
+ S memory s;
+ s.a = 8;
+ s.b = new uint[](3);
+ s.b[0] = 9;
+ s.b[1] = 10;
+ s.b[2] = 11;
+ data = abi.encode(s);
+ return abi.decode(data, (S));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f()"),
+ encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(abi_decode_calldata)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(bytes calldata data) external pure returns (uint, bytes memory r) {
+ return abi.decode(data, (uint, bytes));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"),
+ encodeArgs(33, 0x40, 7, "abcdefg")
+ );
+}
+
+BOOST_AUTO_TEST_CASE(abi_decode_v2_calldata)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ struct S { uint a; uint[] b; }
+ function f(bytes calldata data) external pure returns (S memory) {
+ return abi.decode(data, (S));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f(bytes)", 0x20, 0x20 * 7, 0x20, 33, 0x40, 3, 10, 11, 12),
+ encodeArgs(0x20, 33, 0x40, 3, 10, 11, 12)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(abi_decode_static_array)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(bytes calldata data) external pure returns (uint[2][3] memory) {
+ return abi.decode(data, (uint[2][3]));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6),
+ encodeArgs(1, 2, 3, 4, 5, 6)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(abi_decode_static_array_v2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ function f(bytes calldata data) external pure returns (uint[2][3] memory) {
+ return abi.decode(data, (uint[2][3]));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6),
+ encodeArgs(1, 2, 3, 4, 5, 6)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(abi_decode_dynamic_array)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(bytes calldata data) external pure returns (uint[] memory) {
+ return abi.decode(data, (uint[]));
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(
+ callContractFunction("f(bytes)", 0x20, 6 * 0x20, 0x20, 4, 3, 4, 5, 6),
+ encodeArgs(0x20, 4, 3, 4, 5, 6)
+ );
+}
+
BOOST_AUTO_TEST_CASE(write_storage_external)
{
char const* sourceCode = R"(
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 55e81867..387505a5 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -433,6 +433,37 @@ BOOST_AUTO_TEST_CASE(getter_is_memory_type)
}
}
+BOOST_AUTO_TEST_CASE(address_staticcall)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() public view returns(bool) {
+ return address(0x4242).staticcall("");
+ }
+ }
+ )";
+
+ if (dev::test::Options::get().evmVersion().hasStaticCall())
+ CHECK_SUCCESS_NO_WARNINGS(sourceCode);
+ else
+ CHECK_ERROR(sourceCode, TypeError, "\"staticcall\" is not supported by the VM version.");
+}
+
+BOOST_AUTO_TEST_CASE(address_staticcall_value)
+{
+ if (dev::test::Options::get().evmVersion().hasStaticCall())
+ {
+ char const* sourceCode = R"(
+ contract C {
+ function f() public view {
+ address(0x4242).staticcall.value;
+ }
+ }
+ )";
+ CHECK_ERROR(sourceCode, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp
index 299cd084..d993b92e 100644
--- a/test/libsolidity/ViewPureChecker.cpp
+++ b/test/libsolidity/ViewPureChecker.cpp
@@ -53,8 +53,11 @@ BOOST_AUTO_TEST_CASE(environment_access)
"tx.origin",
"tx.gasprice",
"this",
- "address(1).balance"
+ "address(1).balance",
};
+ if (dev::test::Options::get().evmVersion().hasStaticCall())
+ view.emplace_back("address(0x4242).staticcall(\"\")");
+
// ``block.blockhash`` and ``blockhash`` are tested separately below because their usage will
// produce warnings that can't be handled in a generic way.
vector<string> pure{
@@ -95,6 +98,22 @@ BOOST_AUTO_TEST_CASE(environment_access)
);
}
+BOOST_AUTO_TEST_CASE(address_staticcall)
+{
+ string text = R"(
+ contract C {
+ function i() view public returns (bool) {
+ return address(0x4242).staticcall("");
+ }
+ }
+ )";
+ if (!dev::test::Options::get().evmVersion().hasStaticCall())
+ CHECK_ERROR(text, TypeError, "\"staticcall\" is not supported by the VM version.");
+ else
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+
BOOST_AUTO_TEST_CASE(assembly_staticcall)
{
string text = R"(
diff --git a/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol
new file mode 100644
index 00000000..aaf6028a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol
@@ -0,0 +1,7 @@
+pragma experimental ABIEncoderV2;
+contract c {
+ event E(uint[] indexed);
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
+// TypeError: (59-65): Indexed reference types cannot yet be used with ABIEncoderV2.
diff --git a/test/libsolidity/syntaxTests/events/event_array_v2.sol b/test/libsolidity/syntaxTests/events/event_array_v2.sol
new file mode 100644
index 00000000..9ccd9fc9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/events/event_array_v2.sol
@@ -0,0 +1,6 @@
+pragma experimental ABIEncoderV2;
+contract c {
+ event E(uint[]);
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol
new file mode 100644
index 00000000..ffae5b9c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol
@@ -0,0 +1,7 @@
+pragma experimental ABIEncoderV2;
+contract c {
+ event E(uint[][] indexed);
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
+// TypeError: (59-67): Indexed reference types cannot yet be used with ABIEncoderV2.
diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol b/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol
new file mode 100644
index 00000000..efc7439e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol
@@ -0,0 +1,6 @@
+pragma experimental ABIEncoderV2;
+contract c {
+ event E(uint[][]);
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
diff --git a/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol
new file mode 100644
index 00000000..a8e0837f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol
@@ -0,0 +1,8 @@
+pragma experimental ABIEncoderV2;
+contract c {
+ struct S { uint a ; }
+ event E(S indexed);
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
+// TypeError: (85-86): Indexed reference types cannot yet be used with ABIEncoderV2.
diff --git a/test/libsolidity/syntaxTests/events/event_struct_v2.sol b/test/libsolidity/syntaxTests/events/event_struct_v2.sol
new file mode 100644
index 00000000..97ca61b6
--- /dev/null
+++ b/test/libsolidity/syntaxTests/events/event_struct_v2.sol
@@ -0,0 +1,7 @@
+pragma experimental ABIEncoderV2;
+contract c {
+ struct S { uint a ; }
+ event E(S);
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/573_similar_name_longer_than_80_not_suggested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/573_similar_name_longer_than_80_not_suggested.sol
new file mode 100644
index 00000000..c6719d8c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/573_similar_name_longer_than_80_not_suggested.sol
@@ -0,0 +1,7 @@
+contract test {
+ function f() public {
+ int YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY;
+ }
+}
+// ----
+// DeclarationError: (146-236): Undeclared identifier.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/574_similar_name_shorter_than_80_suggested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/574_similar_name_shorter_than_80_suggested.sol
new file mode 100644
index 00000000..61fb2d82
--- /dev/null
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/574_similar_name_shorter_than_80_suggested.sol
@@ -0,0 +1,7 @@
+contract test {
+ function f() public {
+ int YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY;
+ }
+}
+// ----
+// DeclarationError: (137-216): Undeclared identifier. Did you mean "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"? \ No newline at end of file
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_calldata.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_calldata.sol
new file mode 100644
index 00000000..28d2f2e7
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_calldata.sol
@@ -0,0 +1,8 @@
+// This restriction might be lifted in the future
+contract C {
+ function f() public pure {
+ abi.decode("abc", (bytes calldata));
+ }
+}
+// ----
+// ParserError: (121-129): Expected ',' but got 'calldata'
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_invalid_arg_count.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_invalid_arg_count.sol
new file mode 100644
index 00000000..f903e1ed
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_invalid_arg_count.sol
@@ -0,0 +1,12 @@
+contract C {
+ function f() public pure {
+ abi.decode();
+ abi.decode(msg.data);
+ abi.decode(msg.data, uint, uint);
+ }
+}
+// ----
+// TypeError: (46-58): This function takes two arguments, but 0 were provided.
+// TypeError: (64-84): This function takes two arguments, but 1 were provided.
+// TypeError: (90-122): This function takes two arguments, but 3 were provided.
+// TypeError: (111-115): The second argument to "abi.decode" has to be a tuple of types.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory.sol
new file mode 100644
index 00000000..e4667e34
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f() public pure {
+ abi.decode("abc", (bytes memory, uint[][2] memory));
+ }
+}
+// ----
+// ParserError: (71-77): Expected ',' but got 'memory'
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory_v2.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory_v2.sol
new file mode 100644
index 00000000..8a7462d1
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory_v2.sol
@@ -0,0 +1,10 @@
+pragma experimental "ABIEncoderV2";
+
+contract C {
+ struct S { uint x; uint[] b; }
+ function f() public pure returns (S memory, bytes memory, uint[][2] memory) {
+ return abi.decode("abc", (S, bytes, uint[][2]));
+ }
+}
+// ----
+// Warning: (0-35): Experimental features are turned on. Do not use experimental features on live deployments.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nontuple.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nontuple.sol
new file mode 100644
index 00000000..d813c712
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nontuple.sol
@@ -0,0 +1,11 @@
+contract C {
+ function f() public pure {
+ abi.decode("abc", uint);
+ abi.decode("abc", this);
+ abi.decode("abc", f());
+ }
+}
+// ----
+// TypeError: (64-68): The second argument to "abi.decode" has to be a tuple of types.
+// TypeError: (93-97): The second argument to "abi.decode" has to be a tuple of types.
+// TypeError: (122-125): The second argument to "abi.decode" has to be a tuple of types.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_simple.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_simple.sol
new file mode 100644
index 00000000..356ee91c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_simple.sol
@@ -0,0 +1,5 @@
+contract C {
+ function f() public pure returns (uint, bytes32, C) {
+ return abi.decode("abc", (uint, bytes32, C));
+ }
+}
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_singletontuple.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_singletontuple.sol
new file mode 100644
index 00000000..57eccacf
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_singletontuple.sol
@@ -0,0 +1,6 @@
+contract C {
+ function f() public pure returns (uint) {
+ return abi.decode("abc", (uint));
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_storage.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_storage.sol
new file mode 100644
index 00000000..d9910b64
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_storage.sol
@@ -0,0 +1,8 @@
+// This restriction might be lifted in the future
+contract C {
+ function f() {
+ abi.decode("abc", (bytes storage));
+ }
+}
+// ----
+// ParserError: (109-116): Expected ',' but got 'storage'