diff options
55 files changed, 693 insertions, 287 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 0cc0edec..c3caf86e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,7 +3,7 @@ name: Bug Report about: Bug reports about the Solidity Compiler. --- -## Prerequisites +<!--## Prerequisites - First, many thanks for taking part in the community. We really appreciate that. - We realize there is a lot of information requested here. We ask only that you do your best to provide as much information as possible so we can better help you. @@ -12,12 +12,11 @@ about: Bug reports about the Solidity Compiler. - [Stack Overflow](https://ethereum.stackexchange.com/) - Ensure the issue isn't already reported. - The issue should be reproducible with the latest solidity version; however, this isn't a hard requirement and being reproducible with an older version is sufficient. - -*Delete the above section and the instructions in the sections below before submitting* +--> ## Description -Please shortly describe the bug you have found, and what you expect instead. +<!--Please shortly describe the bug you have found, and what you expect instead.--> ## Environment @@ -28,6 +27,7 @@ Please shortly describe the bug you have found, and what you expect instead. ## Steps to Reproduce +<!-- Please provide a *minimal* source code example to trigger the bug you have found. Please also mention any command line flags that are necessary for triggering the bug. Provide as much information as necessary to reproduce the bug. @@ -36,3 +36,4 @@ Provide as much information as necessary to reproduce the bug. // Some *minimal* Solidity source code to reproduce the bug. // ... ``` +-->
\ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 6b98fb99..6702b62c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -3,7 +3,7 @@ name: Feature Request about: Solidity language or infrastructure feature requests. --- -## Prerequisites +<!--## Prerequisites - First, many thanks for taking part in the community. We really appreciate that. - We realize there is a lot of data requested here. We ask only that you do your best to provide as much information as possible so we can better help you. @@ -14,24 +14,34 @@ about: Solidity language or infrastructure feature requests. *Delete the above section and the instructions in the sections below before submitting* +--> + ## Abstract +<!-- Please describe by example what problem you see in the current Solidity language and reason about it. +--> ## Motivation +<!-- In this section you describe how you propose to address the problem you described earlier, including by giving one or more exemplary source code snippets for demonstration. +--> ## Specification +<!-- The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow any developer to implement the functionality. +--> ## Backwards Compatibility +<!-- All language changes that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. Please describe how you propose to deal with these incompatibilities. +-->
\ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/general.md b/.github/ISSUE_TEMPLATE/general.md index 2d277865..410b42e0 100644 --- a/.github/ISSUE_TEMPLATE/general.md +++ b/.github/ISSUE_TEMPLATE/general.md @@ -3,7 +3,7 @@ name: General Feedback about: Any general feedback (neither feature request nor bug reports) --- -## Prerequisites +<!--## Prerequisites - First, many thanks for taking part in the community. We really appreciate that. - Read the [contributing guidelines](http://solidity.readthedocs.io/en/latest/contributing.html). @@ -13,7 +13,9 @@ about: Any general feedback (neither feature request nor bug reports) - Ensure the issue isn't already reported. *Delete the above section and the instructions in the sections below before submitting* - +--> ## Description +<!-- Please describe the purpose of your ticket. +--> diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6f544bc1..9734fe81 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,9 @@ -### Your checklist for this pull request +<!--### Your checklist for this pull request Please review the [guidelines for contributing](http://solidity.readthedocs.io/en/latest/contributing.html) to this repository. Please also note that this project is released with a [Contributor Code of Conduct](CONDUCT.md). By participating in this project you agree to abide by its terms. +--> ### Checklist - [ ] Code compiles correctly @@ -13,6 +14,9 @@ Please also note that this project is released with a [Contributor Code of Condu - [ ] Used meaningful commit messages ### Description + +<!-- Please explain the changes you made here. Thank you for your help! +-->
\ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 20b4d97b..f2c84d20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.4.26") +set(PROJECT_VERSION "0.5.0") project(solidity VERSION ${PROJECT_VERSION}) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) diff --git a/Changelog.md b/Changelog.md index 21de2ce6..fbb41ece 100644 --- a/Changelog.md +++ b/Changelog.md @@ -84,11 +84,14 @@ Language Features: * 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, (...))``. + * General: Disallow zero length for fixed-size arrays. * Parser: Accept the ``address payable`` type during parsing. Compiler Features: + * Build System: Support for Mojave version of macOS added. * C API (``libsolc``): Export the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods. - * Code Generator: ``CREATE2`` instruction has been updated to match EIP1014 (aka "Skinny CREATE2"). + * Code Generator: ``CREATE2`` instruction has been updated to match EIP1014 (aka "Skinny CREATE2"). It also is accepted as part of Constantinople. + * Code Generator: ``EXTCODEHASH`` instruction has been added based on EIP1052. * Type Checker: Nicer error message when trying to reference overloaded identifiers in inline assembly. * Type Checker: Show named argument in case of error. * Type System: IntegerType is split into IntegerType and AddressType internally. @@ -101,6 +104,7 @@ Bugfixes: * Tests: Fix chain parameters to make ipc tests work with newer versions of cpp-ethereum. * Code Generator: Fix allocation of byte arrays (zeroed out too much memory). * Code Generator: Properly handle negative number literals in ABIEncoderV2. + * Code Generator: Do not crash on using a length of zero for multidimensional fixed-size arrays. * Commandline Interface: Correctly handle paths with backslashes on windows. * Fix NatSpec json output for `@notice` and `@dev` tags on contract definitions. * Optimizer: Correctly estimate gas costs of constants for special cases. @@ -29,8 +29,10 @@ Instructions about how to build and install the Solidity compiler can be found i A "Hello World" program in Solidity is of even less use than in other languages, but still: ``` +pragma solidity ^0.4.16; + contract HelloWorld { - function f() pure returns (string memory) { + function helloWorld() external pure returns (string memory) { return "Hello, World!"; } } diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 5a7add5d..b49dd1e0 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -107,7 +107,10 @@ For most of the topics the compiler will provide suggestions. other way around is not allowed. Converting ``address`` to ``address payable`` is possible via conversion through ``uint160``. If ``c`` is a contract, ``address(c)`` results in ``address payable`` only if ``c`` has a - payable fallback function. + payable fallback function. If you use the :ref:`withdraw pattern<withdrawal_pattern>`, + you most likely do not have to change your code because ``transfer`` + is only used on ``msg.sender`` instead of stored addresses and ``msg.sender`` + is an ``address payable``. * Conversions between ``bytesX`` and ``uintY`` of different size are now disallowed due to ``bytesX`` padding on the right and ``uintY`` padding on @@ -222,6 +225,8 @@ Variables * Detecting cyclic dependencies in variables and structs is limited in recursion to 256. +* Fixed-size arrays with a length of zero are now disallowed. + Syntax ------ @@ -327,7 +332,7 @@ New version: :: - pragma solidity >0.4.25; + pragma solidity >0.4.99 <0.6.0; contract OtherContract { uint x; diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 4e7c88d0..7c9d9f9e 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -13,9 +13,9 @@ The Contract Application Binary Interface (ABI) is the standard way to interact from outside the blockchain and for contract-to-contract interaction. Data is encoded according to its type, as described in this specification. The encoding is not self describing and thus requires a schema in order to decode. -We assume the interface functions of a contract are strongly typed, known at compilation time and static. No introspection mechanism will be provided. We assume that all contracts will have the interface definitions of any contracts they call available at compile-time. +We assume the interface functions of a contract are strongly typed, known at compilation time and static. We assume that all contracts will have the interface definitions of any contracts they call available at compile-time. -This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. Should these cases become important they can be adequately handled as facilities built within the Ethereum ecosystem. +This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. .. _abi_function_selector: @@ -23,12 +23,12 @@ Function Selector ================= The first four bytes of the call data for a function call specifies the function to be called. It is the -first (left, high-order in big-endian) four bytes of the Keccak (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype, i.e. +first (left, high-order in big-endian) four bytes of the Keccak-256 (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype without data location specifier, i.e. the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used. .. note:: The return type of a function is not part of this signature. In :ref:`Solidity's function overloading <overload-function>` return types are not considered. The reason is to keep function call resolution context-independent. - The JSON description of the ABI however contains both inputs and outputs. See (the :ref:`JSON ABI <abi_json>`) + The :ref:`JSON description of the ABI<abi_json>` however contains both inputs and outputs. Argument Encoding ================= @@ -112,7 +112,7 @@ Properties: 1. The number of reads necessary to access a value is at most the depth of the value inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a previous version of the ABI, the number of reads scaled linearly with the total number of dynamic parameters in the worst case. - 2. The data of a variable or array element is not interleaved with other data and it is relocatable, i.e. it only uses relative "addresses" + 2. The data of a variable or array element is not interleaved with other data and it is relocatable, i.e. it only uses relative "addresses". We distinguish static and dynamic types. Static types are encoded in-place and dynamic types are encoded at a separately allocated location after the current block. @@ -211,7 +211,7 @@ Given the contract: :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract Foo { function bar(bytes3[2] memory) public pure {} @@ -437,8 +437,8 @@ A function description is a JSON object with the fields: * ``components``: used for tuple types (more below). - ``outputs``: an array of objects similar to ``inputs``, can be omitted if function doesn't return anything; -- ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state <pure-functions>`), ``view`` (:ref:`specified to not modify the blockchain state <view-functions>`), ``nonpayable`` (function does not accept ether) and ``payable`` (function accepts ether); -- ``payable``: ``true`` if function accepts ether, ``false`` otherwise; +- ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state <pure-functions>`), ``view`` (:ref:`specified to not modify the blockchain state <view-functions>`), ``nonpayable`` (function does not accept Ether) and ``payable`` (function accepts Ether); +- ``payable``: ``true`` if function accepts Ether, ``false`` otherwise; - ``constant``: ``true`` if function is either ``pure`` or ``view``, ``false`` otherwise. ``type`` can be omitted, defaulting to ``"function"``, likewise ``payable`` and ``constant`` can be omitted, both defaulting to ``false``. @@ -449,7 +449,7 @@ Constructor and fallback function never have ``name`` or ``outputs``. Fallback f The fields ``constant`` and ``payable`` are deprecated and will be removed in the future. Instead, the ``stateMutability`` field can be used to determine the same properties. .. note:: - Sending non-zero ether to non-payable function will revert the transaction. + Sending non-zero Ether to non-payable function will revert the transaction. An event description is a JSON object with fairly similar fields: @@ -468,7 +468,7 @@ For example, :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract Test { constructor() public { b = hex"12345678901234567890123456789012"; } @@ -515,7 +515,7 @@ As an example, the code :: - pragma solidity ^0.4.19; + pragma solidity >=0.4.19 <0.6.0; pragma experimental ABIEncoderV2; contract Test { diff --git a/docs/assembly.rst b/docs/assembly.rst index 90bfa1f1..c609fa9d 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -67,7 +67,7 @@ idea is that assembly libraries will be used to enhance the Solidity language. .. code:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; library GetCode { function at(address _addr) public view returns (bytes memory o_code) { @@ -92,7 +92,7 @@ efficient code, for example: .. code:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; library VectorSum { // This function is less efficient because the optimizer currently fails to @@ -271,12 +271,16 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t | +-------------------------+-----+---+-----------------------------------------------------------------+ +| extcodehash(a) | | C | code hash of address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ | create(v, p, s) | | F | create new contract with code mem[p...(p+s)) and send v wei | | | | | and return the new address | +-------------------------+-----+---+-----------------------------------------------------------------+ | create2(v, n, p, s) | | C | create new contract with code mem[p...(p+s)) at address | -| | | | keccak256(<address> . n . keccak256(mem[p...(p+s))) and send v | -| | | | wei and return the new address | +| | | | keccak256(0xff . self . n . keccak256(mem[p...(p+s))) | +| | | | and send v wei and return the new address, where ``0xff`` is a | +| | | | 8 byte value, ``self`` is the current contract's address | +| | | | as a 20 byte value and ``n`` is a big-endian 256-bit value | +-------------------------+-----+---+-----------------------------------------------------------------+ | call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) | | insize, out, outsize) | | | providing g gas and v wei and output area | @@ -381,7 +385,7 @@ Local Solidity variables are available for assignments, for example: .. code:: - pragma solidity ^0.4.11; + pragma solidity >=0.4.11 <0.6.0; contract C { uint b; @@ -420,7 +424,7 @@ be just ``0``, but it can also be a complex functional-style expression. .. code:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f(uint x) public view returns (uint b) { @@ -685,7 +689,7 @@ Example: We will follow an example compilation from Solidity to assembly. We consider the runtime bytecode of the following Solidity program:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f(uint x) public pure returns (uint y) { diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index d26e4377..84c18936 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -13,11 +13,11 @@ Withdrawal from Contracts The recommended method of sending funds after an effect is using the withdrawal pattern. Although the most intuitive method of sending Ether, as a result of an effect, is a -direct ``send`` call, this is not recommended as it +direct ``transfer`` call, this is not recommended as it introduces a potential security risk. You may read more about this on the :ref:`security_considerations` page. -This is an example of the withdrawal pattern in practice in +The following is an example of the withdrawal pattern in practice in a contract where the goal is to send the most money to the contract in order to become the "richest", inspired by `King of the Ether <https://www.kingoftheether.com/>`_. @@ -28,7 +28,7 @@ become the new richest. :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract WithdrawalContract { address public richest; @@ -65,7 +65,7 @@ This is as opposed to the more intuitive sending pattern: :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract SendContract { address payable public richest; @@ -130,7 +130,7 @@ restrictions highly readable. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract AccessRestriction { // These will be assigned at the construction @@ -282,7 +282,7 @@ function finishes. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract StateMachine { enum Stages { diff --git a/docs/contracts.rst b/docs/contracts.rst index f7ceb950..93f54e4a 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -42,7 +42,7 @@ This means that cyclic creation dependencies are impossible. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract OwnedToken { // TokenCreator is a contract type that is defined below. @@ -173,7 +173,7 @@ return parameter list for functions. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f(uint a) private pure returns (uint b) { return a + 1; } @@ -187,7 +187,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint private data; @@ -231,7 +231,7 @@ when they are declared. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint public data = 42; @@ -251,7 +251,7 @@ it evaluates to a state variable. If it is accessed externally :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint public data; @@ -270,7 +270,8 @@ to write a function, for example: :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; + contract arrayExample { // public state variable uint[] public myArray; @@ -295,7 +296,7 @@ The next example is more complex: :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Complex { struct Data { @@ -330,7 +331,7 @@ inheritable properties of contracts and may be overridden by derived contracts. :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract owned { constructor() public { owner = msg.sender; } @@ -456,7 +457,7 @@ value types and strings. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint constant x = 32**22 + 8; @@ -499,7 +500,7 @@ The following statements are considered modifying the state: :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract C { function f(uint a, uint b) public view returns (uint) { @@ -544,7 +545,7 @@ In addition to the list of state modifying statements explained above, the follo :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract C { function f(uint a, uint b) public pure returns (uint) { @@ -632,7 +633,7 @@ Like any function, the fallback function can execute complex operations as long :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract Test { // This function is called for all messages sent to @@ -683,7 +684,7 @@ The following example shows overloading of the function :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract A { function f(uint _in) public pure returns (uint out) { @@ -701,7 +702,7 @@ externally visible functions differ by their Solidity types but not by their ext :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; // This will not compile contract A { @@ -734,7 +735,7 @@ candidate, resolution fails. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract A { function f(uint8 _in) public pure returns (uint8 out) { @@ -794,7 +795,7 @@ All non-indexed arguments will be :ref:`ABI-encoded <ABI>` into the data part of :: - pragma solidity ^0.4.21; + pragma solidity >=0.4.21 <0.6.0; contract ClientReceipt { event Deposit( @@ -851,7 +852,7 @@ as topics. The event call above can be performed in the same way as :: - pragma solidity ^0.4.10; + pragma solidity >=0.4.10 <0.6.0; contract C { function f() public payable { @@ -899,7 +900,7 @@ Details are given in the following example. :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract owned { constructor() public { owner = msg.sender; } @@ -971,7 +972,7 @@ Note that above, we call ``mortal.kill()`` to "forward" the destruction request. The way this is done is problematic, as seen in the following example:: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract owned { constructor() public { owner = msg.sender; } @@ -1000,7 +1001,7 @@ derived override, but this function will bypass ``Base1.kill``, basically because it does not even know about ``Base1``. The way around this is to use ``super``:: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract owned { constructor() public { owner = msg.sender; } @@ -1059,7 +1060,7 @@ equivalent to ``constructor() public {}``. For example: :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract A { uint public a; @@ -1089,7 +1090,7 @@ The constructors of all the base contracts will be called following the linearization rules explained below. If the base constructors have arguments, derived contracts need to specify all of them. This can be done in two ways:: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract Base { uint x; @@ -1130,7 +1131,7 @@ Multiple Inheritance and Linearization Languages that allow multiple inheritance have to deal with several problems. One is the `Diamond Problem <https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem>`_. Solidity is similar to Python in that it uses "`C3 Linearization <https://en.wikipedia.org/wiki/C3_linearization>`_" -to force a specific order in the directed acyclic graph of base classes. This +to force a specific order in the directed acyclic graph (DAG) of base classes. This results in the desirable property of monotonicity but disallows some inheritance graphs. Especially, the order in which the base classes are given in the ``is`` directive is @@ -1148,7 +1149,7 @@ error "Linearization of inheritance graph impossible". :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract X {} contract A is X {} @@ -1179,7 +1180,7 @@ Abstract Contracts Contracts are marked as abstract when at least one of their functions lacks an implementation as in the following example (note that the function declaration header is terminated by ``;``):: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Feline { function utterance() public returns (bytes32); @@ -1187,7 +1188,7 @@ Contracts are marked as abstract when at least one of their functions lacks an i Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Feline { function utterance() public returns (bytes32); @@ -1238,7 +1239,7 @@ Interfaces are denoted by their own keyword: :: - pragma solidity ^0.4.11; + pragma solidity >=0.4.11 <0.6.0; interface Token { enum TokenType { Fungible, NonFungible } @@ -1294,13 +1295,13 @@ contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL`` .. index:: using for, set -The following example illustrates how to use libraries (butmanual method +The following example illustrates how to use libraries (but manual method be sure to check out :ref:`using for <using-for>` for a more advanced example to implement a set). :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; library Set { // We define a new struct datatype that will be used to @@ -1374,7 +1375,7 @@ custom types without the overhead of external function calls: :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; library BigInt { struct bigint { @@ -1515,7 +1516,7 @@ available without having to add further code. Let us rewrite the set example from the :ref:`libraries` in this way:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; // This is the same code as before, just without comments library Set { @@ -1565,7 +1566,7 @@ Let us rewrite the set example from the It is also possible to extend elementary types in that way:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; library Search { function indexOf(uint[] storage self, uint value) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index ae0abc49..80311a63 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -20,7 +20,7 @@ For example, suppose we want our contract to accept one kind of external calls with two integers, we would write something like:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract Simple { uint sum; @@ -40,7 +40,7 @@ The output parameters can be declared with the same syntax after the the sum and the product of the two given integers, then we would write:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract Simple { function arithmetic(uint _a, uint _b) @@ -99,7 +99,7 @@ Internal Function Calls Functions of the current contract can be called directly ("internally"), also recursively, as seen in this nonsensical example:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function g(uint a) public pure returns (uint ret) { return a + f(); } @@ -129,7 +129,7 @@ all function arguments have to be copied to memory. When calling functions of other contracts, the amount of Wei sent with the call and the gas can be specified with special options ``.value()`` and ``.gas()``, respectively:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract InfoFeed { function info() public payable returns (uint ret) { return 42; } @@ -176,7 +176,7 @@ parameters from the function declaration, but can be in arbitrary order. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { mapping(uint => uint) data; @@ -199,7 +199,7 @@ Those parameters will still be present on the stack, but they are inaccessible. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { // omitted name for parameter @@ -222,7 +222,7 @@ is compiled so recursive creation-dependencies are not possible. :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract D { uint public x; @@ -345,7 +345,7 @@ the two variables have the same name but disjoint scopes. :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract C { function minimalScoping() pure public { { @@ -366,7 +366,7 @@ In any case, you will get a warning about the outer variable being shadowed. :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; // This will report a warning contract C { function f() pure public returns (uint) { @@ -386,7 +386,7 @@ In any case, you will get a warning about the outer variable being shadowed. :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; // This will not compile contract C { function f() pure public returns (uint) { @@ -433,7 +433,7 @@ a message string for ``require``, but not for ``assert``. :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract Sharer { function sendHalf(address payable addr) public payable returns (uint balance) { @@ -479,7 +479,7 @@ The following example shows how an error string can be used together with revert :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract VendingMachine { function buy(uint amount) public payable { diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index d2b7de9c..a474f905 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -43,21 +43,15 @@ Can you return an array or a ``string`` from a solidity function call? Yes. See `array_receiver_and_returner.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/60_array_receiver_and_returner.sol>`_. -What is problematic, though, is returning any variably-sized data (e.g. a -variably-sized array like ``uint[]``) from a function **called from within Solidity**. -This is a limitation of the EVM and will be solved with the next protocol update. - -Returning variably-sized data as part of an external transaction or call is fine. - Is it possible to in-line initialize an array like so: ``string[] myarray = ["a", "b"];`` ========================================================================================= Yes. However it should be noted that this currently only works with statically sized memory arrays. You can even create an inline memory -array in the return statement. Pretty cool, huh? +array in the return statement. Example:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f() public pure returns (uint8[5] memory) { @@ -70,7 +64,7 @@ Example:: Can a contract function return a ``struct``? ============================================ -Yes, but only in ``internal`` function calls. +Yes, but only in ``internal`` function calls or if ``pragma experimental "ABIEncoderV2";`` is used. If I return an ``enum``, I only get integer values in web3.js. How to get the named values? =========================================================================================== @@ -87,7 +81,7 @@ should be noted that you must declare them as static memory arrays. Examples:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { struct S { @@ -127,7 +121,7 @@ which will be extended in the future. In addition, Arachnid has written `solidit For now, if you want to modify a string (even when you only want to know its length), you should always convert it to a ``bytes`` first:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { string s; @@ -145,7 +139,17 @@ you should always convert it to a ``bytes`` first:: Can I concatenate two strings? ============================== -You have to do it manually for now. +Yes, you can use ``abi.encodePacked``:: + + pragma solidity >=0.4.0 <0.6.0; + + library ConcatHelper { + function concat(bytes memory a, bytes memory b) + internal pure returns (bytes memory) { + return abi.encodePacked(a, b); + } + } + Why is the low-level function ``.call()`` less favorable than instantiating a contract with a variable (``ContractB b;``) and executing its functions (``b.doSomething();``)? ============================================================================================================================================================================= @@ -282,7 +286,7 @@ In the case of a ``contract A`` calling a new instance of ``contract B``, parent You will need to make sure that you have both contracts aware of each other's presence and that ``contract B`` has a ``payable`` constructor. In this example:: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract B { constructor() public payable {} @@ -299,8 +303,8 @@ In this example:: Can a contract function accept a two-dimensional array? ======================================================= -This is not yet implemented for external calls and dynamic arrays - -you can only use one level of dynamic arrays. +If you want to pass two-dimensional arrays across non-internal functions, +you most likely need to use ``pragma experimental "ABIEncoderV2";``. What is the relationship between ``bytes32`` and ``string``? Why is it that ``bytes32 somevar = "stringliteral";`` works and what does the saved 32-byte hex value mean? ======================================================================================================================================================================== @@ -330,7 +334,7 @@ Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) Sure. Take care that if you cross the memory / storage boundary, independent copies will be created:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { uint[20] x; @@ -367,7 +371,7 @@ contract level) with ``arrayname.length = <some new length>;``. If you get the :: - pragma solidity ^0.4.18; + pragma solidity >=0.4.18 <0.6.0; // This will not compile contract C { @@ -401,7 +405,7 @@ case in C or Java). Is it possible to return an array of strings (``string[]``) from a Solidity function? ===================================================================================== -Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic array itself). +Only when ``pragma experimental "ABIEncoderV2";`` is used. What does the following strange check do in the Custom Token contract? ====================================================================== diff --git a/docs/index.rst b/docs/index.rst index 20fa1505..0449ff42 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,15 +6,18 @@ Solidity :alt: Solidity logo :align: center -Solidity is a contract-oriented, high-level language for implementing smart contracts. -It was influenced by C++, Python and JavaScript -and is designed to target the Ethereum Virtual Machine (EVM). +Solidity is an object-oriented, high-level language for implementing smart +contracts. Smart contracts are programs which govern the behaviour of accounts +within the Ethereum state. + +Solidity was influenced by C++, Python and JavaScript and is designed to target +the Ethereum Virtual Machine (EVM). Solidity is statically typed, supports inheritance, libraries and complex user-defined types among other features. -As you will see, it is possible to create contracts for voting, -crowdfunding, blind auctions, multi-signature wallets and more. +With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions, +and multi-signature wallets. .. note:: The best way to try out Solidity right now is using diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 5ba7ed12..5e841417 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -8,15 +8,16 @@ Introduction to Smart Contracts A Simple Smart Contract *********************** -Let us begin with the most basic example. It is fine if you do not understand everything -right now, we will go into more detail later. +Let us begin with a basic example that sets the value of a variable and exposes +it for other contracts to access. It is fine if you do not understand +everything right now, we will go into more detail later. Storage ======= :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract SimpleStorage { uint storedData; @@ -34,7 +35,7 @@ The first line simply tells that the source code is written for Solidity version 0.4.0 or anything newer that does not break functionality (up to, but not including, version 0.5.0). This is to ensure that the contract is not compilable with a new (breaking) compiler version, where it could behave differently. -So-called pragmas are common instrutions for compilers about how to treat the +So-called pragmas are common instructions for compilers about how to treat the source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_). A contract in the sense of Solidity is a collection of code (its *functions*) and @@ -80,7 +81,7 @@ registering with username and password — all you need is an Ethereum keypair. :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; contract Coin { // The keyword "public" makes those variables diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 11f85aac..fb18f8a9 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -13,6 +13,12 @@ and :ref:`pragma directives<pragma>`. Pragmas ======= +The ``pragma`` keyword can be used to enable certain compiler features +or checks. A pragma directive is always local to a source file, so +you have to add the pragma to all your files if you want enable it +in all of your project. If you :ref:`import<import>` another file, the pragma +from that file will not automatically apply to the importing file. + .. index:: ! pragma, version .. _version_pragma: @@ -43,6 +49,13 @@ the exact version of the compiler, so that bugfix releases are still possible. It is possible to specify much more complex rules for the compiler version, the expression follows those used by `npm <https://docs.npmjs.com/misc/semver>`_. +.. note:: + Using the version pragma will *not* change the version of the compiler. + It will also *not* enable or disable features of the compiler. It will just + instruct the compiler to check whether its version matches the one + required by the pragma. If it does not match, the compiler will issue + an error. + .. index:: ! pragma, experimental .. _experimental_pragma: @@ -64,6 +77,8 @@ for this part of the code is still under development) and has not received as much testing as the old encoder. You can activate it using ``pragma experimental ABIEncoderV2;``. +.. _smt_checker: + SMTChecker ~~~~~~~~~~ @@ -261,7 +276,7 @@ for the two input parameters and two returned values. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; /** @title Shape calculator. */ contract ShapeCalculator { diff --git a/docs/metadata.rst b/docs/metadata.rst index 9c4f2574..c0613809 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -93,15 +93,16 @@ explanatory purposes. } } +.. warning:: + Since the bytecode of the resulting contract contains the metadata hash, any + change to the metadata results in a change of the bytecode. This includes + changes to a filename or path, and since the metadata includes a hash of all the + sources used, a single whitespace change results in different metadata, and + different bytecode. + .. note:: Note the ABI definition above has no fixed order. It can change with compiler versions. -Since the bytecode of the resulting contract contains the metadata hash, any -change to the metadata results in a change of the bytecode. This includes -changes to a filename or path, and since the metadata includes a hash of all the -sources used, a single whitespace change results in different metadata, and -different bytecode. - Encoding of the Metadata Hash in the Bytecode ============================================= @@ -117,19 +118,28 @@ to the end of the deployed bytecode:: So in order to retrieve the data, the end of the deployed bytecode can be checked to match that pattern and use the Swarm hash to retrieve the file. +.. note:: + The compiler currently uses the "swarm version 0" hash of the metadata, + but this might change in the future, so do not rely on this sequence + to start with ``0xa1 0x65 'b' 'z' 'z' 'r' '0'``. We might also + add additional data to this CBOR structure, so the + best option is to use a proper CBOR parser. + + Usage for Automatic Interface Generation and NatSpec ==================================================== The metadata is used in the following way: A component that wants to interact -with a contract (e.g. Mist) retrieves the code of the contract, from that +with a contract (e.g. Mist or any wallet) retrieves the code of the contract, from that the Swarm hash of a file which is then retrieved. That file is JSON-decoded into a structure like above. The component can then use the ABI to automatically generate a rudimentary user interface for the contract. -Furthermore, Mist can use the userdoc to display a confirmation message to the user -whenever they interact with the contract. +Furthermore, the wallet can use the NatSpec user documentation to display a confirmation message to the user +whenever they interact with the contract, together with requesting +authorization for the transaction signature. Additional information about Ethereum Natural Specification (NatSpec) can be found `here <https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format>`_. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 5d2819df..12603f2e 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -48,7 +48,7 @@ non-elementary type, the positions are found by adding an offset of ``keccak256( So for the following contract snippet:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { struct s { uint a; uint b; } diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 8df12b7c..bd06276b 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -55,7 +55,7 @@ complete contract): :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract Fund { @@ -78,7 +78,7 @@ as it uses ``call`` which forwards all remaining gas by default: :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract Fund { @@ -97,7 +97,7 @@ outlined further below: :: - pragma solidity ^0.4.11; + pragma solidity >=0.4.11 <0.6.0; contract Fund { /// Mapping of ether shares of the contract. @@ -136,15 +136,16 @@ Sending and Receiving Ether - If a contract receives Ether (without a function being called), the fallback function is executed. If it does not have a fallback function, the Ether will be rejected (by throwing an exception). During the execution of the fallback function, the contract can only rely - on the "gas stipend" it is passed (2300 gas) being available to it at that time. This stipend is not enough to access storage in any way. + on the "gas stipend" it is passed (2300 gas) being available to it at that time. This stipend is not enough to modify storage + (do not take this for granted though, the stipend might change with future hard forks). To be sure that your contract can receive Ether in that way, check the gas requirements of the fallback function (for example in the "details" section in Remix). - There is a way to forward more gas to the receiving contract using ``addr.call.value(x)("")``. This is essentially the same as ``addr.transfer(x)``, only that it forwards all remaining gas and opens up the ability for the - recipient to perform more expensive actions (and it only returns a failure code - and does not automatically propagate the error). This might include calling back + recipient to perform more expensive actions (and it returns a failure code + instead of automatically propagating the error). This might include calling back into the sending contract or other state changes you might not have thought of. So it allows for great flexibility for honest users but also for malicious actors. @@ -182,7 +183,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract TxUserWallet { @@ -202,7 +203,7 @@ Now someone tricks you into sending ether to the address of this attack wallet: :: - pragma solidity >0.4.24; + pragma solidity >0.4.99 <0.6.0; interface TxUserWallet { function transferTo(address payable dest, uint amount) external; @@ -223,6 +224,26 @@ Now someone tricks you into sending ether to the address of this attack wallet: If your wallet had checked ``msg.sender`` for authorization, it would get the address of the attack wallet, instead of the owner address. But by checking ``tx.origin``, it gets the original address that kicked off the transaction, which is still the owner address. The attack wallet instantly drains all your funds. + +Two's Complement / Underflows / Overflows +========================================= + +As in many programming languages, Solidity's integer types are not actually integers. +They resemble integers when the values are small, but behave differently if the numbers are larger. +For example, the following is true: ``uint8(255) + uint8(1) == 0``. This situation is called +an *overflow*. It occurs when an operation is performed that requires a fixed size variable +to store a number (or piece of data) that is outside the range of the variable's data type. +An *underflow* is the converse situation: ``uint8(0) - uint8(1) == 255``. + +In general, read about the limits of two's complement representation, which even has some +more special edge cases for signed numbers. + +Try to use ``require`` to limit the size of inputs to a reasonable range and use the +:ref:`SMT checker<smt_checker>` to find potential overflows, or +use a library like +`SafeMath<https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol>` +if you want all overflows to cause a revert. + Minor Details ============= @@ -246,12 +267,8 @@ implications, there might be another issue buried beneath it. Any compiler warning we issue can be silenced by slight changes to the code. -Also try to enable the "0.5.0" safety features as early as possible -by adding ``pragma experimental "v0.5.0";``. Note that in this case, -the word ``experimental`` does not mean that the safety features are in any -way risky, it is just a way to enable some features that are -not yet part of the latest version of Solidity due to backwards -compatibility. +Always use the latest version of the compiler to be notified about all recently +introduced warnings. Restrict the Amount of Ether ============================ @@ -305,6 +322,12 @@ of "failsafe" mode, which, for example, disables most of the features, hands ove control to a fixed and trusted third party or just converts the contract into a simple "give me back my money" contract. +Ask for Peer Review +=================== + +The more people examine a piece of code, the more issues are found. +Asking people to review your code also helps as a cross-check to find out whether your code +is easy to understand - a very important criterion for good smart contracts. ******************* Formal Verification diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index d01886f8..0f9a71ab 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -36,7 +36,7 @@ of votes. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; /// @title Voting with delegation. contract Ballot { @@ -225,7 +225,7 @@ activate themselves. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract SimpleAuction { // Parameters of the auction. Times are either @@ -542,7 +542,7 @@ Safe Remote Purchase :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract Purchase { uint public value; @@ -793,7 +793,7 @@ The full contract :: - pragma solidity ^0.4.24; + pragma solidity >=0.4.24 <0.6.0; contract ReceiverPays { address owner = msg.sender; @@ -988,7 +988,7 @@ The full contract :: - pragma solidity ^0.4.24; + pragma solidity >=0.4.24 <0.6.0; contract SimplePaymentChannel { address payable public sender; // The account sending payments. diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index d8bc51b8..582e5338 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -26,7 +26,7 @@ storage. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract SimpleStorage { uint storedData; // State variable @@ -46,7 +46,7 @@ Functions are the executable units of code within a contract. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract SimpleAuction { function bid() public payable { // Function @@ -68,7 +68,7 @@ Function modifiers can be used to amend the semantics of functions in a declarat :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract Purchase { address public seller; @@ -95,7 +95,7 @@ Events are convenience interfaces with the EVM logging facilities. :: - pragma solidity ^0.4.21; + pragma solidity >=0.4.21 <0.6.0; contract SimpleAuction { event HighestBidIncreased(address bidder, uint amount); // Event @@ -119,7 +119,7 @@ Structs are custom defined types that can group several variables (see :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Ballot { struct Voter { // Struct @@ -140,7 +140,7 @@ Enums can be used to create custom types with a finite set of 'constant values' :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Purchase { enum State { Created, Locked, Inactive } // Enum diff --git a/docs/style-guide.rst b/docs/style-guide.rst index b97beebd..7b48ccad 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -52,7 +52,7 @@ Surround top level declarations in solidity source with two blank lines. Yes:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract A { // ... @@ -70,7 +70,7 @@ Yes:: No:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract A { // ... @@ -89,7 +89,7 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu Yes:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract A { function spam() public pure; @@ -109,7 +109,7 @@ Yes:: No:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract A { function spam() public pure { @@ -237,7 +237,7 @@ Import statements should always be placed at the top of the file. Yes:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; import "./Owned.sol"; @@ -251,7 +251,7 @@ Yes:: No:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract A { // ... @@ -283,7 +283,7 @@ Within a grouping, place the ``view`` and ``pure`` functions last. Yes:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract A { constructor() public { @@ -315,7 +315,7 @@ Yes:: No:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract A { @@ -411,7 +411,7 @@ should: Yes:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Coin { struct Bank { @@ -422,7 +422,7 @@ Yes:: No:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Coin { @@ -723,7 +723,7 @@ manner as modifiers if the function declaration is long or hard to read. Yes:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; // Base contracts just to make this compile contract B { @@ -755,7 +755,7 @@ Yes:: No:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; // Base contracts just to make this compile contract B { @@ -955,7 +955,7 @@ As shown in the example below, if the contract name is `Congress` and the librar Yes:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; // Owned.sol contract Owned { @@ -984,7 +984,7 @@ Yes:: No:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; // owned.sol contract owned { diff --git a/docs/types.rst b/docs/types.rst index ca1bd586..84c448ff 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -51,7 +51,7 @@ Operators: * Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) * Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) * Shift operators: ``<<`` (left shift), ``>>`` (right shift) -* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (remainder), ``**`` (exponentiation) +* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) Comparisons @@ -82,12 +82,25 @@ Addition, Subtraction and Multiplication ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Addition, subtraction and multiplication have the usual semantics. -They wrap in two's complement notation, meaning that +They wrap in two's complement representation, meaning that for example ``uint256(0) - uint256(1) == 2**256 - 1``. You have to take these overflows into account when designing safe smart contracts. -Division and Modulus -^^^^^^^^^^^^^^^^^^^^ +The expression ``-x`` is equivalent to ``(T(0) - x)`` where +``T`` is the type of ``x``. This means that ``-x`` will not be negative +if the type of ``x`` is an unsigned integer type. Also, ``-x`` can be +positive if ``x`` is negative. There is another caveat also resulting +from two's complement representation:: + + int x = -2**255; + assert(-x == x); + +This means that even if a number is negative, you cannot assume that +its negation will be positive. + + +Division +^^^^^^^^ Since the type of the result of an operation is always the type of one of the operands, division on integers always results in an integer. @@ -96,7 +109,23 @@ In Solidity, division rounds towards zero. This mean that ``int256(-5) / int256( Note that in contrast, division on :ref:`literals<rational_literals>` results in fractional values of arbitrary precision. -Division by zero and modulus with zero throws a runtime exception. +.. note:: + Division by zero causes a failing assert. + +Modulo +^^^^^^ + +The modulo operation ``a % n`` yields the remainder ``r`` after the division of the operand ``a`` +by the operand ``n``, where ``q = int(a / n)`` and ``r = a - (n * q)``. This means that modulo +results in the same sign as its left operand (or zero) and ``a % n == -(abs(a) % n)`` holds for negative ``a``: + + * ``int256(5) % int256(2) == int256(1)`` + * ``int256(5) % int256(-2) == int256(1)`` + * ``int256(-5) % int256(2) == int256(-1)`` + * ``int256(-5) % int256(-2) == int256(-1)`` + +.. note:: + Modulo with zero causes a failing assert. Exponentiation ^^^^^^^^^^^^^^ @@ -104,7 +133,8 @@ Exponentiation Exponentiation is only available for unsigned types. Please take care that the types you are using are large enough to hold the result and prepare for potential wrapping behaviour. -Note that ``0**0`` is defined by the EVM as ``1``. +.. note:: + Note that ``0**0`` is defined by the EVM as ``1``. .. index:: ! ufixed, ! fixed, ! fixed point number @@ -122,7 +152,7 @@ the type and ``N`` represents how many decimal points are available. ``M`` must Operators: * Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) -* Arithmetic operators: ``+``, ``-``, unary ``-``, unary ``+``, ``*``, ``/``, ``%`` (remainder) +* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo) .. note:: The main difference between floating point (``float`` and ``double`` in many languages, more precisely IEEE 754 numbers) and fixed point numbers is @@ -159,6 +189,13 @@ has the type ``address payable``, if ``x`` is of integer or fixed bytes type, a If ``x`` is a contract without payable fallback function, then ``address(x)`` will be of type ``address``. In external function signatures ``address`` is used for both the ``address`` and the ``address payable`` type. +.. note:: + It might very well be that you do not need to care about the distinction between ``address`` + and ``address payable`` and just use ``address`` everywhere. For example, + if you are using the :ref:`withdrawal pattern<withdrawal_pattern>`, you can (and should) store the + address itself as ``address``, because you invoke the ``transfer`` function on + ``msg.sender``, which is an ``address payable``. + Operators: * ``<=``, ``<``, ``==``, ``!=``, ``>=`` and ``>`` @@ -425,9 +462,37 @@ a non-rational number). String Literals --------------- -String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. +String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. + +String literals support the following escape characters: + + - ``\<newline>`` (escapes an actual newline) + - ``\\`` (backslash) + - ``\'`` (single quote) + - ``\"`` (double quote) + - ``\b`` (backspace) + - ``\f`` (form feed) + - ``\n`` (newline) + - ``\r`` (carriage return) + - ``\t`` (tab) + - ``\v`` (vertical tab) + - ``\xNN`` (hex escape, see below) + - ``\uNNNN`` (unicode escape, see below) + +``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. + +The string in the following example has a length of ten bytes. +It starts with a newline byte, followed by a double quote, a single +quote a backslash character and then (without separator) the +character sequence ``abcdef``. -String literals support escape characters, such as ``\n``, ``\xNN`` and ``\uNNNN``. ``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. +:: + + "\n\"\'\\abc\ + def" + +Any unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to +terminate the string literal. Newline only terminates the string literal if it is not preceded by a ``\``. .. index:: literal, bytes @@ -446,8 +511,9 @@ Enums ----- Enums are one way to create a user-defined type in Solidity. They are explicitly convertible -to and from all integer types but implicit conversion is not allowed. The explicit conversions -check the value ranges at runtime and a failure causes an exception. Enums needs at least one member. +to and from all integer types but implicit conversion is not allowed. The explicit conversion +from integer checks at runtime that the value lies inside the range of the enum and causes a failing assert otherwise. +Enums needs at least one member. The data representation is the same as for enums in C: The options are represented by subsequent unsigned integer values starting from ``0``. @@ -455,7 +521,7 @@ subsequent unsigned integer values starting from ``0``. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } @@ -515,6 +581,11 @@ omitted. Note that this only applies to function types. Visibility has to be specified explicitly for functions defined in contracts, they do not have a default. +Conversions: + +A value of external function type can be explicitly converted to ``address`` +resulting in the address of the contract of the function. + A function type ``A`` is implicitly convertible to a function type ``B`` if and only if their parameter types are identical, their return types are identical, their internal/external property is identical and the state mutability of ``A`` @@ -524,7 +595,7 @@ is not more restrictive than the state mutability of ``B``. In particular: - ``view`` functions can be converted to ``non-payable`` functions - ``payable`` functions can be converted to ``non-payable`` functions -No other conversions are possible. +No other conversions between function types are possible. The rule about ``payable`` and ``non-payable`` might be a little confusing, but in essence, if a function is ``payable``, this means that it @@ -532,8 +603,8 @@ also accepts a payment of zero Ether, so it also is ``non-payable``. On the other hand, a ``non-payable`` function will reject Ether sent to it, so ``non-payable`` functions cannot be converted to ``payable`` functions. -If a function type variable is not initialized, calling it will result -in an exception. The same happens if you call a function after using ``delete`` +If a function type variable is not initialised, calling it results +in a failed assertion. The same happens if you call a function after using ``delete`` on it. If external function types are used outside of the context of Solidity, @@ -544,10 +615,12 @@ Note that public functions of the current contract can be used both as an internal and as an external function. To use ``f`` as an internal function, just use ``f``, if you want to use its external form, use ``this.f``. -Additionally, public (or external) functions also have a special member called ``selector``, +Members: + +Public (or external) functions also have a special member called ``selector``, which returns the :ref:`ABI function selector <abi_function_selector>`:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract Selector { function f() public pure returns (bytes4) { @@ -557,7 +630,7 @@ which returns the :ref:`ABI function selector <abi_function_selector>`:: Example that shows how to use internal function types:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; library ArrayUtils { // internal functions can be used in internal library functions because @@ -608,7 +681,7 @@ Example that shows how to use internal function types:: Another example that uses external function types:: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract Oracle { struct Request { @@ -680,18 +753,18 @@ non-persistent area where function arguments are stored, and behaves mostly like depending on the kind of variable, function type, etc., but all complex types must now give an explicit data location. -Data locations are important because they change how assignments behave: -assignments between storage and memory and also to a state variable (even from other state variables) -always create an independent copy. -Assignments to local storage variables only assign a reference though, and -this reference always points to the state variable even if the latter is changed -in the meantime. -On the other hand, assignments from a memory stored reference type to another -memory-stored reference type do not create a copy. +Data locations are not only relevant for persistency of data, but also for the semantics of assignments: +assignments between storage and memory (or from calldata) always create an independent copy. +Assignments from memory to memory only create references. This means that changes to one memory variable +are also visible in all other memory variables that refer to the same data. +Assignments from storage to a local storage variables also only assign a reference. +In contrast, all other assignments to storage always copy. Examples for this case +are assignments to state variables or to members of local variables of storage struct type, even +if the local variable itself is just a reference. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint[] x; // the data location of x is storage @@ -717,13 +790,6 @@ memory-stored reference type do not create a copy. function h(uint[] memory) public pure {} } -Summary -^^^^^^^ - -Forced data location: - - parameters (not return) of external functions: calldata - - state variables: storage - .. index:: ! array .. _arrays: @@ -732,9 +798,10 @@ Arrays ------ Arrays can have a compile-time fixed size or they can be dynamic. -For storage arrays, the element type can be arbitrary (i.e. also other -arrays, mappings or structs). For memory arrays, it cannot be a mapping and -has to be an ABI type if it is an argument of a publicly-visible function. +The are few restrictions for the element, it can also be +another array, a mapping or a struct. The general restrictions for +types apply, though, in that mappings can only be used in storage +and publicly-visible functions need parameters that are ABI types. An array of fixed size ``k`` and element type ``T`` is written as ``T[k]``, an array of dynamic size as ``T[]``. As an example, an array of 5 dynamic @@ -744,9 +811,13 @@ third dynamic array, you use ``x[2][1]`` (indices are zero-based and access works in the opposite way of the declaration, i.e. ``x[2]`` shaves off one level in the type from the right). +Accessing an array past its end causes a revert. If you want to add +new elements, you have to use ``.push()`` or increase the ``.length`` +member (see below). + Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, -but it is packed tightly in calldata. ``string`` is equal to ``bytes`` but does not allow -length or index access (for now). +but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow +length or index access. So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper. As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length string (UTF-8) data. If you can limit the length to a certain @@ -766,14 +837,14 @@ The numeric index will become a required parameter for the getter. Allocating Memory Arrays ^^^^^^^^^^^^^^^^^^^^^^^^ -Creating arrays with variable length in memory can be done using the ``new`` keyword. +You can use the ``new`` keyword to create arrays with a runtime-dependent length in memory. As opposed to storage arrays, it is **not** possible to resize memory arrays (e.g. by assigning to the ``.length`` member). You either have to calculate the required size in advance or create a new memory array and copy every element. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f(uint len) public pure { @@ -795,7 +866,7 @@ assigned to a variable right away. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f() public pure { @@ -809,14 +880,14 @@ assigned to a variable right away. The type of an array literal is a memory array of fixed size whose base type is the common type of the given elements. The type of ``[1, 2, 3]`` is ``uint8[3] memory``, because the type of each of these constants is ``uint8``. -Because of that, it was necessary to convert the first element in the example +Because of that, it is necessary to convert the first element in the example above to ``uint``. Note that currently, fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible: :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; // This will not compile. contract C { @@ -836,15 +907,31 @@ Members ^^^^^^^ **length**: - Arrays have a ``length`` member to read their number of elements. - Dynamically-sized arrays (only available for storage) have a read-write ``length`` member to resize the array. Increasing the length adds uninitialized elements to the array, this has *O(1)* complexity. Reducing the length performs :ref:``delete`` on each removed element and has *O(n)* complexity where *n* is the number of elements being deleted. Please note that calling ``length--`` on an empty array will set the length of the array to 2^256-1 due to ``uint256`` underflow wrapping. + Arrays have a ``length`` member that contains their number of elements. + The length of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created. + For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array. + Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion. + Increasing the length adds new zero-initialised elements to the array. + Reducing the length performs an implicit :ref:``delete`` on each of the removed elements. **push**: - Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that can be used to append an element at the end of the array. The function returns the new length. + Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length. **pop**: - Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that can be used to remove an element from the end of the array. This will also implicitly call :ref:``delete`` on the removed element. + Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that you can use to remove an element from the end of the array. This also implicitly calls :ref:``delete`` on the removed element. .. warning:: - It is not yet possible to use arrays of arrays in external functions. + If you use ``.length--`` on an empty array, it causes an underflow and + thus sets the length to ``2**256-1``. + +.. note:: + Increasing the length of a storage array has constant gas costs because + storage is assumed to be zero-initialised, while decreasing + the length has at least linear cost (but in most cases worse than linear), + because it includes explicitly clearing the removed + elements similar to calling :ref:``delete`` on them. + +.. note:: + It is not yet possible to use arrays of arrays in external functions + (but they are supported in public functions). .. note:: In EVM versions before Byzantium, it was not possible to access @@ -854,7 +941,7 @@ Members :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract ArrayContract { uint[2**20] m_aLotOfIntegers; @@ -862,15 +949,34 @@ Members // dynamic array of pairs (i.e. of fixed size arrays of length two). // Because of that, T[] is always a dynamic array of T, even if T // itself is an array. + // Data location for all state variables is storage. bool[2][] m_pairsOfFlags; // newPairs is stored in memory - the only possibility - // for public function arguments + // for public contract function arguments function setAllFlagPairs(bool[2][] memory newPairs) public { - // assignment to a storage array replaces the complete array + // assignment to a storage array performs a copy of ``newPairs`` and + // replaces the complete array ``m_pairsOfFlags``. m_pairsOfFlags = newPairs; } + struct StructType { + uint[] contents; + uint moreInfo; + } + StructType s; + + function f(uint[] memory c) public { + // stores a reference to ``s`` in ``g`` + StructType storage g = s; + // also changes ``s.moreInfo``. + g.moreInfo = 2; + // assigns a copy because ``g.contents`` + // is not a local variable, but a member of + // a local variable. + g.contents = c; + } + function setFlagPair(uint index, bool flagA, bool flagB) public { // access to a non-existing index will throw an exception m_pairsOfFlags[index][0] = flagA; @@ -934,7 +1040,7 @@ shown in the following example: :: - pragma solidity ^0.4.11; + pragma solidity >=0.4.11 <0.6.0; contract CrowdFunding { // Defines a new type with two fields. @@ -956,7 +1062,10 @@ shown in the following example: function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { campaignID = numCampaigns++; // campaignID is return variable - // Creates new struct and saves in storage. We leave out the mapping type. + // Creates new struct in memory and copies it to storage. + // We leave out the mapping type, because it is not valid in memory. + // If structs are copied (even from storage to storage), mapping types + // are always omitted, because they cannot be enumerated. campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); } @@ -986,11 +1095,12 @@ Struct types can be used inside mappings and arrays and they can itself contain mappings and arrays. It is not possible for a struct to contain a member of its own type, -although the struct itself can be the value type of a mapping member. +although the struct itself can be the value type of a mapping member +or it can contain a dynamically-sized array of its type. This restriction is necessary, as the size of the struct has to be finite. Note how in all the functions, a struct type is assigned to a local variable -(of the default storage data location). +with data location ``storage``. This does not copy the struct but only stores a reference so that assignments to members of the local variable actually write to the state. @@ -1001,7 +1111,7 @@ assigning it to a local variable, as in .. index:: !mapping Mappings -======== +-------- You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``. The ``_KeyType`` can be any elementary type. This means it can be any of @@ -1010,7 +1120,7 @@ or complex types like contract types, enums, mappings, structs and any array typ apart from ``bytes`` and ``string`` are not allowed. ``_ValueType`` can be any type, including mappings. -You can think of mappings as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_, which are virtually initialized +You can think of mappings as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_, which are virtually initialised such that every possible key exists and is mapped to a value whose byte-representation is all zeros, a type's :ref:`default value <default-value>`. The similarity ends there, the key data is not stored in a mapping, only its ``keccak256`` hash is used to look up the value. @@ -1018,8 +1128,11 @@ mapping, only its ``keccak256`` hash is used to look up the value. Because of this, mappings do not have a length or a concept of a key or value being set. -Mappings are **only** allowed for state variables (or as storage reference types -in internal functions). +Mappings can only have a data location of ``storage`` and thus +are allowed for state variables, as storage reference types +in functions, or as parameters for library functions. +They cannot be used as parameters or return parameters +of contract functions that are publicly visible. You can mark variables of mapping type as ``public`` and Solidity creates a :ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a @@ -1030,7 +1143,7 @@ each ``_KeyType``, recursively. For example with a mapping: :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract MappingExample { mapping(address => uint) public balances; @@ -1075,7 +1188,7 @@ value it referred to previously. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract DeleteExample { uint data; diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 1e4bbecc..39520bec 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -10,13 +10,17 @@ Using the Commandline Compiler ****************************** .. note:: - This section doesn't apply to :ref:`solcjs <solcjs>`. + This section does not apply to :ref:`solcjs <solcjs>`, not even if it is used in commandline mode. One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler. Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``. -Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. By default, the optimizer will optimize the contract for 200 runs. If you want to optimize for initial contract deployment and get the smallest output, set it to ``--runs=1``. If you expect many transactions and don't care for higher deployment cost and output size, set ``--runs`` to a high number. +Before you deploy your contract, activate the optimizer when compiling using ``solc --optimize --bin sourceFile.sol``. +By default, the optimizer will optimize the contract assuming it is called 200 times across its lifetime. +If you want the initial contract deployment to be cheaper and the later function executions to be more expensive, +set it to ``--runs=1``. If you expect many transactions and do not care for higher deployment cost and +output size, set ``--runs`` to a high number. The commandline compiler will automatically read imported files from the filesystem, but it is also possible to provide path redirects using ``prefix=path`` in the following way: @@ -43,7 +47,7 @@ Either add ``--libraries "Math:0x12345678901234567890 Heap:0xabcdef0123456"`` to If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__LibraryName____``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. -If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. +If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. .. _evm-version: .. index:: ! EVM version, compile target @@ -60,18 +64,23 @@ version to compile for to avoid particular features or behaviours. behaviour. Please ensure, especially if running a private chain, that you use matching EVM versions. -You use the ``--evm-version`` option on the command line: +On the command line, you can select the EVM version as follows: .. code-block:: shell solc --evm-version <VERSION> contract.sol -Or if using the :ref:`standard JSON interface <compiler-api>`, with the ``evmVersion`` key: +In the :ref:`standard JSON interface <compiler-api>`, use the ``"evmVersion"`` +key in the ``"settings"`` field: -.. code-block:: json +.. code-block:: none { - "evmVersion": "<VERSION>" + "sources": { ... }, + "settings": { + "optimizer": { ... }, + "evmVersion": "<VERSION>" + } } Target options @@ -80,7 +89,7 @@ Target options Below is a list of target EVM versions and the compiler-relevant changes introduced at each version. Backward compatibility is not guaranteed between each version. -- ``homestead`` +- ``homestead`` (oldest version) - ``tangerineWhistle`` - gas cost for access to other accounts increased, relevant for gas estimation and the optimizer. - all gas sent by default for external calls, previously a certain amount had to be retained. @@ -88,7 +97,7 @@ at each version. Backward compatibility is not guaranteed between each version. - gas cost for the ``exp`` opcode increased, relevant for gas estimation and the optimizer. - ``byzantium`` (**default**) - opcodes ``returndatacopy``, ``returndatasize`` and ``staticcall`` are available in assembly. - - the ``staticcall`` opcode is used when calling view or pure functions, which prevents the functions from modifying state at the EVM level, i.e., even applies when you use invalid type conversions. + - the ``staticcall`` opcode is used when calling non-library view or pure functions, which prevents the functions from modifying state at the EVM level, i.e., even applies when you use invalid type conversions. - it is possible to access dynamic data returned from function calls. - ``revert`` opcode introduced, which means that ``revert()`` will not waste gas. - ``constantinople`` (still in progress) @@ -100,11 +109,16 @@ at each version. Backward compatibility is not guaranteed between each version. Compiler Input and Output JSON Description ****************************************** -These JSON formats are used by the compiler API as well as are available through ``solc``. These are subject to change, -some fields are optional (as noted), but it is aimed at to only make backwards compatible changes. +The recommended way to interface with the Solidity compiler especially for +more complex and automated setups is the so-called JSON-input-output interface. +The same interface is provided by all distributions of the compiler. + +The fields are generally subject to change, +some are optional (as noted), but we try to only make backwards compatible changes. The compiler API expects a JSON formatted input and outputs the compilation result in a JSON formatted output. +The following subsections describe the format through an example. Comments are of course not permitted and used here only for explanatory purposes. Input Description @@ -113,7 +127,7 @@ Input Description .. code-block:: none { - // Required: Source code language, such as "Solidity", "serpent", "lll", "assembly", etc. + // Required: Source code language, such as "Solidity", "Vyper", "lll", "assembly", etc. language: "Solidity", // Required sources: diff --git a/docs/yul.rst b/docs/yul.rst index e010a708..09aa6b65 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -417,6 +417,12 @@ The following functions must be available: | create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei | | | and return the new address | +---------------------------------------------+-----------------------------------------------------------------+ +| create2(v:u256, n:u256, p:u256, s:u256) | create new contract with code mem[p...(p+s)) at address | +| | keccak256(0xff . self . n . keccak256(mem[p...(p+s))) | +| | and send v wei and return the new address, where ``0xff`` is a | +| | 8 byte value, ``self`` is the current contract's address | +| | as a 20 byte value and ``n`` is a big-endian 256-bit value | ++---------------------------------------------+-----------------------------------------------------------------+ | call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) | | insize:u256, out:u256, | providing g gas and v wei and output area | | outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | @@ -492,6 +498,8 @@ The following functions must be available: +---------------------------------------------+-----------------------------------------------------------------+ | extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a | +---------------------------------------------+-----------------------------------------------------------------+ +| extcodehash(a:u256) | code hash of address a | ++---------------------------------------------+-----------------------------------------------------------------+ | *Others* | +---------------------------------------------+-----------------------------------------------------------------+ | discard(unused:bool) | discard value | diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index e49e675d..63452f36 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -69,6 +69,21 @@ void Assembly::append(Assembly const& _a, int _deposit) append(Instruction::POP); } +AssemblyItem const& Assembly::append(AssemblyItem const& _i) +{ + assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); + m_deposit += _i.deposit(); + m_items.push_back(_i); + if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty()) + m_items.back().setLocation(m_currentSourceLocation); + return back(); +} + +void Assembly::injectStart(AssemblyItem const& _i) +{ + m_items.insert(m_items.begin(), _i); +} + unsigned Assembly::bytesRequired(unsigned subTagSize) const { for (unsigned tagSize = subTagSize; true; ++tagSize) @@ -323,16 +338,6 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const return root; } -AssemblyItem const& Assembly::append(AssemblyItem const& _i) -{ - assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); - m_deposit += _i.deposit(); - m_items.push_back(_i); - if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty()) - m_items.back().setLocation(m_currentSourceLocation); - return back(); -} - AssemblyItem Assembly::namedTag(string const& _name) { assertThrow(!_name.empty(), AssemblyException, "Empty named tag."); @@ -348,11 +353,6 @@ AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) return AssemblyItem(PushLibraryAddress, h); } -void Assembly::injectStart(AssemblyItem const& _i) -{ - m_items.insert(m_items.begin(), _i); -} - Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs) { OptimiserSettings settings; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 1ed9b859..2a29874e 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -52,18 +52,19 @@ public: /// Returns a tag identified by the given name. Creates it if it does not yet exist. AssemblyItem namedTag(std::string const& _name); AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); } + bytes const& data(h256 const& _i) const { return m_data.at(_i); } AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); } Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); } Assembly& sub(size_t _sub) { return *m_subs.at(_sub); } - AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); } AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem newPushLibraryAddress(std::string const& _identifier); - void append(Assembly const& _a); - void append(Assembly const& _a, int _deposit); AssemblyItem const& append(AssemblyItem const& _i); AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); } AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } + + template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; } + /// Pushes the final size of the current assembly itself. Use this when the code is modified /// after compilation and CODESIZE is not an option. void appendProgramSize() { append(AssemblyItem(PushProgramSize)); } @@ -84,12 +85,9 @@ public: /// Appends @a _data literally to the very end of the bytecode. void appendAuxiliaryDataToEnd(bytes const& _data) { m_auxiliaryData += _data; } - template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; } + /// Returns the assembly items. AssemblyItems const& items() const { return m_items; } - AssemblyItem const& back() const { return m_items.back(); } - std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); } - void injectStart(AssemblyItem const& _i); int deposit() const { return m_deposit; } void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); } void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); } @@ -97,9 +95,8 @@ public: /// Changes the source location used for each appended item. void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; } - /// Assembles the assembly into bytecode. The assembly should not be modified after this call. + /// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached. LinkerObject const& assemble() const; - bytes const& data(h256 const& _i) const { return m_data.at(_i); } struct OptimiserSettings { @@ -140,6 +137,18 @@ public: StringMap const& _sourceCodes = StringMap() ) const; +public: + // These features are only used by LLL + AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); } + + void append(Assembly const& _a); + void append(Assembly const& _a, int _deposit); + + void injectStart(AssemblyItem const& _i); + + AssemblyItem const& back() const { return m_items.back(); } + std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); } + protected: /// Does the same operations as @a optimise, but should only be applied to a sub and /// returns the replaced tags. Also takes an argument containing the tags of this assembly diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 3554f809..d98b3efa 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -101,8 +101,8 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ break; case Instruction::KECCAK256: gas = GasCosts::keccak256Gas; - gas += wordGas(GasCosts::keccak256WordGas, m_state->relativeStackElement(-1)); gas += memoryGas(0, -1); + gas += wordGas(GasCosts::keccak256WordGas, m_state->relativeStackElement(-1)); break; case Instruction::CALLDATACOPY: case Instruction::CODECOPY: @@ -114,6 +114,9 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::EXTCODESIZE: gas = GasCosts::extCodeGas(m_evmVersion); break; + case Instruction::EXTCODEHASH: + gas = GasCosts::balanceGas(m_evmVersion); + break; case Instruction::EXTCODECOPY: gas = GasCosts::extCodeGas(m_evmVersion); gas += memoryGas(-1, -3); @@ -125,8 +128,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::LOG3: case Instruction::LOG4: { - unsigned n = unsigned(_item.instruction()) - unsigned(Instruction::LOG0); - gas = GasCosts::logGas + GasCosts::logTopicGas * n; + gas = GasCosts::logGas + GasCosts::logTopicGas * getLogNumber(_item.instruction()); gas += memoryGas(0, -1); if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) gas += GasCosts::logDataGas * (*value); @@ -215,7 +217,7 @@ GasMeter::GasConsumption GasMeter::memoryGas(ExpressionClasses::Id _position) if (!value) return GasConsumption::infinite(); if (*value < m_largestMemoryAccess) - return GasConsumption(u256(0)); + return GasConsumption(0); u256 previous = m_largestMemoryAccess; m_largestMemoryAccess = *value; auto memGas = [=](u256 const& pos) -> u256 diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index 7cb8015f..da90b028 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -137,6 +137,8 @@ public: static unsigned runGas(Instruction _instruction); /// @returns the gas cost of the supplied data, depending whether it is in creation code, or not. + /// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas + /// otherwise code will be stored and have to pay "createDataGas" cost. static u256 dataGas(bytes const& _data, bool _inCreation); private: diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index d5b82e75..cf98c938 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -73,6 +73,7 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions = { "EXTCODECOPY", Instruction::EXTCODECOPY }, { "RETURNDATASIZE", Instruction::RETURNDATASIZE }, { "RETURNDATACOPY", Instruction::RETURNDATACOPY }, + { "EXTCODEHASH", Instruction::EXTCODEHASH }, { "BLOCKHASH", Instruction::BLOCKHASH }, { "COINBASE", Instruction::COINBASE }, { "TIMESTAMP", Instruction::TIMESTAMP }, @@ -216,6 +217,7 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo = { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } }, { Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } }, { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, + { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Balance } }, { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } }, { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } }, { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index e2e2b63e..63424eeb 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -82,6 +82,7 @@ enum class Instruction: uint8_t EXTCODECOPY, ///< copy external code (from another contract) RETURNDATASIZE = 0x3d, ///< get size of return data buffer RETURNDATACOPY = 0x3e, ///< copy return data in current environment to memory + EXTCODEHASH = 0x3f, ///< get external code hash (from another contract) BLOCKHASH = 0x40, ///< get hash of most recent complete block COINBASE, ///< get the block's coinbase address @@ -218,6 +219,12 @@ inline bool isSwapInstruction(Instruction _inst) return Instruction::SWAP1 <= _inst && _inst <= Instruction::SWAP16; } +/// @returns true if the instruction is a LOG +inline bool isLogInstruction(Instruction _inst) +{ + return Instruction::LOG0 <= _inst && _inst <= Instruction::LOG4; +} + /// @returns the number of PUSH Instruction _inst inline unsigned getPushNumber(Instruction _inst) { @@ -236,6 +243,12 @@ inline unsigned getSwapNumber(Instruction _inst) return (byte)_inst - unsigned(Instruction::SWAP1) + 1; } +/// @returns the number of LOG Instruction _inst +inline unsigned getLogNumber(Instruction _inst) +{ + return (byte)_inst - unsigned(Instruction::LOG0); +} + /// @returns the PUSH<_number> instruction inline Instruction pushInstruction(unsigned _number) { diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h index 9537b176..fb821684 100644 --- a/libevmasm/PathGasMeter.h +++ b/libevmasm/PathGasMeter.h @@ -57,6 +57,16 @@ public: GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state); + static GasMeter::GasConsumption estimateMax( + AssemblyItems const& _items, + solidity::EVMVersion _evmVersion, + size_t _startIndex, + std::shared_ptr<KnownState> const& _state + ) + { + return PathGasMeter(_items, _evmVersion).estimateMax(_startIndex, _state); + } + private: /// Adds a new path item to the queue, but only if we do not already have /// a higher gas usage at that point. diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 71267ee8..78f3c9c7 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -151,6 +151,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item) case Instruction::MSIZE: // depends on previous writes and reads, not only on content case Instruction::BALANCE: // depends on previous calls case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: case Instruction::RETURNDATACOPY: // depends on previous calls case Instruction::RETURNDATASIZE: return false; @@ -172,6 +173,7 @@ bool SemanticInformation::movable(Instruction _instruction) case Instruction::KECCAK256: case Instruction::BALANCE: case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: case Instruction::RETURNDATASIZE: case Instruction::SLOAD: case Instruction::PC: @@ -233,6 +235,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::GASPRICE: case Instruction::EXTCODESIZE: case Instruction::EXTCODECOPY: + case Instruction::EXTCODEHASH: case Instruction::BLOCKHASH: case Instruction::COINBASE: case Instruction::TIMESTAMP: diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index f4e49655..dc536f77 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -545,11 +545,13 @@ void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const void CodeTransform::checkStackHeight(void const* _astElement) const { solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); + int stackHeightInAnalysis = m_info.stackHeightInfo.at(_astElement); + int stackHeightInCodegen = m_assembly.stackHeight() - m_stackAdjustment; solAssert( - m_info.stackHeightInfo.at(_astElement) == m_assembly.stackHeight() - m_stackAdjustment, + stackHeightInAnalysis == stackHeightInCodegen, "Stack height mismatch between analysis and code generation phase: Analysis: " + - to_string(m_info.stackHeightInfo.at(_astElement)) + + to_string(stackHeightInAnalysis) + " code gen: " + - to_string(m_assembly.stackHeight() - m_stackAdjustment) + to_string(stackHeightInCodegen) ); } diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 8a576e2e..81de3c43 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -246,6 +246,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric.get()); if (!lengthType || !lengthType->mobileType()) fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression."); + else if (lengthType->isZero()) + fatalTypeError(length->location(), "Array with zero length specified."); else if (lengthType->isFractional()) fatalTypeError(length->location(), "Array with fractional length specified."); else if (lengthType->isNegative()) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c42a0068..3d119c82 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2039,7 +2039,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (!contract) m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); if (contract->contractKind() == ContractDefinition::ContractKind::Interface) - m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); + m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); if (!contract->annotation().unimplementedFunctions.empty()) { SecondarySourceLocation ssl; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c97ee657..fd72bf41 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1276,13 +1276,13 @@ u256 RationalNumberType::literalValue(Literal const*) const else { auto fixed = fixedPointType(); - solAssert(fixed, ""); + solAssert(fixed, "Rational number cannot be represented as fixed point type."); int fractionalDigits = fixed->fractionalDigits(); shiftedValue = m_value.numerator() * boost::multiprecision::pow(bigint(10), fractionalDigits) / m_value.denominator(); } // we ignore the literal and hope that the type was correctly determined - solAssert(shiftedValue <= u256(-1), "Integer constant too large."); + solAssert(shiftedValue <= u256(-1), "Number constant too large."); solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small."); if (m_value >= rational(0)) diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 9a0110cf..947b6d05 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -565,8 +565,10 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio // We assume that returndatacopy, returndatasize and staticcall are either all available // or all not available. solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); + // Similarly we assume bitwise shifting and create2 go together. + solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); - if (_instr == solidity::Instruction::CREATE2) + if (_instr == solidity::Instruction::EXTCODEHASH) m_errorReporter.warning( _location, "The \"" + @@ -593,7 +595,8 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio else if (( _instr == solidity::Instruction::SHL || _instr == solidity::Instruction::SHR || - _instr == solidity::Instruction::SAR + _instr == solidity::Instruction::SAR || + _instr == solidity::Instruction::CREATE2 ) && !m_evmVersion.hasBitwiseShifting()) m_errorReporter.warning( _location, diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index b68e1f4e..657727ac 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -75,6 +75,7 @@ public: bool supportsReturndata() const { return *this >= byzantium(); } bool hasStaticCall() const { return *this >= byzantium(); } bool hasBitwiseShifting() const { return *this >= constantinople(); } + bool hasCreate2() const { return *this >= constantinople(); } /// Whether we have to retain the costs for the call opcode itself (false), /// or whether we can just forward easily all remaining gas (true). diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index a532f86e..e70e23a2 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -160,8 +160,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( ); } - PathGasMeter meter(_items, m_evmVersion); - return meter.estimateMax(0, state); + return PathGasMeter::estimateMax(_items, m_evmVersion, 0, state); } GasEstimator::GasConsumption GasEstimator::functionalEstimation( @@ -183,7 +182,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( if (parametersSize > 0) state->feedItem(swapInstruction(parametersSize)); - return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state); + return PathGasMeter::estimateMax(_items, m_evmVersion, _offset, state); } set<ASTNode const*> GasEstimator::finestNodesAtLocation( diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index e7b1553f..2305da13 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -342,10 +342,25 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) } m_compilerStack.setRemappings(remappings); - Json::Value optimizerSettings = settings.get("optimizer", Json::Value()); - bool const optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool(); - unsigned const optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt(); - m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); + if (settings.isMember("optimizer")) + { + Json::Value optimizerSettings = settings["optimizer"]; + if (optimizerSettings.isMember("enabled")) + { + if (!optimizerSettings["enabled"].isBool()) + return formatFatalError("JSONError", "The \"enabled\" setting must be a boolean."); + + bool const optimize = optimizerSettings["enabled"].asBool(); + unsigned optimizeRuns = 200; + if (optimizerSettings.isMember("runs")) + { + if (!optimizerSettings["runs"].isUInt()) + return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number."); + optimizeRuns = optimizerSettings["runs"].asUInt(); + } + m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); + } + } map<string, h160> libraries; Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue)); diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 96501499..b107f7c9 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -87,9 +87,12 @@ case $(uname -s) in 10.13) echo "Installing solidity dependencies on macOS 10.13 High Sierra." ;; + 10.14) + echo "Installing solidity dependencies on macOS 10.14 Mojave." + ;; *) echo "Unsupported macOS version." - echo "We only support Mavericks, Yosemite, El Capitan, Sierra and High Sierra." + echo "We only support Mavericks, Yosemite, El Capitan, Sierra, High Sierra and Mojave." exit 1 ;; esac diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index f7577cb3..78e45429 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -177,6 +177,8 @@ do then echo " - $dir" cd "$dir" + # Replace version pragmas + find . -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity [\^0-9\.]*/pragma solidity >=0.0/' compileFull -w *.sol */*.sol cd .. fi diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 70503605..eb274c09 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -40,7 +40,7 @@ namespace { static char const* registrarCode = R"DELIMITER( -pragma solidity ^0.4.0; +pragma solidity >=0.4.0 <0.6.0; contract NameRegister { function addr(string memory _name) public view returns (address o_owner); diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index ae921a96..e82f389f 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -53,7 +53,7 @@ static char const* registrarCode = R"DELIMITER( // @authors: // Gav Wood <g@ethdev.com> -pragma solidity ^0.4.0; +pragma solidity >=0.4.0 <0.6.0; contract Registrar { event Changed(string indexed name); diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index e0e3045c..9fe02e46 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -56,7 +56,7 @@ static char const* walletCode = R"DELIMITER( // some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the // interior is executed. -pragma solidity ^0.4.0; +pragma solidity >=0.4.0 <0.6.0; contract multiowned { diff --git a/test/externalTests.sh b/test/externalTests.sh index f2839083..0168fb03 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -56,10 +56,10 @@ function test_truffle echo "Current commit hash: `git rev-parse HEAD`" npm install find . -name soljson.js -exec cp "$SOLJSON" {} \; - if [ "$name" == "Gnosis" ]; then + if [ "$name" == "Zeppelin" -o "$name" == "Gnosis" ]; then echo "Replaced fixed-version pragmas..." # Replace fixed-version pragmas in Gnosis (part of Consensys best practice) - find contracts test -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity 0/pragma solidity ^0/' + find contracts test -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity [\^0-9\.]*/pragma solidity >=0.0/' fi assertsol="node_modules/truffle/build/Assert.sol" if [ -f "$assertsol" ] diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp index be798dc8..a4394f54 100644 --- a/test/liblll/Compiler.cpp +++ b/test/liblll/Compiler.cpp @@ -186,6 +186,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_functional) "60006000600060003c", "3d", "6000600060003e", + "60003f", "600040", "41", "42", @@ -291,6 +292,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_functional) "{ (EXTCODECOPY 0 0 0 0) }", "{ (RETURNDATASIZE) }", "{ (RETURNDATACOPY 0 0 0) }", + "{ (EXTCODEHASH 0) }", "{ (BLOCKHASH 0) }", "{ (COINBASE) }", "{ (TIMESTAMP) }", @@ -409,6 +411,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_asm) "3c", "3d", "3e", + "3f", "40", "41", "42", @@ -547,6 +550,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_asm) "{ (asm EXTCODECOPY) }", "{ (asm RETURNDATASIZE) }", "{ (asm RETURNDATACOPY) }", + "{ (asm EXTCODEHASH) }", "{ (asm BLOCKHASH) }", "{ (asm COINBASE) }", "{ (asm TIMESTAMP) }", diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index af8465fc..640bf4d0 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -396,7 +396,7 @@ BOOST_AUTO_TEST_CASE(returndatasize_as_variable) {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} }); if (!dev::test::Options::get().evmVersion().supportsReturndata()) - expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible"))); + expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible VMs."))); CHECK_ALLOW_MULTI(text, expectations); } @@ -407,9 +407,24 @@ BOOST_AUTO_TEST_CASE(create2_as_variable) )"; // This needs special treatment, because the message mentions the EVM version, // so cannot be run via isoltest. + vector<pair<Error::Type, std::string>> expectations(vector<pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} + }); + if (!dev::test::Options::get().evmVersion().hasCreate2()) + expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"create2\" instruction is only available for Constantinople-compatible VMs."))); + CHECK_ALLOW_MULTI(text, expectations); +} + +BOOST_AUTO_TEST_CASE(extcodehash_as_variable) +{ + char const* text = R"( + contract c { function f() public view { uint extcodehash; extcodehash; assembly { pop(extcodehash(0)) } }} + )"; + // This needs special treatment, because the message mentions the EVM version, + // so cannot be run via isoltest. CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, - {Error::Type::Warning, "The \"create2\" instruction is not supported by the VM version"}, + {Error::Type::Warning, "The \"extcodehash\" instruction is not supported by the VM version"}, })); } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index d34bacda..1570a9d2 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -225,6 +225,71 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_CHECK(containsAtMostWarnings(result)); } +BOOST_AUTO_TEST_CASE(optimizer_enabled_not_boolean) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "optimizer": { + "enabled": "wrong" + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "The \"enabled\" setting must be a boolean.")); +} + +BOOST_AUTO_TEST_CASE(optimizer_runs_not_a_number) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "optimizer": { + "enabled": true, + "runs": "not a number" + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "The \"runs\" setting must be an unsigned number.")); +} + +BOOST_AUTO_TEST_CASE(optimizer_runs_not_an_unsigned_number) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "optimizer": { + "enabled": true, + "runs": -1 + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "The \"runs\" setting must be an unsigned number.")); +} + BOOST_AUTO_TEST_CASE(basic_compilation) { char const* input = R"( diff --git a/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol b/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol new file mode 100644 index 00000000..fd8f3078 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol @@ -0,0 +1,15 @@ +contract C { + function a() public pure returns(int[0][500] memory) {} + function b() public pure returns(uint[0][500] memory) {} + function c() public pure returns(byte[0][500] memory) {} + function d() public pure returns(bytes32[0][500] memory) {} + function e() public pure returns(bytes[0][500] memory) {} + function e() public pure returns(string[0][500] memory) {} +} +// ---- +// TypeError: (52-53): Array with zero length specified. +// TypeError: (111-112): Array with zero length specified. +// TypeError: (170-171): Array with zero length specified. +// TypeError: (232-233): Array with zero length specified. +// TypeError: (292-293): Array with zero length specified. +// TypeError: (353-354): Array with zero length specified. diff --git a/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol b/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol new file mode 100644 index 00000000..b38939e3 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol @@ -0,0 +1,15 @@ +contract C { + int[0] a; + uint[0] b; + byte[0] c; + bytes32[0] d; + bytes[0] e; + string[0] f; +} +// ---- +// TypeError: (19-20): Array with zero length specified. +// TypeError: (32-33): Array with zero length specified. +// TypeError: (45-46): Array with zero length specified. +// TypeError: (61-62): Array with zero length specified. +// TypeError: (75-76): Array with zero length specified. +// TypeError: (90-91): Array with zero length specified. diff --git a/test/libsolidity/syntaxTests/array/length/parentheses.sol b/test/libsolidity/syntaxTests/array/length/parentheses.sol index 40f55ad6..8dbcc0a4 100644 --- a/test/libsolidity/syntaxTests/array/length/parentheses.sol +++ b/test/libsolidity/syntaxTests/array/length/parentheses.sol @@ -21,5 +21,5 @@ contract C { uint[((2) + 1) + 1] a12; uint[(2 + 1) + ((1))] a13; uint[(((2) + 1)) + (((1)))] a14; - uint[((((2) + 1)) + (((1))))%1] a15; + uint[((((3) + 1)) + (((1))))%2] a15; } diff --git a/test/libsolidity/syntaxTests/parsing/arrays_in_storage.sol b/test/libsolidity/syntaxTests/parsing/arrays_in_storage.sol index 9181222e..9adf6f12 100644 --- a/test/libsolidity/syntaxTests/parsing/arrays_in_storage.sol +++ b/test/libsolidity/syntaxTests/parsing/arrays_in_storage.sol @@ -1,6 +1,6 @@ contract c { uint[10] a; uint[] a2; - struct x { uint[2**20] b; y[0] c; } + struct x { uint[2**20] b; y[1] c; } struct y { uint d; mapping(uint=>x)[] e; } } |