diff options
author | chriseth <c@ethdev.com> | 2016-10-25 21:31:36 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2016-10-25 21:32:37 +0800 |
commit | 2353da71c77dd235b35d16e7e024fa62408df610 (patch) | |
tree | 756604eaddf853a77c1fb04248f2305e1a33739a | |
parent | af6afb0415761b53721f89c7f65064807f41cbd3 (diff) | |
parent | e3761bdf928e6a06e6620bc1b570d44264d24734 (diff) | |
download | dexon-solidity-2353da71c77dd235b35d16e7e024fa62408df610.tar.gz dexon-solidity-2353da71c77dd235b35d16e7e024fa62408df610.tar.zst dexon-solidity-2353da71c77dd235b35d16e7e024fa62408df610.zip |
Merge remote-tracking branch 'origin/develop' into release
69 files changed, 1204 insertions, 876 deletions
diff --git a/.travis.yml b/.travis.yml index da9bd2f9..1f35a509 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,10 +84,11 @@ matrix: # OS X Mavericks (10.9) # https://en.wikipedia.org/wiki/OS_X_Mavericks # - - os: osx - osx_image: beta-xcode6.2 - env: - - ZIP_SUFFIX=osx-mavericks +# Disabled because of problems on travis. +# - os: osx +# osx_image: beta-xcode6.2 +# env: +# - ZIP_SUFFIX=osx-mavericks # OS X Yosemite (10.10) # https://en.wikipedia.org/wiki/OS_X_Yosemite @@ -186,7 +187,9 @@ deploy: script: test $TRAVIS_EMSCRIPTEN != On || scripts/release_emscripten.sh skip_cleanup: true on: - branch: develop + branch: + - develop + - release # This is the deploy target for the native build (Linux and macOS) # which generates ZIPs per commit. We are in agreement that diff --git a/CMakeLists.txt b/CMakeLists.txt index f190a507..62440265 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.2") +set(PROJECT_VERSION "0.4.3") project(solidity VERSION ${PROJECT_VERSION}) # Let's find our dependencies diff --git a/Changelog.md b/Changelog.md index b5c0631d..b399c71a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,30 @@ +### 0.4.3 (2016-10-25) + +Features: + + * Inline assembly: support both ``suicide`` and ``selfdestruct`` opcodes + (note: ``suicide`` is deprecated). + * Inline assembly: issue warning if stack is not balanced after block. + * Include ``keccak256()`` as an alias to ``sha3()``. + * Support shifting constant numbers. + +Bugfixes: + * Commandline interface: Disallow unknown options in ``solc``. + * Name resolver: Allow inheritance of ``enum`` definitions. + * Type checker: Proper type checking for bound functions. + * Type checker: fixed crash related to invalid fixed point constants + * Type checker: fixed crash related to invalid literal numbers. + * Type checker: ``super.x`` does not look up ``x`` in the current contract. + * Code generator: expect zero stack increase after ``super`` as an expression. + * Code generator: fix an internal compiler error for ``L.Foo`` for ``enum Foo`` defined in library ``L``. + * Code generator: allow inheritance of ``enum`` definitions. + * Inline assembly: support the ``address`` opcode. + * Inline assembly: fix parsing of assignment after a label. + * Inline assembly: external variables of unsupported type (such as ``this``, ``super``, etc.) + are properly detected as unusable. + * Inline assembly: support variables within modifiers. + * Optimizer: fix related to stale knowledge about SHA3 operations + ### 0.4.2 (2016-09-17) Bugfixes: diff --git a/appveyor.yml b/appveyor.yml index afc2914f..b45e9f11 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,6 +27,10 @@ # (c) 2016 cpp-ethereum contributors. #------------------------------------------------------------------------------ +branches: + only: + - master + - develop os: Visual Studio 2015 configuration: - RelWithDebInfo diff --git a/docs/conf.py b/docs/conf.py index 485184f2..4d22c9bd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -56,9 +56,9 @@ copyright = '2016, Ethereum' # built documents. # # The short X.Y version. -version = '0.4.1' +version = '0.4.3' # The full version, including alpha/beta/rc tags. -release = '0.4.1-develop' +release = '0.4.3-develop' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/contracts.rst b/docs/contracts.rst index ef29a686..7f8ace44 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -20,6 +20,9 @@ Contracts can be created "from outside" or from Solidity contracts. When a contract is created, its constructor (a function with the same name as the contract) is executed once. +A constructor is optional. Only one constructor is allowed and this means +overloading is not supported. + From ``web3.js``, i.e. the JavaScript API, this is done as follows:: @@ -136,7 +139,7 @@ This means that cyclic creation dependencies are impossible. ) returns (bool ok) { // Check some arbitrary condition. address tokenAddress = msg.sender; - return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); + return (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); } } @@ -421,9 +424,9 @@ change by overriding). .. index:: ! constant -********** -Constants -********** +************************ +Constant State Variables +************************ State variables can be declared as constant (this is not yet implemented for array and struct types and not possible for mapping types). @@ -442,6 +445,27 @@ for these variables and every occurrence is replaced by their constant value. The value expression can only contain integer arithmetics. +****************** +Constant Functions +****************** + +Functions can be declared constant. These functions promise not to modify the state. + +:: + + pragma solidity ^0.4.0; + + contract C { + function f(uint a, uint b) constant returns (uint) { + return a * (b + 42); + } + } + +.. note:: + Accessor methods are marked constant. + +.. warning:: + The compiler does not enforce yet that a constant method is not modifying state. .. index:: ! fallback function, function;fallback @@ -544,7 +568,7 @@ to be searched for: It is possible to filter for specific values of indexed arguments in the user interface. If arrays (including ``string`` and ``bytes``) are used as indexed arguments, the -sha3-hash of it is stored as topic instead. +Keccak-256 hash of it is stored as topic instead. The hash of the signature of the event is one of the topics except if you declared the event with ``anonymous`` specifier. This means that it is @@ -622,7 +646,7 @@ as topics. The event call above can be performed in the same way as ); where the long hexadecimal number is equal to -``sha3("Deposit(address,hash256,uint256)")``, the signature of the event. +``keccak256("Deposit(address,hash256,uint256)")``, the signature of the event. Additional Resources for Understanding Events ============================================== @@ -976,7 +1000,7 @@ are all compiled as calls (``DELEGATECALL``) to an external contract/library. If you use libraries, take care that an actual external function call is performed. ``msg.sender``, ``msg.value`` and ``this`` will retain their values -in this call, though (prior to Homestead, ``msg.sender`` and +in this call, though (prior to Homestead, because of the use of `CALLCODE`, ``msg.sender`` and ``msg.value`` changed, though). The following example shows how to use memory types and diff --git a/docs/contributing.rst b/docs/contributing.rst index a316abd6..111fb932 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -15,7 +15,9 @@ In particular, we need help in the following areas: <http://ethereum.stackexchange.com/>`_ and the `Solidity Gitter <https://gitter.im/ethereum/solidity>`_ * Fixing and responding to `Solidity's GitHub issues - <https://github.com/ethereum/solidity/issues>`_ + <https://github.com/ethereum/solidity/issues>`_, especially those tagged as + `up-for-grabs <https://github.com/ethereum/solidity/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs>`_ which are + meant as introductory issues for external contributors. How to Report Issues ==================== diff --git a/docs/control-structures.rst b/docs/control-structures.rst index db24d5c3..597829d3 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -7,7 +7,7 @@ Expressions and Control Structures Control Structures =================== -Most of the control structures from C/JavaScript are available in Solidity +Most of the control structures from C or JavaScript are available in Solidity except for ``switch`` and ``goto``. So there is: ``if``, ``else``, ``while``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with the usual semantics known from C or JavaScript. @@ -322,14 +322,16 @@ In the following example, we show how ``throw`` can be used to easily revert an } } -Currently, there are six situations, where exceptions happen automatically in Solidity: +Currently, there are situations, where exceptions happen automatically in Solidity: -1. If you access an array beyond its length (i.e. ``x[i]`` where ``i >= x.length``). -2. If a function called via a message call does not finish properly (i.e. it runs out of gas or throws an exception itself). -3. If a non-existent function on a library is called or Ether is sent to a library. -4. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). -5. If you perform an external function call targeting a contract that contains no code. -6. If a contract-creation call using the ``new`` keyword fails. +1. If you access an array at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``). +2. If you access a fixed-length ``bytesN`` at a too large or negative index. +3. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. +4. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly"). +5. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). +6. If you perform an external function call targeting a contract that contains no code. +7. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). +8. If your contract receives Ether via a public accessor function. Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. @@ -503,7 +505,7 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. +-------------------------+------+-----------------------------------------------------------------+ | pc | | current position in code | +-------------------------+------+-----------------------------------------------------------------+ -| pop | `*` | remove topmost stack slot | +| pop(x) | `-` | remove the element pushed by x | +-------------------------+------+-----------------------------------------------------------------+ | dup1 ... dup16 | | copy ith stack slot to the top (counting from top) | +-------------------------+------+-----------------------------------------------------------------+ @@ -559,9 +561,9 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. | delegatecall(g, a, in, | | identical to `callcode` but also keep ``caller`` | | insize, out, outsize) | | and ``callvalue`` | +-------------------------+------+-----------------------------------------------------------------+ -| return(p, s) | `*` | end execution, return data mem[p..(p+s)) | +| return(p, s) | `-` | end execution, return data mem[p..(p+s)) | +-------------------------+------+-----------------------------------------------------------------+ -| selfdestruct(a) | `*` | end execution, destroy current contract and send funds to a | +| selfdestruct(a) | `-` | end execution, destroy current contract and send funds to a | +-------------------------+------+-----------------------------------------------------------------+ | log0(p, s) | `-` | log without topics and data mem[p..(p+s)) | +-------------------------+------+-----------------------------------------------------------------+ @@ -783,7 +785,7 @@ Conventions in Solidity In contrast to EVM assembly, Solidity knows types which are narrower than 256 bits, e.g. ``uint24``. In order to make them more efficient, most arithmetic operations just -treat them as 256 bit numbers and the higher-order bits are only cleaned at the +treat them as 256-bit numbers and the higher-order bits are only cleaned at the point where it is necessary, i.e. just shortly before they are written to memory or before comparisons are performed. This means that if you access such a variable from within inline assembly, you might have to manually clean the higher order bits diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index acc0c106..43fba332 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -9,33 +9,12 @@ This list was originally compiled by `fivedogit <mailto:fivedogit@gmail.com>`_. Basic Questions *************** -What is Solidity? +Example contracts ================= -Solidity is the DEV-created (i.e. Ethereum Foundation-created), -Javascript-inspired language that can be used to create smart contracts -on the Ethereum blockchain. There are other -languages you can use as well (LLL, Serpent, etc). The main points in -favour of Solidity is that it is statically typed and offers many -advanced features like inheritance, libraries, complex -user-defined types and a bytecode optimizer. - -Solidity contracts can be compiled a few different ways (see below) and the -resulting output can be cut/pasted into a geth console to deploy them to the -Ethereum blockchain. - There are some `contract examples <https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts/>`_ by fivedogit and there should be a `test contract <https://github.com/ethereum/solidity/blob/develop/test/libsolidity/SolidityEndToEndTest.cpp>`_ for every single feature of Solidity. -How do I compile contracts? -=========================== - -Probably the fastest way is the `online compiler <https://ethereum.github.io/browser-solidity/>`_. - -You can also use the ``solc`` binary which comes with cpp-ethereum to compile -contracts or an emerging option is to use Mix, the IDE. - - Create and publish the most basic contract possible =================================================== @@ -71,13 +50,6 @@ several blockchain explorers. Contracts on the blockchain should have their original source code published if they are to be used by third parties. -Does ``selfdestruct()`` free up space in the blockchain? -======================================================== - -It removes the contract bytecode and storage from the current block -into the future, but since the blockchain stores every single block (i.e. -all history), this will not actually free up space on full/archive nodes. - Create a contract that can be killed and return funds ===================================================== @@ -113,32 +85,6 @@ Use a non-constant function (req ``sendTransaction``) to increment a variable in See `value_incrementer.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/20_value_incrementer.sol>`_. -Get contract address in Solidity -================================ - -Short answer: The global variable ``this`` is the contract address. - -See `basic_info_getter <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/15_basic_info_getter.sol>`_. - -Long answer: ``this`` is a variable representing the current contract. -Its type is the type of the contract. Since any contract type basically inherits from the -``address`` type, ``this`` is always convertible to ``address`` and in this case contains -its own address. - -What is the difference between a function marked ``constant`` and one that is not? -================================================================================== - -``constant`` functions can perform some action and return a value, but cannot -change state (this is not yet enforced by the compiler). In other words, a -constant function cannot save or update any variables within the contract or wider -blockchain. These functions are called using ``c.someFunction(...)`` from -geth or any other web3.js environment. - -"non-constant" functions (those lacking the ``constant`` specifier) must be called -with ``c.someMethod.sendTransaction({from:eth.accounts[x], gas: 1000000});`` -That is, because they can change state, they have to have a gas -payment sent along to get the work done. - Get a contract to return its funds to you (not using ``selfdestruct(...)``). ============================================================================ @@ -146,52 +92,6 @@ This example demonstrates how to send funds from a contract to an address. See `endowment_retriever <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/30_endowment_retriever.sol>`_. -What is a ``mapping`` and how do we use them? -============================================= - -A mapping is very similar to a K->V hashmap. -If you have a state variable of type ``mapping (string -> uint) x;``, then you can -access the value by ``x["somekeystring"]``. - -How can I get the length of a ``mapping``? -========================================== - -Mappings are a rather low-level data structure. It does not store the keys -and it is not possible to know which or how many values are "set". Actually, -all values to all possible keys are set by default, they are just -initialised with the zero value. - -In this sense, the attribute ``length`` for a mapping does not really apply. - -If you want to have a "sized mapping", you can use the iterable mapping -(see below) or just a dynamically-sized array of structs. - -Are ``mapping``'s iterable? -=========================== - -Mappings themselves are not iterable, but you can use a higher-level -datastructure on top of it, for example the `iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_. - -Can I put arrays inside of a ``mapping``? How do I make a ``mapping`` of a ``mapping``? -======================================================================================= - -Mappings are already syntactically similar to arrays as they are, therefore it doesn't make much sense to store an array in them. Rather what you should do is create a mapping of a mapping. - -An example of this would be:: - - contract C { - struct myStruct { - uint someNumber; - string someString; - } - - mapping(uint => mapping(string => myStruct)) myDynamicMapping; - - function storeInMapping() { - myDynamicMapping[1]["Foo"] = myStruct(2, "Bar"); - } - } - Can you return an array or a ``string`` from a solidity function call? ====================================================================== @@ -223,61 +123,6 @@ Example:: } } -What are ``event``'s and why do we need them? -============================================= - -Let us suppose that you need a contract to alert the outside world when -something happens. The contract can fire an event, which can be listened to -from web3 (inside geth or a web application). The main advantage of events -is that they are stored in a special way on the blockchain so that it -is very easy to search for them. - -What are the different function visibilities? -============================================= - -The visibility specifiers do not only change the visibility but also -the way functions can be called. In general, functions in the -same contract can also be called internally (which is cheaper -and allows for memory types to be passed by reference). This -is done if you just use ``f(1,2)``. If you use ``this.f(1,2)`` -or ``otherContract.f(1,2)``, the function is called externally. - -Internal function calls have the advantage that you can use -all Solidity types as parameters, but you have to stick to the -simpler ABI types for external calls. - -* ``external``: all, only externally - -* ``public``: all (this is the default), externally and internally - -* ``internal``: only this contract and contracts deriving from it, only internally - -* ``private``: only this contract, only internally - - -Do contract constructors have to be publicly visible? -===================================================== - -You can use the visibility specifiers, but they do not yet have any effect. -The constructor is removed from the contract code once it is deployed, - -Can a contract have multiple constructors? -========================================== - -No, a contract can have only one constructor. - -More specifically, it can only have one function whose name matches -that of the constructor. - -Having multiple constructors with different number of arguments -or argument types, as it is possible in other languages -is not allowed in Solidity. - -Is a constructor required? -========================== - -No. If there is no constructor, a generic one without arguments and no actions will be used. - Are timestamps (``now,`` ``block.timestamp``) reliable? ======================================================= @@ -363,14 +208,6 @@ Examples:: C c = new C(); } -What is the ``modifier`` keyword? -================================= - -Modifiers are a way to prepend or append code to a function in order -to add guards, initialisation or cleanup functionality in a concise way. - -For examples, see the `features.sol <https://github.com/ethereum/dapp-bin/blob/master/library/features.sol>`_. - How do structs work? ==================== @@ -590,12 +427,6 @@ The correct way to do this is the following:: } } -Can a regular (i.e. non-contract) ethereum account be closed permanently like a contract can? -============================================================================================= - -No. Non-contract accounts "exist" as long as the private key is known by -someone or can be generated in some way. - What is the difference between ``bytes`` and ``byte[]``? ======================================================== @@ -641,16 +472,6 @@ Use the constructor. Anything inside it will be executed when the contract is fi See `replicator.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/50_replicator.sol>`_. -Can a contract create another contract? -======================================= - -Yes, see `replicator.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/50_replicator.sol>`_. - -Note that the full code of the created contract has to be included in the creator contract. -This also means that cyclic creations are not possible (because the contract would have -to contain its own code) - at least not in a general way. - - How do you create 2-dimensional arrays? ======================================= @@ -709,10 +530,12 @@ How do I initialize a contract with only a specific amount of wei? Currently the approach is a little ugly, but there is little that can be done to improve it. In the case of a ``contract A`` calling a new instance of ``contract B``, parentheses have to be used around ``new B`` because ``B.value`` would refer to a member of ``B`` called ``value``. -You will need to make sure that you have both contracts aware of each other's presence. +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:: - contract B {} + contract B { + function B() payable {} + } contract A { diff --git a/docs/index.rst b/docs/index.rst index 4cf75282..3b47ce78 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,8 +1,12 @@ Solidity ======== -Solidity is a high-level language whose syntax is similar to that of JavaScript -and it is designed to compile to code for the Ethereum Virtual Machine. +Solidity is a contract-oriented, high-level language whose syntax is similar to that of JavaScript +and it is designed to target the Ethereum Virtual Machine. + +Solidity is statically typed, supports inheritance, libraries and complex +user-defines types among other features. + As you will see, it is possible to create contracts for voting, crowdfunding, blind auctions, multi-signature wallets and more. @@ -16,7 +20,7 @@ Useful links * `Ethereum <https://ethereum.org>`_ -* `Changelog <https://github.com/ethereum/wiki/wiki/Solidity-Changelog>`_ +* `Changelog <https://github.com/ethereum/solidity/blob/develop/Changelog.md>`_ * `Story Backlog <https://www.pivotaltracker.com/n/projects/1189488>`_ diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index ebb7537b..44ee34f4 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -9,7 +9,7 @@ Installing Solidity Versioning ========== -Solidity versions follow `semantic versioning <https://semver.org>` and in addition to +Solidity versions follow `semantic versioning <https://semver.org>`_ and in addition to releases, **nightly development builds** are also made available. The nightly builds are not guaranteed to be working and despite best efforts they might contain undocumented and/or broken changes. We recommend to use the latest release. Package installers below @@ -205,10 +205,11 @@ to semver and the severity of the change. Finally, a release is always made with of the current nightly build, but without the ``prerelease`` specifier. Example: -- 0) the 0.4.0 release is made -- 1) nightly build has a version of 0.4.1 from now on -- 2) non-breaking changes are introduced - no change in version -- 3) a breaking change is introduced - version is bumped to 0.5.0 -- 4) the 0.5.0 release is made + +0. the 0.4.0 release is made +1. nightly build has a version of 0.4.1 from now on +2. non-breaking changes are introduced - no change in version +3. a breaking change is introduced - version is bumped to 0.5.0 +4. the 0.5.0 release is made This behaviour works well with the version pragma. diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 922056ec..eeea85a7 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -348,10 +348,12 @@ storage. A contract can neither read nor write to any storage apart from its own. The second memory area is called **memory**, of which a contract obtains -a freshly cleared instance for each message call. Memory can be -addressed at byte level, but read and written to in 32 byte (256-bit) -chunks. Memory is more costly the larger it grows (it scales -quadratically). +a freshly cleared instance for each message call. Memory is linear and can be +addressed at byte level, but reads are limited to a width of 256 bits, while writes +can be either 8 bits or 256 bits wide. Memory is expanded by a word (256-bit), when +accessing (either reading or writing) a previously untouched memory word (ie. any offset +within a word). At the time of expansion, the cost in gas must be paid. Memory is more +costly the larger it grows (it scales quadratically). The EVM is not a register machine but a stack machine, so all computations are performed on an area called the **stack**. It has a maximum size of @@ -453,13 +455,19 @@ receives the address of the new contract on the stack. .. index:: selfdestruct -``selfdestruct`` -================ +Self-destruct +============= The only possibility that code is removed from the blockchain is when a contract at that address performs the ``selfdestruct`` operation. The remaining Ether stored at that address is sent to a designated -target and then the storage and code is removed. +target and then the storage and code is removed from the state. + +.. warning:: Even if a contract's code does not contain a call to ``selfdestruct``, + it can still perform that operation using ``delegatecall`` or ``callcode``. + +.. note:: The pruning of old contracts may or may not be implemented by Ethereum + clients. Additionally, archive nodes could choose to keep the contract storage + and code indefinitely. -Note that even if a contract's code does not contain a call to ``selfdestruct``, -it can still perform that operation using ``delegatecall`` or ``callcode``. +.. note:: Currently **external accounts** cannot be removed from the state. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 7d4cedb6..0b3eed38 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -34,17 +34,17 @@ Statically-sized variables (everything except mapping and dynamically-sized arra The elements of structs and arrays are stored after each other, just as if they were given explicitly. -Due to their unpredictable size, mapping and dynamically-sized array types use a ``sha3`` +Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash computation to find the starting position of the value or the array data. These starting positions are always full stack slots. The mapping or the dynamic array itself occupies an (unfilled) slot in storage at some position ``p`` according to the above rule (or by recursively applying this rule for mappings to mappings or arrays of arrays). For a dynamic array, this slot stores the number of elements in the array (byte arrays and strings are an exception here, see below). For a mapping, the slot is unused (but it is needed so that two equal mappings after each other will use a different hash distribution). -Array data is located at ``sha3(p)`` and the value corresponding to a mapping key -``k`` is located at ``sha3(k . p)`` where ``.`` is concatenation. If the value is again a -non-elementary type, the positions are found by adding an offset of ``sha3(k . p)``. +Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key +``k`` is located at ``keccak256(k . p)`` where ``.`` is concatenation. If the value is again a +non-elementary type, the positions are found by adding an offset of ``keccak256(k . p)``. -``bytes`` and ``string`` store their data in the same slot where also the length is stored if they are short. In particular: If the data is at most ``31`` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. If it is longer, the main slot stores ``length * 2 + 1`` and the data is stored as usual in ``sha3(slot)``. +``bytes`` and ``string`` store their data in the same slot where also the length is stored if they are short. In particular: If the data is at most ``31`` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. If it is longer, the main slot stores ``length * 2 + 1`` and the data is stored as usual in ``keccak256(slot)``. So for the following contract snippet:: @@ -54,7 +54,25 @@ So for the following contract snippet:: mapping(uint => mapping(uint => s)) data; } -The position of ``data[4][9].b`` is at ``sha3(uint256(9) . sha3(uint256(4) . uint256(1))) + 1``. +The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``. + +**************** +Layout in Memory +**************** + +Solidity reserves three 256-bit slots: + +- 0 - 64: scratch space for hashing methods +- 64 - 96: currently allocated memory size (aka. free memory pointer) + +Scratch space can be used between statements (ie. within inline assembly). + +Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future). + +.. warning:: + There are some operations in Solidity that need a temporary memory area larger than 64 bytes and therefore will not fit into the scratch space. They will be placed where the free memory points to, but given their short lifecycle, the pointer is not updated. The memory may or may not be zeroed out. Because of this, one shouldn't expect the free memory to be zeroed out. + +.. index: memory layout ***************** Esoteric Features @@ -281,7 +299,7 @@ The following is the order of precedence for operators, listed in order of evalu | *16* | Comma operator | ``,`` | +------------+-------------------------------------+--------------------------------------------+ -.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, sha3, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send +.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send Global Variables ================ @@ -299,7 +317,8 @@ Global Variables - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - ``tx.gasprice`` (``uint``): gas price of the transaction - ``tx.origin`` (``address``): sender of the transaction (full call chain) -- ``sha3(...) returns (bytes32)``: compute the Ethereum-SHA-3 (KECCAK-256) hash of the (tightly packed) arguments +- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments +- ``sha3(...) returns (bytes32)``: an alias to `keccak256()` - ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments - ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the (tightly packed) arguments - ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error @@ -338,3 +357,16 @@ Modifiers - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. - ``payable`` for functions: Allows them to receive Ether together with a call. + +Reserved Keywords +================= + +These keywords are reserved in Solidity. They might become part of the syntax in the future: + +``abstract``, ``after``, ``case``, ``catch``, ``final``, ``in``, ``inline``, ``interface``, ``let``, ``match``, +``of``, ``pure``, ``relocatable``, ``static``, ``switch``, ``try``, ``type``, ``typeof``, ``view``. + +Language Grammar +================ + +The entire language grammar is `available here <https://github.com/ethereum/solidity/blob/release/libsolidity/grammar.txt>`_. diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index a2f4ec4c..77e1bf08 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -123,7 +123,7 @@ Sending and Receiving Ether ``addr.call.value(x)()``. This is essentially the same as ``addr.send(x)``, only that it forwards all remaining gas and opens up the ability for the recipient to perform more expensive actions. This might include calling back - into the sending contract or other state changes you might not have though of. + 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. - If you want to send Ether using ``address.send``, there are certain details to be aware of: @@ -207,7 +207,7 @@ Minor Details You can craft transactions that call a function ``f(uint8 x)`` with a raw byte argument of ``0xff000001`` and with ``0x00000001``. Both are fed to the contract and both will look like the number ``1`` as far as ``x`` is concerned, but ``msg.data`` will - be different, so if you use ``sha3(msg.data)`` for anything, you will get different results. + be different, so if you use ``keccak256(msg.data)`` for anything, you will get different results. *************** Recommendations diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 9d3dd6f6..2e53b78c 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -415,7 +415,7 @@ high or low invalid bids. revealEnd = biddingEnd + _revealTime; } - /// Place a blinded bid with `_blindedBid` = sha3(value, + /// Place a blinded bid with `_blindedBid` = keccak256(value, /// fake, secret). /// The sent ether is only refunded if the bid is correctly /// revealed in the revealing phase. The bid is valid if the @@ -459,7 +459,7 @@ high or low invalid bids. var bid = bids[msg.sender][i]; var (value, fake, secret) = (_values[i], _fake[i], _secret[i]); - if (bid.blindedBid != sha3(value, fake, secret)) { + if (bid.blindedBid != keccak256(value, fake, secret)) { // Bid was not actually revealed. // Do not refund deposit. continue; diff --git a/docs/types.rst b/docs/types.rst index 9d7ebec9..9e7d9b4a 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -106,7 +106,7 @@ the function ``call`` is provided which takes an arbitrary number of arguments o address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2; nameReg.call("register", "MyName"); - nameReg.call(bytes4(sha3("fun(uint256)")), a); + nameReg.call(bytes4(keccak256("fun(uint256)")), a); ``call`` returns a boolean indicating whether the invoked function terminated (``true``) or caused an EVM exception (``false``). It is not possible to access the actual data returned (for this we would need to know the encoding and size in advance). @@ -186,7 +186,7 @@ the type ``ufixed0x256`` because ``1/3`` is not finitely representable in binary approximated. Any operator that can be applied to integers can also be applied to literal expressions as -long as the operators are integers. If any of the two is fractional, bit operations are disallowed +long as the operands are integers. If any of the two is fractional, bit operations are disallowed and exponentiation is disallowed if the exponent is fractional (because that might result in a non-rational number). @@ -371,6 +371,9 @@ So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper. that you are accessing the low-level bytes of the UTF-8 representation, and not the individual characters! +It is possible to mark arrays ``public`` and have Solidity create an accessor. +The numeric index will become a required parameter for the accessor. + .. index:: ! array;allocating, new Allocating Memory Arrays @@ -610,13 +613,43 @@ can actually be any type, including mappings. Mappings can be seen as hashtables which are virtually initialized 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 here, though: The key data is not actually stored -in a mapping, only its ``sha3`` hash used to look up the value. +in a mapping, only its ``keccak256`` hash 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). +It is possible to mark mappings ``public`` and have Solidity create an accessor. +The ``_KeyType`` will become a required parameter for the accessor and it will +return ``_ValueType``. + +The ``_ValueType`` can be a mapping too. The accessor will have one parameter +for each ``_KeyType``, recursively. + +:: + + pragma solidity ^0.4.0; + + contract MappingExample { + mapping(address => uint) public balances; + + function update(uint newBalance) { + balances[msg.sender] = newBalance; + } + } + + contract MappingUser { + function f() returns (uint) { + return MappingExample(<address>).balances(this); + } + } + + +.. note:: + Mappings are not iterable, but it is possible to implement a data structure on top of them. + For an example, see `iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_. + .. index:: assignment, ! delete, lvalue Operators Involving LValues diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 9ee334cf..3499bc71 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -79,7 +79,7 @@ Block and Transaction Properties You can only access the hashes of the most recent 256 blocks, all other values will be zero. -.. index:: sha3, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send +.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send Mathematical and Cryptographic Functions ---------------------------------------- @@ -88,8 +88,10 @@ Mathematical and Cryptographic Functions compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. +``keccak256(...) returns (bytes32)``: + compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments ``sha3(...) returns (bytes32)``: - compute the Ethereum-SHA-3 (KECCAK-256) hash of the (tightly packed) arguments + alias to `keccak256()` ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments ``ripemd160(...) returns (bytes20)``: @@ -100,18 +102,18 @@ Mathematical and Cryptographic Functions In the above, "tightly packed" means that the arguments are concatenated without padding. This means that the following are all identical:: - sha3("ab", "c") - sha3("abc") - sha3(0x616263) - sha3(6382179) - sha3(97, 98, 99) + keccak256("ab", "c") + keccak256("abc") + keccak256(0x616263) + keccak256(6382179) + keccak256(97, 98, 99) -If padding is needed, explicit type conversions can be used: ``sha3("\x00\x12")`` is the -same as ``sha3(uint16(0x12))``. +If padding is needed, explicit type conversions can be used: ``keccak256("\x00\x12")`` is the +same as ``keccak256(uint16(0x12))``. Note that constants will be packed using the minimum number of bytes required to store them. -This means that, for example, ``sha3(0) == sha3(uint8(0))`` and -``sha3(0x12345678) == sha3(uint32(0x12345678))``. +This means that, for example, ``keccak256(0) == keccak256(uint8(0))`` and +``keccak256(0x12345678) == keccak256(uint32(0x12345678))``. It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net. diff --git a/libdevcore/ABI.h b/libdevcore/ABI.h index 5b7d160d..423cfda8 100644 --- a/libdevcore/ABI.h +++ b/libdevcore/ABI.h @@ -63,7 +63,7 @@ template <class T, class ... U> bytes abiInAux(T const& _t, U const& ... _u) template <class ... T> bytes abiIn(std::string _id, T const& ... _t) { - return sha3(_id).ref().cropped(0, 4).toBytes() + abiInAux(_t ...); + return keccak256(_id).ref().cropped(0, 4).toBytes() + abiInAux(_t ...); } template <class T> struct ABIDeserialiser {}; diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 6fbc112d..43ae7162 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -87,47 +87,6 @@ using bytes = std::vector<byte>; using bytesRef = vector_ref<byte>; using bytesConstRef = vector_ref<byte const>; -template <class T> -class secure_vector -{ -public: - secure_vector() {} - secure_vector(secure_vector<T> const& /*_c*/) = default; // See https://github.com/ethereum/libweb3core/pull/44 - explicit secure_vector(unsigned _size): m_data(_size) {} - explicit secure_vector(unsigned _size, T _item): m_data(_size, _item) {} - explicit secure_vector(std::vector<T> const& _c): m_data(_c) {} - explicit secure_vector(vector_ref<T> _c): m_data(_c.data(), _c.data() + _c.size()) {} - explicit secure_vector(vector_ref<const T> _c): m_data(_c.data(), _c.data() + _c.size()) {} - ~secure_vector() { ref().cleanse(); } - - secure_vector<T>& operator=(secure_vector<T> const& _c) - { - if (&_c == this) - return *this; - - ref().cleanse(); - m_data = _c.m_data; - return *this; - } - std::vector<T>& writable() { clear(); return m_data; } - std::vector<T> const& makeInsecure() const { return m_data; } - - void clear() { ref().cleanse(); } - - vector_ref<T> ref() { return vector_ref<T>(&m_data); } - vector_ref<T const> ref() const { return vector_ref<T const>(&m_data); } - - size_t size() const { return m_data.size(); } - bool empty() const { return m_data.empty(); } - - void swap(secure_vector<T>& io_other) { m_data.swap(io_other.m_data); } - -private: - std::vector<T> m_data; -}; - -using bytesSec = secure_vector<byte>; - // Numeric types. using bigint = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<>>; using u64 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<64, 64, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>; diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index fc438276..b27271cf 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -20,13 +20,6 @@ */ #include "CommonData.h" -#include <random> - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:4724) // potential mod by 0, line 78 of boost/random/uniform_int_distribution.hpp (boost 1.55) -#endif -#include <boost/random/uniform_int_distribution.hpp> #if defined(_MSC_VER) #pragma warning(pop) #endif diff --git a/libdevcore/Exceptions.h b/libdevcore/Exceptions.h index a6c1f9ab..667ec31c 100644 --- a/libdevcore/Exceptions.h +++ b/libdevcore/Exceptions.h @@ -47,41 +47,15 @@ private: #define DEV_SIMPLE_EXCEPTION(X) struct X: virtual Exception { const char* what() const noexcept override { return #X; } } -/// Base class for all RLP exceptions. -struct RLPException: virtual Exception { RLPException(std::string _message = std::string()): Exception(_message) {} }; -#define DEV_SIMPLE_EXCEPTION_RLP(X) struct X: virtual RLPException { const char* what() const noexcept override { return #X; } } - -DEV_SIMPLE_EXCEPTION_RLP(BadCast); -DEV_SIMPLE_EXCEPTION_RLP(BadRLP); -DEV_SIMPLE_EXCEPTION_RLP(OversizeRLP); -DEV_SIMPLE_EXCEPTION_RLP(UndersizeRLP); - DEV_SIMPLE_EXCEPTION(BadHexCharacter); -DEV_SIMPLE_EXCEPTION(NoNetworking); -DEV_SIMPLE_EXCEPTION(NoUPnPDevice); -DEV_SIMPLE_EXCEPTION(RootNotFound); -struct BadRoot: virtual Exception { public: BadRoot(h256 const& _root): Exception("BadRoot " + _root.hex()), root(_root) {} h256 root; }; DEV_SIMPLE_EXCEPTION(FileError); -DEV_SIMPLE_EXCEPTION(Overflow); -DEV_SIMPLE_EXCEPTION(FailedInvariant); -DEV_SIMPLE_EXCEPTION(ValueTooLarge); - -struct InterfaceNotSupported: virtual Exception { public: InterfaceNotSupported(std::string _f): Exception("Interface " + _f + " not supported.") {} }; -struct ExternalFunctionFailure: virtual Exception { public: ExternalFunctionFailure(std::string _f): Exception("Function " + _f + "() failed.") {} }; // error information to be added to exceptions using errinfo_invalidSymbol = boost::error_info<struct tag_invalidSymbol, char>; -using errinfo_wrongAddress = boost::error_info<struct tag_address, std::string>; using errinfo_comment = boost::error_info<struct tag_comment, std::string>; using errinfo_required = boost::error_info<struct tag_required, bigint>; using errinfo_got = boost::error_info<struct tag_got, bigint>; -using errinfo_min = boost::error_info<struct tag_min, bigint>; -using errinfo_max = boost::error_info<struct tag_max, bigint>; -using RequirementError = boost::tuple<errinfo_required, errinfo_got>; -using errinfo_hash256 = boost::error_info<struct tag_hash, h256>; using errinfo_required_h256 = boost::error_info<struct tag_required_h256, h256>; using errinfo_got_h256 = boost::error_info<struct tag_get_h256, h256>; -using Hash256RequirementError = boost::tuple<errinfo_required_h256, errinfo_got_h256>; -using errinfo_extraData = boost::error_info<struct tag_extraData, bytes>; } diff --git a/libdevcore/SHA3.cpp b/libdevcore/SHA3.cpp index 584ef07e..3b12f39f 100644 --- a/libdevcore/SHA3.cpp +++ b/libdevcore/SHA3.cpp @@ -24,7 +24,7 @@ #include <cstdio> #include <cstdlib> #include <cstring> -#include "picosha2.h" + using namespace std; using namespace dev; @@ -49,12 +49,19 @@ namespace keccak #define decsha3(bits) \ int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t); +#define deckeccak(bits) \ + int keccak##bits(uint8_t*, size_t, const uint8_t*, size_t); + decshake(128) decshake(256) decsha3(224) decsha3(256) decsha3(384) decsha3(512) +deckeccak(224) +deckeccak(256) +deckeccak(384) +deckeccak(512) /******** The Keccak-f[1600] permutation ********/ @@ -192,6 +199,14 @@ static inline int hash(uint8_t* out, size_t outlen, if (outlen > (bits/8)) { \ return -1; \ } \ + return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x06); \ + } +#define defkeccak(bits) \ + int keccak##bits(uint8_t* out, size_t outlen, \ + const uint8_t* in, size_t inlen) { \ + if (outlen > (bits/8)) { \ + return -1; \ + } \ return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ } @@ -205,17 +220,20 @@ defsha3(256) defsha3(384) defsha3(512) -} +/*** KECCAK FOFs ***/ +defkeccak(224) +defkeccak(256) +defkeccak(384) +defkeccak(512) -unsigned g_sha3Counter = 0; +} -bool sha3(bytesConstRef _input, bytesRef o_output) +bool keccak256(bytesConstRef _input, bytesRef o_output) { // FIXME: What with unaligned memory? if (o_output.size() != 32) return false; - ++g_sha3Counter; - keccak::sha3_256(o_output.data(), 32, _input.data(), _input.size()); + keccak::keccak256(o_output.data(), 32, _input.data(), _input.size()); // keccak::keccak(ret.data(), 32, (uint64_t const*)_input.data(), _input.size()); return true; } diff --git a/libdevcore/SHA3.h b/libdevcore/SHA3.h index 5393952f..c481bfc9 100644 --- a/libdevcore/SHA3.h +++ b/libdevcore/SHA3.h @@ -25,7 +25,6 @@ #include <string> #include "FixedHash.h" -#include "vector_ref.h" namespace dev { @@ -34,26 +33,24 @@ namespace dev /// Calculate SHA3-256 hash of the given input and load it into the given output. /// @returns false if o_output.size() != 32. -bool sha3(bytesConstRef _input, bytesRef o_output); +bool keccak256(bytesConstRef _input, bytesRef o_output); /// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. -inline h256 sha3(bytesConstRef _input) { h256 ret; sha3(_input, ret.ref()); return ret; } +inline h256 keccak256(bytesConstRef _input) { h256 ret; keccak256(_input, ret.ref()); return ret; } /// Calculate SHA3-256 hash of the given input, returning as a 256-bit hash. -inline h256 sha3(bytes const& _input) { return sha3(bytesConstRef(&_input)); } +inline h256 keccak256(bytes const& _input) { return keccak256(bytesConstRef(&_input)); } /// Calculate SHA3-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash. -inline h256 sha3(std::string const& _input) { return sha3(bytesConstRef(_input)); } +inline h256 keccak256(std::string const& _input) { return keccak256(bytesConstRef(_input)); } /// Calculate SHA3-256 hash of the given input (presented as a FixedHash), returns a 256-bit hash. -template<unsigned N> inline h256 sha3(FixedHash<N> const& _input) { return sha3(_input.ref()); } +template<unsigned N> inline h256 keccak256(FixedHash<N> const& _input) { return keccak256(_input.ref()); } /// Calculate SHA3-256 hash of the given input, possibly interpreting it as nibbles, and return the hash as a string filled with binary data. -inline std::string sha3(std::string const& _input, bool _isNibbles) { return asString((_isNibbles ? sha3(fromHex(_input)) : sha3(bytesConstRef(&_input))).asBytes()); } +inline std::string keccak256(std::string const& _input, bool _isNibbles) { return asString((_isNibbles ? keccak256(fromHex(_input)) : keccak256(bytesConstRef(&_input))).asBytes()); } /// Calculate SHA3-256 MAC -inline void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output) { sha3(_secret.toBytes() + _plain.toBytes()).ref().populate(_output); } - -extern unsigned g_sha3Counter; +inline void keccak256mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output) { keccak256(_secret.toBytes() + _plain.toBytes()).ref().populate(_output); } } diff --git a/libdevcore/debugbreak.h b/libdevcore/debugbreak.h index 65612a34..f8838a5f 100644 --- a/libdevcore/debugbreak.h +++ b/libdevcore/debugbreak.h @@ -93,6 +93,13 @@ static void __inline__ trap_instruction(void) /* Has same known problem and workaround * as Thumb mode */ } +#elif defined(ETH_EMSCRIPTEN) +enum { HAVE_TRAP_INSTRUCTION = 1, }; +__attribute__((gnu_inline, always_inline)) +static void __inline__ trap_instruction(void) +{ + asm("debugger"); +} #else enum { HAVE_TRAP_INSTRUCTION = 0, }; #endif @@ -101,11 +108,7 @@ __attribute__((gnu_inline, always_inline)) static void __inline__ debug_break(void) { if (HAVE_TRAP_INSTRUCTION) { -#if defined(ETH_EMSCRIPTEN) - asm("debugger"); -#else trap_instruction(); -#endif } else if (DEBUG_BREAK_PREFER_BUILTIN_TRAP_TO_SIGTRAP) { /* raises SIGILL on Linux x86{,-64}, to continue in gdb: * (gdb) handle SIGILL stop nopass diff --git a/libdevcore/picosha2.h b/libdevcore/picosha2.h deleted file mode 100644 index 44b6bee5..00000000 --- a/libdevcore/picosha2.h +++ /dev/null @@ -1,360 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (C) 2014 okdshin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -#ifndef PICOSHA2_H -#define PICOSHA2_H -//picosha2:20140213 -#include <cstdint> -#include <iostream> -#include <vector> -#include <iterator> -#include <cassert> -#include <sstream> -#include <algorithm> - -namespace picosha2 -{ - -namespace detail -{ - -inline uint8_t mask_8bit(uint8_t x){ - return x&0xff; -} - -inline uint32_t mask_32bit(uint32_t x){ - return x&0xffffffff; -} - -static const uint32_t add_constant[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -static const uint32_t initial_message_digest[8] = { - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, - 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 -}; - -inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z){ - return (x&y)^((~x)&z); -} - -inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z){ - return (x&y)^(x&z)^(y&z); -} - -inline uint32_t rotr(uint32_t x, std::size_t n){ - assert(n < 32); - return mask_32bit((x>>n)|(x<<(32-n))); -} - -inline uint32_t bsig0(uint32_t x){ - return rotr(x, 2)^rotr(x, 13)^rotr(x, 22); -} - -inline uint32_t bsig1(uint32_t x){ - return rotr(x, 6)^rotr(x, 11)^rotr(x, 25); -} - -inline uint32_t shr(uint32_t x, std::size_t n){ - assert(n < 32); - return x >> n; -} - -inline uint32_t ssig0(uint32_t x){ - return rotr(x, 7)^rotr(x, 18)^shr(x, 3); -} - -inline uint32_t ssig1(uint32_t x){ - return rotr(x, 17)^rotr(x, 19)^shr(x, 10); -} - -template<typename RaIter1, typename RaIter2> -void hash256_block(RaIter1 message_digest, RaIter2 first, RaIter2 last){ - (void)last; // FIXME: check this is valid - uint32_t w[64]; - std::fill(w, w+64, 0); - for(std::size_t i = 0; i < 16; ++i){ - w[i] = (static_cast<uint32_t>(mask_8bit(*(first+i*4)))<<24) - |(static_cast<uint32_t>(mask_8bit(*(first+i*4+1)))<<16) - |(static_cast<uint32_t>(mask_8bit(*(first+i*4+2)))<<8) - |(static_cast<uint32_t>(mask_8bit(*(first+i*4+3)))); - } - for(std::size_t i = 16; i < 64; ++i){ - w[i] = mask_32bit(ssig1(w[i-2])+w[i-7]+ssig0(w[i-15])+w[i-16]); - } - - uint32_t a = *message_digest; - uint32_t b = *(message_digest+1); - uint32_t c = *(message_digest+2); - uint32_t d = *(message_digest+3); - uint32_t e = *(message_digest+4); - uint32_t f = *(message_digest+5); - uint32_t g = *(message_digest+6); - uint32_t h = *(message_digest+7); - - for(std::size_t i = 0; i < 64; ++i){ - uint32_t temp1 = h+bsig1(e)+ch(e,f,g)+add_constant[i]+w[i]; - uint32_t temp2 = bsig0(a)+maj(a,b,c); - h = g; - g = f; - f = e; - e = mask_32bit(d+temp1); - d = c; - c = b; - b = a; - a = mask_32bit(temp1+temp2); - } - *message_digest += a; - *(message_digest+1) += b; - *(message_digest+2) += c; - *(message_digest+3) += d; - *(message_digest+4) += e; - *(message_digest+5) += f; - *(message_digest+6) += g; - *(message_digest+7) += h; - for(std::size_t i = 0; i < 8; ++i){ - *(message_digest+i) = mask_32bit(*(message_digest+i)); - } -} - -}//namespace detail - -template<typename InIter> -void output_hex(InIter first, InIter last, std::ostream& os){ - os.setf(std::ios::hex, std::ios::basefield); - while(first != last){ - os.width(2); - os.fill('0'); - os << static_cast<unsigned int>(*first); - ++first; - } - os.setf(std::ios::dec, std::ios::basefield); -} - -template<typename InIter> -void bytes_to_hex_string(InIter first, InIter last, std::string& hex_str){ - std::ostringstream oss; - output_hex(first, last, oss); - hex_str.assign(oss.str()); -} - -template<typename InContainer> -void bytes_to_hex_string(const InContainer& bytes, std::string& hex_str){ - bytes_to_hex_string(bytes.begin(), bytes.end(), hex_str); -} - -template<typename InIter> -std::string bytes_to_hex_string(InIter first, InIter last){ - std::string hex_str; - bytes_to_hex_string(first, last, hex_str); - return hex_str; -} - -template<typename InContainer> -std::string bytes_to_hex_string(const InContainer& bytes){ - std::string hex_str; - bytes_to_hex_string(bytes, hex_str); - return hex_str; -} - -class hash256_one_by_one { -public: - hash256_one_by_one(){ - init(); - } - - void init(){ - buffer_.clear(); - std::fill(data_length_digits_, data_length_digits_+4, 0); - std::copy(detail::initial_message_digest, detail::initial_message_digest+8, h_); - } - - template<typename RaIter> - void process(RaIter first, RaIter last){ - add_to_data_length(std::distance(first, last)); - std::copy(first, last, std::back_inserter(buffer_)); - std::size_t i = 0; - for(;i+64 <= buffer_.size(); i+=64){ - detail::hash256_block(h_, buffer_.begin()+i, buffer_.begin()+i+64); - } - buffer_.erase(buffer_.begin(), buffer_.begin()+i); - } - - void finish(){ - uint8_t temp[64]; - std::fill(temp, temp+64, 0); - std::size_t remains = buffer_.size(); - std::copy(buffer_.begin(), buffer_.end(), temp); - temp[remains] = 0x80; - - if(remains > 55){ - std::fill(temp+remains+1, temp+64, 0); - detail::hash256_block(h_, temp, temp+64); - std::fill(temp, temp+64-4, 0); - } - else { - std::fill(temp+remains+1, temp+64-4, 0); - } - - write_data_bit_length(&(temp[56])); - detail::hash256_block(h_, temp, temp+64); - } - - template<typename OutIter> - void get_hash_bytes(OutIter first, OutIter last)const{ - for(const uint32_t* iter = h_; iter != h_+8; ++iter){ - for(std::size_t i = 0; i < 4 && first != last; ++i){ - *(first++) = detail::mask_8bit(static_cast<uint8_t>((*iter >> (24-8*i)))); - } - } - } - -private: - void add_to_data_length(uint32_t n) { - uint32_t carry = 0; - data_length_digits_[0] += n; - for(std::size_t i = 0; i < 4; ++i) { - data_length_digits_[i] += carry; - if(data_length_digits_[i] >= 65536u) { - data_length_digits_[i] -= 65536u; - carry = 1; - } - else { - break; - } - } - } - void write_data_bit_length(uint8_t* begin) { - uint32_t data_bit_length_digits[4]; - std::copy( - data_length_digits_, data_length_digits_+4, - data_bit_length_digits - ); - - // convert byte length to bit length (multiply 8 or shift 3 times left) - uint32_t carry = 0; - for(std::size_t i = 0; i < 4; ++i) { - uint32_t before_val = data_bit_length_digits[i]; - data_bit_length_digits[i] <<= 3; - data_bit_length_digits[i] |= carry; - data_bit_length_digits[i] &= 65535u; - carry = (before_val >> (16-3)) & 65535u; - } - - // write data_bit_length - for(int i = 3; i >= 0; --i) { - (*begin++) = static_cast<uint8_t>(data_bit_length_digits[i] >> 8); - (*begin++) = static_cast<uint8_t>(data_bit_length_digits[i]); - } - } - std::vector<uint8_t> buffer_; - uint32_t data_length_digits_[4]; //as 64bit integer (16bit x 4 integer) - uint32_t h_[8]; -}; - -inline void get_hash_hex_string(const hash256_one_by_one& hasher, std::string& hex_str){ - uint8_t hash[32]; - hasher.get_hash_bytes(hash, hash+32); - return bytes_to_hex_string(hash, hash+32, hex_str); -} - -inline std::string get_hash_hex_string(const hash256_one_by_one& hasher){ - std::string hex_str; - get_hash_hex_string(hasher, hex_str); - return hex_str; -} - -template<typename RaIter, typename OutIter> -void hash256(RaIter first, RaIter last, OutIter first2, OutIter last2){ - hash256_one_by_one hasher; - //hasher.init(); - hasher.process(first, last); - hasher.finish(); - hasher.get_hash_bytes(first2, last2); -} - -template<typename RaIter, typename OutContainer> -void hash256(RaIter first, RaIter last, OutContainer& dst){ - hash256(first, last, dst.begin(), dst.end()); -} - -template<typename RaContainer, typename OutIter> -void hash256(const RaContainer& src, OutIter first, OutIter last){ - hash256(src.begin(), src.end(), first, last); -} - -template<typename RaContainer, typename OutContainer> -void hash256(const RaContainer& src, OutContainer& dst){ - hash256(src.begin(), src.end(), dst.begin(), dst.end()); -} - - -template<typename RaIter> -void hash256_hex_string(RaIter first, RaIter last, std::string& hex_str){ - uint8_t hashed[32]; - hash256(first, last, hashed, hashed+32); - std::ostringstream oss; - output_hex(hashed, hashed+32, oss); - hex_str.assign(oss.str()); -} - -template<typename RaIter> -std::string hash256_hex_string(RaIter first, RaIter last){ - std::string hex_str; - hash256_hex_string(first, last, hex_str); - return hex_str; -} - -inline void hash256_hex_string(const std::string& src, std::string& hex_str){ - hash256_hex_string(src.begin(), src.end(), hex_str); -} - -template<typename RaContainer> -void hash256_hex_string(const RaContainer& src, std::string& hex_str){ - hash256_hex_string(src.begin(), src.end(), hex_str); -} - -template<typename RaContainer> -std::string hash256_hex_string(const RaContainer& src){ - return hash256_hex_string(src.begin(), src.end()); -} - -}//namespace picosha2 - -#endif //PICOSHA2_H diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 698377c9..0f543181 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -69,26 +69,6 @@ public: void copyTo(vector_ref<typename std::remove_const<_T>::type> _t) const { if (overlapsWith(_t)) memmove(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); else memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } /// Copies the contents of this vector_ref to the contents of @a _t, and zeros further trailing elements in @a _t. void populate(vector_ref<typename std::remove_const<_T>::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); } - /// Securely overwrite the memory. - /// @note adapted from OpenSSL's implementation. - void cleanse() - { - static unsigned char s_cleanseCounter = 0; - uint8_t* p = (uint8_t*)begin(); - size_t const len = (uint8_t*)end() - p; - size_t loop = len; - size_t count = s_cleanseCounter; - while (loop--) - { - *(p++) = (uint8_t)count; - count += (17 + ((size_t)p & 0xf)); - } - p = (uint8_t*)memchr((uint8_t*)begin(), (uint8_t)count, len); - if (p) - count += (63 + (size_t)p); - s_cleanseCounter = (uint8_t)count; - memset((uint8_t*)begin(), 0, len); - } _T* begin() { return m_data; } _T* end() { return m_data + m_count; } diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index c7822819..e881c1e2 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -296,7 +296,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i) AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) { - h256 h(dev::sha3(_identifier)); + h256 h(dev::keccak256(_identifier)); m_libraries[h] = _identifier; return AssemblyItem(PushLibraryAddress, h); } @@ -327,8 +327,10 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs) AssemblyItems optimisedItems; for (BasicBlock const& block: cfg.optimisedBlocks()) { - assertThrow(!!block.startState, OptimizerException, ""); - CommonSubexpressionEliminator eliminator(*block.startState); + // We used to start with the block's initial state but it caused + // too many inconsistencies. + KnownState emptyState; + CommonSubexpressionEliminator eliminator(emptyState); auto iter = m_items.begin() + block.begin; auto const end = m_items.begin() + block.end; while (iter < end) diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index af28e220..dae1e1da 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -45,11 +45,11 @@ public: AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); } - AssemblyItem newData(bytes const& _data) { h256 h(sha3(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); } + AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); } AssemblyItem newSub(Assembly 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(sha3(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); } + 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); diff --git a/libevmasm/ExpressionClasses.cpp b/libevmasm/ExpressionClasses.cpp index 9d13a57a..cf5e6a0e 100644 --- a/libevmasm/ExpressionClasses.cpp +++ b/libevmasm/ExpressionClasses.cpp @@ -257,6 +257,7 @@ Rules::Rules() {{Instruction::MOD, {0, X}}, [=]{ return u256(0); }}, {{Instruction::AND, {X, 0}}, [=]{ return u256(0); }}, {{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }}, + {{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; } }, // operations involving an expression and itself {{Instruction::AND, {X, X}}, [=]{ return X; }}, {{Instruction::OR, {X, X}}, [=]{ return X; }}, diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index dd269ff4..0b6e0ac5 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -378,7 +378,7 @@ KnownState::Id KnownState::applySha3( for (Id a: arguments) data += toBigEndian(*m_expressionClasses->knownConstant(a)); data.resize(size_t(*l)); - v = m_expressionClasses->find(AssemblyItem(u256(sha3(data)), _location)); + v = m_expressionClasses->find(AssemblyItem(u256(dev::keccak256(data)), _location)); } else v = m_expressionClasses->find(sha3Item, {_start, _length}, true, m_sequenceNumber); diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index e9f86ba0..9dcac845 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -52,17 +52,18 @@ void CodeFragment::finalise(CompilerState const& _cs) CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowASM) { -/* cdebug << "CodeFragment. Locals:"; +/* + std::cout << "CodeFragment. Locals:"; for (auto const& i: _s.defs) - cdebug << i.first << ":" << toHex(i.second.m_code); - cdebug << "Args:"; + std::cout << i.first << ":" << i.second.m_asm.out(); + std::cout << "Args:"; for (auto const& i: _s.args) - cdebug << i.first << ":" << toHex(i.second.m_code); - cdebug << "Outers:"; + std::cout << i.first << ":" << i.second.m_asm.out(); + std::cout << "Outers:"; for (auto const& i: _s.outers) - cdebug << i.first << ":" << toHex(i.second.m_code); - debugOutAST(cout, _t); - cout << endl << flush; + std::cout << i.first << ":" << i.second.m_asm.out(); + debugOutAST(std::cout, _t); + std::cout << endl << flush; */ switch (_t.which()) { diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index f1538789..68499106 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -45,13 +45,21 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error if (_errors) { _errors->push_back("Parse error."); - _errors->push_back(diagnostic_information(_e)); + _errors->push_back(boost::diagnostic_information(_e)); } } - catch (std::exception) + catch (std::exception const& _e) { if (_errors) - _errors->push_back("Parse error."); + { + _errors->push_back("Parse exception."); + _errors->push_back(boost::diagnostic_information(_e)); + } + } + catch (...) + { + if (_errors) + _errors->push_back("Internal parse exception."); } return bytes(); } @@ -70,12 +78,22 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v catch (Exception const& _e) { if (_errors) - _errors->push_back(diagnostic_information(_e)); + { + _errors->push_back("Parse error."); + _errors->push_back(boost::diagnostic_information(_e)); + } + } + catch (std::exception const& _e) + { + if (_errors) { + _errors->push_back("Parse exception."); + _errors->push_back(boost::diagnostic_information(_e)); + } } - catch (std::exception) + catch (...) { if (_errors) - _errors->push_back("Parse error."); + _errors->push_back("Internal parse exception."); } return string(); } diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp index 63351bc4..91c2452d 100644 --- a/liblll/CompilerState.cpp +++ b/liblll/CompilerState.cpp @@ -45,8 +45,6 @@ CodeFragment const& CompilerState::getDef(std::string const& _s) void CompilerState::populateStandard() { static const string s = "{" - "(def 'gav 0x51ba59315b3a95761d0863b05ccc7a7f54703d99)" - "(def 'config 0x661005d2720d855f1d9976f88bb10c1a3398c77f)" "(def 'allgas (- (gas) 21))" "(def 'send (to value) (call allgas to value 0 0 0 0))" "(def 'send (gaslimit to value) (call gaslimit to value 0 0 0 0))" @@ -60,23 +58,21 @@ void CompilerState::populateStandard() "(def 'sha3 (val) { [0]:val (sha3 0 32) })" "(def 'sha3pair (a b) { [0]:a [32]:b (sha3 0 64) })" "(def 'sha3trip (a b c) { [0]:a [32]:b [64]:c (sha3 0 96) })" + "(def 'keccak256 (loc len) (sha3 loc len))" "(def 'return (val) { [0]:val (return 0 32) })" "(def 'returnlll (code) (return 0 (lll code 0)) )" "(def 'makeperm (name pos) { (def name (sload pos)) (def name (v) (sstore pos v)) } )" "(def 'permcount 0)" "(def 'perm (name) { (makeperm name permcount) (def 'permcount (+ permcount 1)) } )" - "(def 'namereg (msg config 0))" - "(def 'coinreg (msg config 1))" - "(def 'gavcoin (msg config 2))" - "(def 'sendgavcoin (to value) { [32]'send [64]:to [96]:value (call allgas gavcoin 0 32 96 0 0) })" - "(def 'regname (name) { [32]'register [64]name (call allgas namereg 0 32 64 0 0) })" - "(def 'regcoin (name) { [32]name (call allgas coinreg 0 32 32 0 0) })" - "(def 'regcoin (name denom) { [32]name [64]denom (call allgas coinreg 0 32 64 0 0) })" "(def 'ecrecover (r s v hash) { [0] r [32] s [64] v [96] hash (msg allgas 1 0 0 128) })" "(def 'sha256 (data datasize) (msg allgas 2 0 data datasize))" "(def 'ripemd160 (data datasize) (msg allgas 3 0 data datasize))" "(def 'sha256 (val) { [0]:val (sha256 0 32) })" "(def 'ripemd160 (val) { [0]:val (ripemd160 0 32) })" + "(def 'wei 1)" + "(def 'szabo 1000000000000)" + "(def 'finney 1000000000000000)" + "(def 'ether 1000000000000000000)" "}"; CodeFragment::compile(s, *this); } diff --git a/liblll/Exceptions.h b/liblll/Exceptions.h index 1e9671b3..aa4bf4e8 100644 --- a/liblll/Exceptions.h +++ b/liblll/Exceptions.h @@ -39,6 +39,7 @@ class InvalidName: public CompilerException {}; class InvalidMacroArgs: public CompilerException {}; class InvalidLiteral: public CompilerException {}; class BareSymbol: public CompilerException {}; +class ParserException: public CompilerException {}; } } diff --git a/liblll/Parser.cpp b/liblll/Parser.cpp index aa4a4de2..2754e9f5 100644 --- a/liblll/Parser.cpp +++ b/liblll/Parser.cpp @@ -96,19 +96,13 @@ void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out) using symbol_type = sp::basic_string<std::string, sp::utree_type::symbol_type>; using it = string::const_iterator; - static const u256 ether = u256(1000000000) * 1000000000; - static const u256 finney = u256(1000000000) * 1000000; - static const u256 szabo = u256(1000000000) * 1000; - qi::rule<it, space_type, sp::utree()> element; qi::rule<it, string()> str = '"' > qi::lexeme[+(~qi::char_(std::string("\"") + '\0'))] > '"'; qi::rule<it, string()> strsh = '\'' > qi::lexeme[+(~qi::char_(std::string(" ;$@()[]{}:\n\t") + '\0'))]; qi::rule<it, symbol_type()> symbol = qi::lexeme[+(~qi::char_(std::string(" $@[]{}:();\"\x01-\x1f\x7f") + '\0'))]; qi::rule<it, string()> intstr = qi::lexeme[ qi::no_case["0x"][qi::_val = "0x"] >> *qi::char_("0-9a-fA-F")[qi::_val += qi::_1]] | qi::lexeme[+qi::char_("0-9")[qi::_val += qi::_1]]; qi::rule<it, bigint()> integer = intstr; - qi::rule<it, bigint()> multiplier = qi::lit("wei")[qi::_val = 1] | qi::lit("szabo")[qi::_val = szabo] | qi::lit("finney")[qi::_val = finney] | qi::lit("ether")[qi::_val = ether]; - qi::rule<it, space_type, bigint()> quantity = integer[qi::_val = qi::_1] >> -multiplier[qi::_val *= qi::_1]; - qi::rule<it, space_type, sp::utree()> atom = quantity[qi::_val = px::construct<sp::any_ptr>(px::new_<bigint>(qi::_1))] | (str | strsh)[qi::_val = qi::_1] | symbol[qi::_val = qi::_1]; + qi::rule<it, space_type, sp::utree()> atom = integer[qi::_val = px::construct<sp::any_ptr>(px::new_<bigint>(qi::_1))] | (str | strsh)[qi::_val = qi::_1] | symbol[qi::_val = qi::_1]; qi::rule<it, space_type, sp::utree::list_type()> seq = '{' > *element > '}'; qi::rule<it, space_type, sp::utree::list_type()> mload = '@' > element; qi::rule<it, space_type, sp::utree::list_type()> sload = qi::lit("@@") > element; @@ -143,7 +137,8 @@ void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out) auto ret = s.cbegin(); qi::phrase_parse(ret, s.cend(), element, space, qi::skip_flag::dont_postskip, o_out); for (auto i = ret; i != s.cend(); ++i) - if (!isspace(*i)) - BOOST_THROW_EXCEPTION(std::exception()); + if (!isspace(*i)) { + BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment("Non-whitespace left in parser")); + } } diff --git a/liblll/Parser.h b/liblll/Parser.h index b21989f0..e4542888 100644 --- a/liblll/Parser.h +++ b/liblll/Parser.h @@ -24,6 +24,7 @@ #include <string> #include <vector> #include <libdevcore/Common.h> +#include "Exceptions.h" namespace boost { namespace spirit { class utree; } } namespace sp = boost::spirit; diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index a7ffcfad..d075949e 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -48,6 +48,8 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared< make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Location::MulMod)), make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)), + make_shared<MagicVariableDeclaration>("keccak256", + make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)), make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)), make_shared<MagicVariableDeclaration>("log1", diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h index 3c110b19..e3b642db 100644 --- a/libsolidity/analysis/SemVerHandler.h +++ b/libsolidity/analysis/SemVerHandler.h @@ -40,6 +40,12 @@ struct SemVerVersion std::string prerelease; std::string build; + unsigned major() const { return numbers[0]; } + unsigned minor() const { return numbers[1]; } + unsigned patch() const { return numbers[2]; } + + bool isPrerelease() const { return !prerelease.empty(); } + explicit SemVerVersion(std::string const& _versionString = "0.0.0"); }; diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index a95b4879..dbaa15ed 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -52,13 +52,22 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) { if (!m_versionPragmaFound) { + string errorString("Source file does not specify required compiler version!"); + SemVerVersion recommendedVersion{string(VersionString)}; + if (!recommendedVersion.isPrerelease()) + errorString += + "Consider adding \"pragma solidity ^" + + to_string(recommendedVersion.major()) + + string(".") + + to_string(recommendedVersion.minor()) + + string(".") + + to_string(recommendedVersion.patch()); + string(";\""); + auto err = make_shared<Error>(Error::Type::Warning); *err << errinfo_sourceLocation(_sourceUnit.location()) << - errinfo_comment( - string("Source file does not specify required compiler version! ") + - string("Consider adding \"pragma solidity ^") + VersionNumber + string(";\".") - ); + errinfo_comment(errorString); m_errors.push_back(err); } } @@ -67,7 +76,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) { solAssert(!_pragma.tokens().empty(), ""); solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); - if (_pragma.tokens()[0] != Token::Identifier && _pragma.literals()[0] != "solidity") + if (_pragma.tokens()[0] != Token::Identifier || _pragma.literals()[0] != "solidity") syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); else { diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ae7c13c8..46f4f7f6 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -609,6 +609,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return false; pushes = 1; } + else + return false; for (unsigned i = 0; i < pushes; ++i) _assembly.append(u256(0)); // just to verify the stack height } @@ -716,11 +718,10 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if (ref->dataStoredIn(DataLocation::Storage)) { - auto err = make_shared<Error>(Error::Type::Warning); - *err << - errinfo_sourceLocation(varDecl.location()) << - errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?"); - m_errors.push_back(err); + warning( + varDecl.location(), + "Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?" + ); } } varDecl.accept(*this); @@ -879,6 +880,10 @@ bool TypeChecker::visit(Conditional const& _conditional) TypePointer trueType = type(_conditional.trueExpression())->mobileType(); TypePointer falseType = type(_conditional.falseExpression())->mobileType(); + if (!trueType) + fatalTypeError(_conditional.trueExpression().location(), "Invalid mobile type."); + if (!falseType) + fatalTypeError(_conditional.falseExpression().location(), "Invalid mobile type."); TypePointer commonType = Type::commonType(trueType, falseType); if (!commonType) @@ -985,10 +990,16 @@ bool TypeChecker::visit(TupleExpression const& _tuple) types.push_back(type(*components[i])); if (_tuple.isInlineArray()) solAssert(!!types[i], "Inline array cannot have empty components"); - if (i == 0 && _tuple.isInlineArray()) - inlineArrayType = types[i]->mobileType(); - else if (_tuple.isInlineArray() && inlineArrayType) - inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType()); + if (_tuple.isInlineArray()) + { + if ((i == 0 || inlineArrayType) && !types[i]->mobileType()) + fatalTypeError(components[i]->location(), "Invalid mobile type."); + + if (i == 0) + inlineArrayType = types[i]->mobileType(); + else if (inlineArrayType) + inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType()); + } } else types.push_back(TypePointer()); @@ -1375,6 +1386,11 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) } else if (exprType->category() == Type::Category::FixedBytes) annotation.isLValue = false; + else if (TypeType const* typeType = dynamic_cast<decltype(typeType)>(exprType.get())) + { + if (ContractType const* contractType = dynamic_cast<decltype(contractType)>(typeType->actualType().get())) + annotation.isLValue = annotation.referencedDeclaration->isLValue(); + } return false; } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 294daa13..695d9881 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -151,7 +151,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter if (signaturesSeen.count(functionSignature) == 0) { signaturesSeen.insert(functionSignature); - FixedHash<4> hash(dev::sha3(functionSignature)); + FixedHash<4> hash(dev::keccak256(functionSignature)); m_interfaceFunctionList->push_back(make_pair(hash, fun)); } } @@ -189,6 +189,7 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const m_inheritableMembers.reset(new vector<Declaration const*>()); auto addInheritableMember = [&](Declaration const* _decl) { + solAssert(_decl, "addInheritableMember got a nullpointer."); if (memberSeen.count(_decl->name()) == 0 && _decl->isVisibleInDerivedContracts()) { memberSeen.insert(_decl->name()); @@ -204,6 +205,9 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const for (StructDefinition const* s: definedStructs()) addInheritableMember(s); + + for (EnumDefinition const* e: definedEnums()) + addInheritableMember(e); } return *m_inheritableMembers; } diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 8fd1584d..7ed4ddce 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -623,7 +623,7 @@ public: virtual bool isLValue() const override; virtual bool isPartOfExternalInterface() const override { return isPublic(); } - bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(scope()); } + bool isLocalVariable() const { return !!dynamic_cast<CallableDeclaration const*>(scope()); } /// @returns true if this variable is a parameter or return parameter of a function. bool isCallableParameter() const; /// @returns true if this variable is a parameter (not return parameter) of an external function. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 4b5f12ce..7cfed3c8 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -23,6 +23,7 @@ #include <libsolidity/ast/Types.h> #include <limits> #include <boost/range/adaptor/reversed.hpp> +#include <boost/range/adaptor/sliced.hpp> #include <libdevcore/CommonIO.h> #include <libdevcore/CommonData.h> #include <libdevcore/SHA3.h> @@ -197,7 +198,9 @@ TypePointer Type::forLiteral(Literal const& _literal) TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) { - if (_b->isImplicitlyConvertibleTo(*_a)) + if (!_a || !_b) + return TypePointer(); + else if (_b->isImplicitlyConvertibleTo(*_a)) return _a; else if (_a->isImplicitlyConvertibleTo(*_b)) return _b; @@ -241,7 +244,8 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition seenFunctions.insert(function); FunctionType funType(*function, false); if (auto fun = funType.asMemberFunction(true, true)) - members.push_back(MemberList::Member(function->name(), fun, function)); + if (_type.isImplicitlyConvertibleTo(*fun->selfType())) + members.push_back(MemberList::Member(function->name(), fun, function)); } } return members; @@ -481,7 +485,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal !all_of(radixPoint + 1, _literal.value().end(), ::isdigit) || !all_of(_literal.value().begin(), radixPoint, ::isdigit) ) - throw; + return make_tuple(false, rational(0)); //Only decimal notation allowed here, leading zeros would switch to octal. auto fractionalBegin = find_if_not( radixPoint + 1, @@ -574,7 +578,11 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo); if (!isFractional()) - return fixedBytes.numBytes() * 8 >= integerType()->numBits(); + { + if (integerType()) + return fixedBytes.numBytes() * 8 >= integerType()->numBits(); + return false; + } else return false; } @@ -700,6 +708,34 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ value = rational(denominator, numerator); break; } + case Token::SHL: + { + using boost::multiprecision::pow; + if (fractional) + return TypePointer(); + else if (other.m_value < 0) + return TypePointer(); + else if (other.m_value > numeric_limits<uint32_t>::max()) + return TypePointer(); + uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); + value = m_value.numerator() * pow(bigint(2), exponent); + break; + } + // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue + // determines the resulting type and the type of shift (SAR or SHR). + case Token::SAR: + { + using boost::multiprecision::pow; + if (fractional) + return TypePointer(); + else if (other.m_value < 0) + return TypePointer(); + else if (other.m_value > numeric_limits<uint32_t>::max()) + return TypePointer(); + uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); + value = rational(m_value.numerator() / pow(bigint(2), exponent), 1); + break; + } default: return TypePointer(); } @@ -1291,7 +1327,10 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con if (m_super) { // add the most derived of all functions which are visible in derived contracts - for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts) + auto bases = m_contract.annotation().linearizedBaseContracts; + solAssert(bases.size() >= 1, "linearizedBaseContracts should at least contain the most derived contract."); + // `sliced(1, ...)` ignores the most derived contract, which should not be searchable from `super`. + for (ContractDefinition const* base: bases | boost::adaptors::sliced(1, bases.size())) for (FunctionDefinition const* function: base->definedFunctions()) { if (!function->isVisibleInDerivedContracts()) @@ -1624,7 +1663,17 @@ TypePointer TupleType::mobileType() const { TypePointers mobiles; for (auto const& c: components()) - mobiles.push_back(c ? c->mobileType() : TypePointer()); + { + if (c) + { + auto mt = c->mobileType(); + if (!mt) + return TypePointer(); + mobiles.push_back(mt); + } + else + mobiles.push_back(TypePointer()); + } return make_shared<TupleType>(mobiles); } @@ -2033,7 +2082,7 @@ string FunctionType::externalSignature() const u256 FunctionType::externalIdentifier() const { - return FixedHash<4>::Arith(FixedHash<4>(dev::sha3(externalSignature()))); + return FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(externalSignature()))); } TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) @@ -2065,6 +2114,9 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) const { + if (_bound && m_parameterTypes.empty()) + return FunctionTypePointer(); + TypePointers parameterTypes; for (auto const& t: m_parameterTypes) { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 9173f39a..3f94d11a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -623,6 +623,7 @@ public: } virtual unsigned storageBytes() const override { return 20; } virtual bool canLiveOutsideStorage() const override { return true; } + virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; } virtual bool isValueType() const override { return true; } virtual std::string toString(bool _short) const override; virtual std::string canonicalName(bool _addDataLocation) const override; @@ -871,7 +872,12 @@ public: m_isConstant(_isConstant), m_isPayable(_isPayable), m_declaration(_declaration) - {} + { + solAssert( + !m_bound || !m_parameterTypes.empty(), + "Attempted construction of bound function without self type" + ); + } TypePointers parameterTypes() const; std::vector<std::string> parameterNames() const; @@ -939,8 +945,9 @@ public: /// removed and the location of reference types is changed from CallData to Memory. /// This is needed if external functions are called on other contracts, as they cannot return /// dynamic values. + /// Returns empty shared pointer on a failure. Namely, if a bound function has no parameters. /// @param _inLibrary if true, uses DelegateCall as location. - /// @param _bound if true, the argumenst are placed as `arg1.functionName(arg2, ..., argn)`. + /// @param _bound if true, the arguments are placed as `arg1.functionName(arg2, ..., argn)`. FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const; private: diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index ec496df8..e064c1a6 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -368,8 +368,11 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << Instruction::MUL; } else if (targetTypeCategory == Type::Category::Enum) + { + solAssert(_typeOnStack.mobileType(), ""); // just clean convertType(_typeOnStack, *_typeOnStack.mobileType(), true); + } else if (targetTypeCategory == Type::Category::FixedPoint) { solAssert( diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 18b42fce..ebb84784 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -575,7 +575,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) return true; } ); - solAssert(errors.empty(), "Code generation for inline assembly with errors requested."); + solAssert(Error::containsOnlyWarnings(errors), "Code generation for inline assembly with errors requested."); m_context.setStackOffset(startStackHeight); return false; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 26acd8a4..9a096e2d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -56,8 +56,10 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c if (_varDecl.annotation().type->dataStoredIn(DataLocation::Storage)) { // reference type, only convert value to mobile type and do final conversion in storeValue. - utils().convertType(*type, *type->mobileType()); - type = type->mobileType(); + auto mt = type->mobileType(); + solAssert(mt, ""); + utils().convertType(*type, *mt); + type = mt; } else { @@ -670,7 +672,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.externalSignature()))); + m_context << u256(h256::Arith(dev::keccak256(function.externalSignature()))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); @@ -861,11 +863,12 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) } // Special processing for TypeType because we do not want to visit the library itself - // for internal functions. + // for internal functions, or enum/struct definitions. if (TypeType const* type = dynamic_cast<TypeType const*>(_memberAccess.expression().annotation().type.get())) { if (dynamic_cast<ContractType const*>(type->actualType().get())) { + solAssert(_memberAccess.annotation().type, "_memberAccess has no type"); if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) { if (funType->location() != FunctionType::Location::Internal) @@ -883,6 +886,12 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << m_context.functionEntryLabel(*function).pushTag(); } } + else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type.get())) + { + // no-op + } + else if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) + appendVariable(*variable, static_cast<Expression const&>(_memberAccess)); else _memberAccess.expression().accept(*this); } @@ -1196,15 +1205,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) m_context << m_context.virtualFunctionEntryLabel(*functionDef).pushTag(); else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration)) - { - if (!variable->isConstant()) - setLValueFromDeclaration(*declaration, _identifier); - else - { - variable->value()->accept(*this); - utils().convertType(*variable->value()->annotation().type, *variable->annotation().type); - } - } + appendVariable(*variable, static_cast<Expression const&>(_identifier)); else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration)) { if (contract->isLibrary()) @@ -1432,11 +1433,17 @@ void ExpressionCompiler::appendExternalFunctionCall( // Evaluate arguments. TypePointers argumentTypes; TypePointers parameterTypes = _functionType.parameterTypes(); - bool manualFunctionId = + bool manualFunctionId = false; + if ( (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode || funKind == FunctionKind::BareDelegateCall) && - !_arguments.empty() && - _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == + !_arguments.empty() + ) + { + solAssert(_arguments.front()->annotation().type->mobileType(), ""); + manualFunctionId = + _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == CompilerUtils::dataStartOffset; + } if (manualFunctionId) { // If we have a Bare* and the first type has exactly 4 bytes, use it as @@ -1633,6 +1640,17 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, utils().storeInMemoryDynamic(_expectedType); } +void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression) +{ + if (!_variable.isConstant()) + setLValueFromDeclaration(_variable, _expression); + else + { + _variable.value()->accept(*this); + utils().convertType(*_variable.value()->annotation().type, *_variable.annotation().type); + } +} + void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) { if (m_context.isLocalVariable(&_declaration)) diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 43a92a10..f4ce1fec 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -103,6 +103,8 @@ private: /// expected to be on the stack and is updated by this call. void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression); + /// Appends code for a variable that might be a constant or not + void appendVariable(VariableDeclaration const& _variable, Expression const& _expression); /// Sets the current LValue to a new one (of the appropriate type) from the given declaration. /// Also retrieves the value if it was not requested by @a _expression. void setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression); diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index f3831b40..813fa3ab 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -757,6 +757,20 @@ bool Why3Translator::visit(Literal const& _literal) return false; } +bool Why3Translator::visit(PragmaDirective const& _pragma) +{ + if (_pragma.tokens().empty()) + error(_pragma, "Not supported"); + else if (_pragma.literals().empty()) + error(_pragma, "Not supported"); + else if (_pragma.literals()[0] != "solidity") + error(_pragma, "Not supported"); + else if (_pragma.tokens()[0] != Token::Identifier) + error(_pragma, "A literal 'solidity' is not an identifier. Strange"); + + return false; +} + bool Why3Translator::isStateVariable(VariableDeclaration const* _var) const { return contains(m_currentContract.stateVariables, _var); diff --git a/libsolidity/formal/Why3Translator.h b/libsolidity/formal/Why3Translator.h index 22bfff89..4fdac385 100644 --- a/libsolidity/formal/Why3Translator.h +++ b/libsolidity/formal/Why3Translator.h @@ -94,6 +94,7 @@ private: virtual bool visit(IndexAccess const& _node) override; virtual bool visit(Identifier const& _node) override; virtual bool visit(Literal const& _node) override; + virtual bool visit(PragmaDirective const& _node) override; virtual bool visitNode(ASTNode const& _node) override { diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 755cf281..d84ee10c 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -77,7 +77,7 @@ Expression = | Expression? (',' Expression) | PrimaryExpression -PrimaryExpression = Identifier | BooleanLiteral | NumberLiteral | StringLiteral +PrimaryExpression = Identifier | BooleanLiteral | NumberLiteral | HexLiteral | StringLiteral FunctionCall = ( PrimaryExpression | NewExpression | TypeName ) ( ( '.' Identifier ) | ( '[' Expression ']' ) )* '(' Expression? ( ',' Expression )* ')' NewExpression = 'new' Identifier @@ -88,8 +88,8 @@ BooleanLiteral = 'true' | 'false' NumberLiteral = '0x'? [0-9]+ (' ' NumberUnit)? NumberUnit = 'wei' | 'szabo' | 'finney' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' +HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' - Identifier = [a-zA-Z_] [a-zA-Z_0-9]* ElementaryTypeName = 'address' | 'bool' | 'string' | 'var' diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 53d19b0a..5d920cb7 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -23,6 +23,7 @@ #include <libsolidity/inlineasm/AsmCodeGen.h> #include <memory> #include <functional> +#include <libdevcore/CommonIO.h> #include <libevmasm/Assembly.h> #include <libevmasm/SourceLocation.h> #include <libevmasm/Instruction.h> @@ -213,10 +214,31 @@ public: void operator()(assembly::Block const& _block) { size_t numVariables = m_state.variables.size(); + int deposit = m_state.assembly.deposit(); std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); - // pop variables - // we deliberately do not check stack height + deposit = m_state.assembly.deposit() - deposit; + m_state.assembly.setSourceLocation(_block.location); + + // issue warnings for stack height discrepancies + if (deposit < 0) + { + m_state.addError( + Error::Type::Warning, + "Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.", + _block.location + ); + } + else if (deposit > 0) + { + m_state.addError( + Error::Type::Warning, + "Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.", + _block.location + ); + } + + // pop variables while (m_state.variables.size() > numVariables) { m_state.assembly.append(solidity::Instruction::POP); diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 5c7163ee..8d2c2ed4 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -95,7 +95,9 @@ assembly::Statement Parser::parseStatement() fatalParserError("Label name / variable name must precede \":\"."); assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement); m_scanner->next(); - if (m_scanner->currentToken() == Token::Assign) + // identifier:=: should be parsed as identifier: =: (i.e. a label), + // while identifier:= (being followed by a non-colon) as identifier := (assignment). + if (m_scanner->currentToken() == Token::Assign && m_scanner->peekNextToken() != Token::Colon) { // functional assignment FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location); @@ -133,6 +135,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) // Allowed instructions, lowercase names. static map<string, dev::solidity::Instruction> s_instructions; if (s_instructions.empty()) + { for (auto const& instruction: solidity::c_instructions) { if ( @@ -141,24 +144,29 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) ) continue; string name = instruction.first; - if (instruction.second == solidity::Instruction::SUICIDE) - name = "selfdestruct"; transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); s_instructions[name] = instruction.second; } + // add alias for selfdestruct + s_instructions["selfdestruct"] = solidity::Instruction::SUICIDE; + } + Statement ret; switch (m_scanner->currentToken()) { case Token::Identifier: case Token::Return: case Token::Byte: + case Token::Address: { string literal; if (m_scanner->currentToken() == Token::Return) literal = "return"; else if (m_scanner->currentToken() == Token::Byte) literal = "byte"; + else if (m_scanner->currentToken() == Token::Address) + literal = "address"; else literal = m_scanner->currentLiteral(); // first search the set of instructions. diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index ec6b5d2e..efbbd237 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -21,8 +21,10 @@ * Full-stack compiler that converts a source code string to bytecode. */ -#include <boost/algorithm/string.hpp> -#include <boost/filesystem.hpp> +#include <libsolidity/interface/CompilerStack.h> + +#include <libsolidity/interface/Version.h> +#include <libsolidity/analysis/SemVerHandler.h> #include <libsolidity/ast/AST.h> #include <libsolidity/parsing/Scanner.h> #include <libsolidity/parsing/Parser.h> @@ -32,12 +34,15 @@ #include <libsolidity/analysis/DocStringAnalyser.h> #include <libsolidity/analysis/SyntaxChecker.h> #include <libsolidity/codegen/Compiler.h> -#include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/InterfaceHandler.h> #include <libsolidity/formal/Why3Translator.h> #include <libdevcore/SHA3.h> +#include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> + + using namespace std; using namespace dev; using namespace dev::solidity; @@ -100,6 +105,13 @@ bool CompilerStack::parse() m_errors.clear(); m_parseSuccessful = false; + if (SemVerVersion{string(VersionString)}.isPrerelease()) + { + auto err = make_shared<Error>(Error::Type::Warning); + *err << errinfo_comment("This is a pre-release compiler version, please do not use it in production."); + m_errors.push_back(err); + } + vector<string> sourcesToParse; for (auto const& s: m_sources) sourcesToParse.push_back(s.first); @@ -302,7 +314,7 @@ dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const if (obj.bytecode.empty() || !obj.linkReferences.empty()) return dev::h256(); else - return dev::sha3(obj.bytecode); + return dev::keccak256(obj.bytecode); } Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 99ed75bc..1c804b78 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -136,7 +136,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( ExpressionClasses& classes = state->expressionClasses(); using Id = ExpressionClasses::Id; using Ids = vector<Id>; - Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::sha3(_signature))))); + Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(_signature))))); Id calldata = classes.find(Instruction::CALLDATALOAD, Ids{classes.find(u256(0))}); classes.forceEqual(hashValue, Instruction::DIV, Ids{ calldata, diff --git a/prerelease.txt b/prerelease.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/prerelease.txt diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index fe7ea11d..9b432e95 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -29,7 +29,12 @@ set -e if [[ "$OSTYPE" != "darwin"* ]]; then - date -u +"nightly.%Y.%-m.%-d" > prerelease.txt + if [ "$TRAVIS_BRANCH" = release ] + then + echo -n > prerelease.txt + else + date -u +"nightly.%Y.%-m.%-d" > prerelease.txt + fi ./scripts/travis-emscripten/install_deps.sh docker run -v $(pwd):/src trzeci/emscripten:sdk-tag-1.35.4-64bit ./scripts/travis-emscripten/build_emscripten.sh fi diff --git a/scripts/isolateTests.py b/scripts/isolateTests.py new file mode 100755 index 00000000..fed779d3 --- /dev/null +++ b/scripts/isolateTests.py @@ -0,0 +1,24 @@ +#!/usr/bin/python +# +# This script reads C++ source files and writes all +# multi-line strings into individual files. +# This can be used to extract the Solidity test cases +# into files for e.g. fuzz testing as +# scripts/isolateTests.py tests/libsolidity/SolidityEndToEndTest.cpp + +import sys +lines = sys.stdin.read().split('\n') +inside = False +tests = [] +for l in lines: + if inside: + if l.strip().endswith(')";'): + inside = False + else: + tests[-1] += l + '\n' + else: + if l.strip().endswith('R"('): + inside = True + tests += [''] +for i in range(len(tests)): + open('test%d.sol' % i, 'w').write(tests[i]) diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 6a30faf5..7231f582 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -59,7 +59,7 @@ commitdate=`git show --format=%ci HEAD | head -n 1 | cut - -b1-10 | sed -e 's/-0 echo "$commithash" > commit_hash.txt if [ $branch = develop ] then - debversion="$version-nightly-$commitdate-$commithash" + debversion="$version-develop-$commitdate-$commithash" else debversion="$version" echo -n > prerelease.txt # proper release diff --git a/scripts/travis-emscripten/publish_binary.sh b/scripts/travis-emscripten/publish_binary.sh index d372995c..7bf6f369 100755 --- a/scripts/travis-emscripten/publish_binary.sh +++ b/scripts/travis-emscripten/publish_binary.sh @@ -54,21 +54,45 @@ git config user.name "travis" git config user.email "chris@ethereum.org" git checkout -B gh-pages origin/gh-pages git clean -f -d -x -# We only want one release per day and we do not want to push the same commit twice. -if ls ./bin/soljson-"$VER-nightly.$DATE"-*.js || ls ./bin/soljson-*"commit.$COMMIT.js" + + +FULLVERSION=INVALID +if [ "$TRAVIS_BRANCH" = release ] then - echo "Not publishing, we already published this version today." - exit 0 + # We only want one file with this version + if ls ./bin/soljson-"$VER+"*.js + then + echo "Not publishing, we already published this version." + exit 0 + fi + FULLVERSION="$VER+commit.$COMMIT" +elif [ "$TRAVIS_BRANCH" = develop ] +then + # We only want one release per day and we do not want to push the same commit twice. + if ls ./bin/soljson-"$VER-nightly.$DATE"*.js || ls ./bin/soljson-*"commit.$COMMIT.js" + then + echo "Not publishing, we already published this version today." + exit 0 + fi + FULLVERSION="$VER-nightly.$DATE+commit.$COMMIT" +else + echo "Not publishing, wrong branch." + exit 0 fi + +NEWFILE=./bin/"soljson-$FULLVERSION.js" + +# Prepare for update script +npm install + # This file is assumed to be the product of the build_emscripten.sh script. -cp ../soljson.js ./bin/"soljson-$VER-nightly.$DATE+commit.$COMMIT.js" -node ./update -cd bin -LATEST=$(ls -r soljson-v* | head -n 1) -cp "$LATEST" soljson-latest.js -cp soljson-latest.js ../soljson.js -git add . -git add ../soljson.js -git commit -m "Added compiler version $LATEST" +cp ../soljson.js "$NEWFILE" + +# Run update script +npm run update + +# Publish updates +git add "$NEWFILE" +git commit -a -m "Added compiler version $FULLVERSION" git push origin gh-pages diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index f0a34632..84cc2534 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -401,7 +401,10 @@ Usage: solc [options] [input_file...] Compiles the given Solidity input files (or the standard input if none given or "-" is used as a file name) and outputs the components specified in the options at standard output or in files in the output directory, if specified. -Example: solc --bin -o /tmp/solcoutput contract.sol +Imports are automatically read from the filesystem, but it is also possible to +remap paths using the context:prefix=path syntax. +Example: + solc --bin -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol Allowed options)", po::options_description::m_default_line_length, @@ -471,7 +474,7 @@ Allowed options)", try { po::command_line_parser cmdLineParser(_argc, _argv); - cmdLineParser.options(allOptions).positional(filesPositions).allow_unregistered(); + cmdLineParser.options(allOptions).positional(filesPositions); po::store(cmdLineParser.run(), m_args); } catch (po::error const& _exception) diff --git a/std/Token.sol b/std/Token.sol index 50d9ab7a..396dbf9e 100644 --- a/std/Token.sol +++ b/std/Token.sol @@ -2,10 +2,10 @@ contract Token { event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value); - function totalSupply() constant returns (uint256 supply) {} - function balanceOf(address _owner) constant returns (uint256 balance) {} - function transfer(address _to, uint256 _value) returns (bool success) {} - function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {} - function approve(address _spender, uint256 _value) returns (bool success) {} - function allowance(address _owner, address _spender) constant returns (uint256 remaining) {} + function totalSupply() constant returns (uint256 supply); + function balanceOf(address _owner) constant returns (uint256 balance); + function transfer(address _to, uint256 _value) returns (bool success); + function transferFrom(address _from, address _to, uint256 _value) returns (bool success); + function approve(address _spender, uint256 _value) returns (bool success); + function allowance(address _owner, address _spender) constant returns (uint256 remaining); } diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index cfc630d4..ec968058 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -588,7 +588,7 @@ BOOST_AUTO_TEST_CASE(revoke_addOwner) BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs()); // add a new owner Address deployer = m_sender; - h256 opHash = sha3(FixedHash<4>(dev::sha3("addOwner(address)")).asBytes() + h256(0x33).asBytes()); + h256 opHash = dev::keccak256(FixedHash<4>(dev::keccak256("addOwner(address)")).asBytes() + h256(0x33).asBytes()); BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); m_sender = account(0); diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 1f216680..fc103393 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -82,7 +82,7 @@ public: { u256 gasUsed = 0; GasMeter::GasConsumption gas; - FixedHash<4> hash(dev::sha3(_sig)); + FixedHash<4> hash(dev::keccak256(_sig)); for (bytes const& arguments: _argumentVariants) { sendMessage(hash.asBytes() + arguments, false, 0); diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 8d1a1d6c..6c04367f 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -51,7 +51,7 @@ bool successParse(std::string const& _source, bool _assemble = false) if (_assemble) { stack.assemble(); - if (!stack.errors().empty()) + if (!stack.errors().empty() && !Error::containsOnlyWarnings(stack.errors())) return false; } } @@ -87,9 +87,14 @@ BOOST_AUTO_TEST_CASE(simple_instructions) BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub }")); } +BOOST_AUTO_TEST_CASE(suicide_selfdestruct) +{ + BOOST_CHECK(successParse("{ suicide selfdestruct }")); +} + BOOST_AUTO_TEST_CASE(keywords) { - BOOST_CHECK(successParse("{ byte return }")); + BOOST_CHECK(successParse("{ byte return address }")); } BOOST_AUTO_TEST_CASE(constants) @@ -152,6 +157,18 @@ BOOST_AUTO_TEST_CASE(oversize_string_literals) BOOST_CHECK(!successAssemble("{ let x := \"123456789012345678901234567890123\" }")); } +BOOST_AUTO_TEST_CASE(assignment_after_tag) +{ + BOOST_CHECK(successParse("{ let x := 1 { tag: =: x } }")); +} + +BOOST_AUTO_TEST_CASE(magic_variables) +{ + BOOST_CHECK(!successAssemble("{ this }")); + BOOST_CHECK(!successAssemble("{ ecrecover }")); + BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover }")); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 23bd2abc..8ef9a45b 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1260,7 +1260,7 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("data()") == encodeArgs(8)); BOOST_CHECK(callContractFunction("name()") == encodeArgs("Celina")); - BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::sha3(bytes(1, 0x7b)))); + BOOST_CHECK(callContractFunction("a_hash()") == encodeArgs(dev::keccak256(bytes(1, 0x7b)))); BOOST_CHECK(callContractFunction("an_address()") == encodeArgs(toBigEndian(u160(0x1337)))); BOOST_CHECK(callContractFunction("super_secret_data()") == bytes()); } @@ -1342,7 +1342,7 @@ BOOST_AUTO_TEST_CASE(msg_sig) } )"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes()))); + BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::keccak256("foo(uint256)")).asBytes()))); } BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) @@ -1358,7 +1358,7 @@ BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) } )"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::sha3("foo(uint256)")).asBytes()))); + BOOST_CHECK(callContractFunction("foo(uint256)") == encodeArgs(asString(FixedHash<4>(dev::keccak256("foo(uint256)")).asBytes()))); } BOOST_AUTO_TEST_CASE(now) @@ -1686,7 +1686,7 @@ BOOST_AUTO_TEST_CASE(sha3) compileAndRun(sourceCode); auto f = [&](u256 const& _x) -> u256 { - return dev::sha3(toBigEndian(_x)); + return dev::keccak256(toBigEndian(_x)); }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(5)); @@ -2513,6 +2513,15 @@ BOOST_AUTO_TEST_CASE(super_in_constructor) BOOST_CHECK(callContractFunction("f()") == encodeArgs(1 | 2 | 4 | 8)); } +BOOST_AUTO_TEST_CASE(super_alone) +{ + char const* sourceCode = R"( + contract A { function f() { super; } } + )"; + compileAndRun(sourceCode, 0, "A"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); +} + BOOST_AUTO_TEST_CASE(fallback_function) { char const* sourceCode = R"( @@ -2582,7 +2591,7 @@ BOOST_AUTO_TEST_CASE(event) BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value))); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256)"))); BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender, h256::AlignRight)); BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id)); } @@ -2604,7 +2613,7 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK(m_logs[0].data.empty()); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit()"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); } BOOST_AUTO_TEST_CASE(event_anonymous) @@ -2664,7 +2673,7 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK(m_logs[0].data == encodeArgs((u160)m_sender, id, value, true)); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,bytes32,uint256,bool)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256,bool)"))); } BOOST_AUTO_TEST_CASE(event_really_lots_of_data) @@ -2681,9 +2690,9 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data) callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::sha3("deposit()")).asBytes()); + BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::keccak256("deposit()")).asBytes()); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); } BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) @@ -2707,7 +2716,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3, string("ABC"))); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); } BOOST_AUTO_TEST_CASE(event_indexed_string) @@ -2738,11 +2747,11 @@ BOOST_AUTO_TEST_CASE(event_indexed_string) dynx[i] = i; BOOST_CHECK(m_logs[0].data == bytes()); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3); - BOOST_CHECK_EQUAL(m_logs[0].topics[1], dev::sha3(dynx)); - BOOST_CHECK_EQUAL(m_logs[0].topics[2], dev::sha3( + BOOST_CHECK_EQUAL(m_logs[0].topics[1], dev::keccak256(dynx)); + BOOST_CHECK_EQUAL(m_logs[0].topics[2], dev::keccak256( encodeArgs(u256(4), u256(5), u256(6), u256(7)) )); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("E(string,uint256[4])"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("E(string,uint256[4])"))); } BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) @@ -2784,7 +2793,7 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments) compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13) == encodeArgs( - dev::sha3( + dev::keccak256( toBigEndian(u256(10)) + toBigEndian(u256(12)) + toBigEndian(u256(13))))); @@ -2802,7 +2811,7 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_numeric_literals) compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("foo(uint256,uint16)", 10, 12) == encodeArgs( - dev::sha3( + dev::keccak256( toBigEndian(u256(10)) + bytes{0x0, 0xc} + bytes(1, 0x91)))); @@ -2823,10 +2832,10 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals) })"; compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("foo()") == encodeArgs(dev::sha3("foo"))); + BOOST_CHECK(callContractFunction("foo()") == encodeArgs(dev::keccak256("foo"))); BOOST_CHECK(callContractFunction("bar(uint256,uint16)", 10, 12) == encodeArgs( - dev::sha3( + dev::keccak256( toBigEndian(u256(10)) + bytes{0x0, 0xc} + bytes(1, 0x91) + @@ -2868,7 +2877,27 @@ BOOST_AUTO_TEST_CASE(iterated_sha3_with_bytes) )"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("foo()") == encodeArgs( - u256(dev::sha3(bytes{'b'} + dev::sha3("xyz").asBytes() + bytes{'a'})) + u256(dev::keccak256(bytes{'b'} + dev::keccak256("xyz").asBytes() + bytes{'a'})) + )); +} + +BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) +{ + char const* sourceCode = R"( + contract c { + function foo(uint a, uint b, uint c) returns (bytes32 d) + { + d = keccak256(a, b, c); + } + })"; + compileAndRun(sourceCode); + + BOOST_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13) == encodeArgs( + dev::keccak256( + toBigEndian(u256(10)) + + toBigEndian(u256(12)) + + toBigEndian(u256(13)) + ) )); } @@ -3013,9 +3042,9 @@ BOOST_AUTO_TEST_CASE(bytes_from_calldata_to_memory) } )"; compileAndRun(sourceCode); - bytes calldata1 = FixedHash<4>(dev::sha3("f()")).asBytes() + bytes(61, 0x22) + bytes(12, 0x12); + bytes calldata1 = FixedHash<4>(dev::keccak256("f()")).asBytes() + bytes(61, 0x22) + bytes(12, 0x12); sendMessage(calldata1, false); - BOOST_CHECK(m_output == encodeArgs(dev::sha3(bytes{'a', 'b', 'c'} + calldata1))); + BOOST_CHECK(m_output == encodeArgs(dev::keccak256(bytes{'a', 'b', 'c'} + calldata1))); } BOOST_AUTO_TEST_CASE(call_forward_bytes) @@ -3285,6 +3314,57 @@ BOOST_AUTO_TEST_CASE(using_enums) BOOST_CHECK(callContractFunction("getChoice()") == encodeArgs(2)); } +BOOST_AUTO_TEST_CASE(using_contract_enums_with_explicit_contract_name) +{ + char const* sourceCode = R"( + contract test { + enum Choice { A, B, C } + function answer () returns (test.Choice _ret) + { + _ret = test.Choice.B; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("answer()") == encodeArgs(1)); +} + +BOOST_AUTO_TEST_CASE(using_inherited_enum) +{ + char const* sourceCode = R"( + contract base { + enum Choice { A, B, C } + } + + contract test is base { + function answer () returns (Choice _ret) + { + _ret = Choice.B; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("answer()") == encodeArgs(1)); +} + +BOOST_AUTO_TEST_CASE(using_inherited_enum_excplicitly) +{ + char const* sourceCode = R"( + contract base { + enum Choice { A, B, C } + } + + contract test is base { + function answer () returns (base.Choice _ret) + { + _ret = base.Choice.B; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("answer()") == encodeArgs(1)); +} + BOOST_AUTO_TEST_CASE(constructing_enums_from_ints) { char const* sourceCode = R"( @@ -3389,8 +3469,8 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments) )"; compileAndRun(sourceCode); - string innercalldata1 = asString(FixedHash<4>(dev::sha3("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9)); - string innercalldata2 = asString(FixedHash<4>(dev::sha3("g(uint256)")).asBytes() + encodeArgs(3)); + string innercalldata1 = asString(FixedHash<4>(dev::keccak256("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9)); + string innercalldata2 = asString(FixedHash<4>(dev::keccak256("g(uint256)")).asBytes() + encodeArgs(3)); bytes calldata = encodeArgs( 12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13, u256(innercalldata1.length()), innercalldata1, @@ -4668,7 +4748,7 @@ BOOST_AUTO_TEST_CASE(reusing_memory) } )"; compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(dev::sha3(dev::toBigEndian(u256(0x34))))); + BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(dev::keccak256(dev::toBigEndian(u256(0x34))))); } BOOST_AUTO_TEST_CASE(return_string) @@ -5586,6 +5666,120 @@ BOOST_AUTO_TEST_CASE(accessor_for_const_state_variable) BOOST_CHECK(callContractFunction("ticketPrice()") == encodeArgs(u256(555))); } +BOOST_AUTO_TEST_CASE(state_variable_under_contract_name) +{ + char const* text = R"( + contract Scope { + uint stateVar = 42; + + function getStateVar() constant returns (uint stateVar) { + stateVar = Scope.stateVar; + } + } + )"; + compileAndRun(text); + BOOST_CHECK(callContractFunction("getStateVar()") == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(state_variable_local_variable_mixture) +{ + char const* sourceCode = R"( + contract A { + uint x = 1; + uint y = 2; + function a() returns (uint x) { + x = A.y; + } + } + )"; + + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(2))); +} + +BOOST_AUTO_TEST_CASE(inherited_function) { + char const* sourceCode = R"( + contract A { function f() internal returns (uint) { return 1; } } + contract B is A { + function f() internal returns (uint) { return 2; } + function g() returns (uint) { + return A.f(); + } + } + )"; + + compileAndRun(sourceCode, 0, "B"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(inherited_function_from_a_library) { + char const* sourceCode = R"( + library A { function f() internal returns (uint) { return 1; } } + contract B { + function f() internal returns (uint) { return 2; } + function g() returns (uint) { + return A.f(); + } + } + )"; + + compileAndRun(sourceCode, 0, "B"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(inherited_constant_state_var) +{ + char const* sourceCode = R"( + contract A { + uint constant x = 7; + } + contract B is A { + function f() returns (uint) { + return A.x; + } + } + )"; + + compileAndRun(sourceCode, 0, "B"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7))); +} + +BOOST_AUTO_TEST_CASE(multiple_inherited_state_vars) +{ + char const* sourceCode = R"( + contract A { + uint x = 7; + } + contract B { + uint x = 9; + } + contract C is A, B { + function a() returns (uint) { + return A.x; + } + function b() returns (uint) { + return B.x; + } + function a_set(uint _x) returns (uint) { + A.x = _x; + return 1; + } + function b_set(uint _x) returns (uint) { + B.x = _x; + return 1; + } + } + )"; + + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(7))); + BOOST_CHECK(callContractFunction("b()") == encodeArgs(u256(9))); + BOOST_CHECK(callContractFunction("a_set(uint256)", u256(1)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("b_set(uint256)", u256(3)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("b()") == encodeArgs(u256(3))); +} + BOOST_AUTO_TEST_CASE(constant_string_literal) { char const* sourceCode = R"( @@ -5825,6 +6019,48 @@ BOOST_AUTO_TEST_CASE(using_library_structs) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7), u256(8))); } +BOOST_AUTO_TEST_CASE(library_struct_as_an_expression) +{ + char const* sourceCode = R"( + library Arst { + struct Foo { + int Things; + int Stuff; + } + } + + contract Tsra { + function f() returns(uint) { + Arst.Foo; + return 1; + } + } + )"; + compileAndRun(sourceCode, 0, "Tsra"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(library_enum_as_an_expression) +{ + char const* sourceCode = R"( + library Arst { + enum Foo { + Things, + Stuff + } + } + + contract Tsra { + function f() returns(uint) { + Arst.Foo; + return 1; + } + } + )"; + compileAndRun(sourceCode, 0, "Tsra"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1))); +} + BOOST_AUTO_TEST_CASE(short_strings) { // This test verifies that the byte array encoding that combines length and data works @@ -7231,6 +7467,72 @@ BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call) BOOST_CHECK(callContractFunction("f(address)", cAddrOpt) == encodeArgs(u256(7))); } +BOOST_AUTO_TEST_CASE(shift_constant_left) +{ + char const* sourceCode = R"( + contract C { + uint public a = 0x42 << 8; + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(0x4200))); +} + +BOOST_AUTO_TEST_CASE(shift_negative_constant_left) +{ + char const* sourceCode = R"( + contract C { + int public a = -0x42 << 8; + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(-0x4200))); +} + +BOOST_AUTO_TEST_CASE(shift_constant_right) +{ + char const* sourceCode = R"( + contract C { + uint public a = 0x4200 >> 8; + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(0x42))); +} + +BOOST_AUTO_TEST_CASE(shift_negative_constant_right) +{ + char const* sourceCode = R"( + contract C { + int public a = -0x4200 >> 8; + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(-0x42))); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_in_modifiers) +{ + char const* sourceCode = R"( + contract C { + modifier m { + uint a = 1; + assembly { + a := 2 + } + if (a != 2) + throw; + _; + } + function f() m returns (bool) { + return true; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(true)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 6cf7e0ee..7d44edaf 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -39,6 +39,7 @@ namespace dev { namespace solidity { + using rational = boost::rational<dev::bigint>; /// An Ethereum address: 20 bytes. /// @NOTE This is not endian-specific; it's just a bunch of bytes. using Address = h160; @@ -105,7 +106,7 @@ public: template <class... Args> bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments) { - FixedHash<4> hash(dev::sha3(_sig)); + FixedHash<4> hash(dev::keccak256(_sig)); sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); return m_output; } @@ -155,6 +156,14 @@ public: static bytes encode(char const* _value) { return encode(std::string(_value)); } static bytes encode(byte _value) { return bytes(31, 0) + bytes{_value}; } static bytes encode(u256 const& _value) { return toBigEndian(_value); } + /// @returns the fixed-point encoding of a rational number with a given + /// number of fractional bits. + static bytes encode(std::pair<rational, int> const& _valueAndPrecision) + { + rational const& value = _valueAndPrecision.first; + int fractionalBits = _valueAndPrecision.second; + return encode(u256((value.numerator() << fractionalBits) / value.denominator())); + } static bytes encode(h256 const& _value) { return _value.asBytes(); } static bytes encode(bytes const& _value, bool _padLeft = true) { @@ -186,7 +195,6 @@ public: { return encodeArgs(u256(0x20), u256(_arg.size()), _arg); } - class ContractInterface { public: diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index b8c64336..9fe91cca 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -154,7 +154,7 @@ static FunctionTypePointer retrieveFunctionBySignature( std::string const& _signature ) { - FixedHash<4> hash(dev::sha3(_signature)); + FixedHash<4> hash(dev::keccak256(_signature)); return _contract->interfaceFunctions()[hash]; } @@ -854,6 +854,23 @@ BOOST_AUTO_TEST_CASE(implicit_base_to_derived_conversion) BOOST_CHECK(expectError(text) == Error::Type::TypeError); } +BOOST_AUTO_TEST_CASE(super_excludes_current_contract) +{ + char const* text = R"( + contract A { + function b() {} + } + + contract B is A { + function f() { + super.f(); + } + } + )"; + + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + BOOST_AUTO_TEST_CASE(function_modifier_invocation) { char const* text = R"( @@ -1019,6 +1036,19 @@ BOOST_AUTO_TEST_CASE(private_state_variable) BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an internal variable should not exist"); } +BOOST_AUTO_TEST_CASE(missing_state_variable) +{ + char const* text = R"( + contract Scope { + function getStateVar() constant returns (uint stateVar) { + stateVar = Scope.stateVar; // should fail. + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + + BOOST_AUTO_TEST_CASE(base_class_state_variable_accessor) { // test for issue #1126 https://github.com/ethereum/cpp-ethereum/issues/1126 @@ -1439,6 +1469,21 @@ BOOST_AUTO_TEST_CASE(enum_invalid_member_access) BOOST_CHECK(expectError(text) == Error::Type::TypeError); } +BOOST_AUTO_TEST_CASE(enum_invalid_direct_member_access) +{ + char const* text = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function test() + { + choices = Sit; + } + ActionChoices choices; + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::DeclarationError); +} + BOOST_AUTO_TEST_CASE(enum_explicit_conversion_is_okay) { char const* text = R"( @@ -1500,6 +1545,23 @@ BOOST_AUTO_TEST_CASE(enum_duplicate_values) BOOST_CHECK(expectError(text) == Error::Type::DeclarationError); } +BOOST_AUTO_TEST_CASE(enum_name_resolution_under_current_contract_name) +{ + char const* text = R"( + contract A { + enum Foo { + First, + Second + } + + function a() { + A.Foo; + } + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_CASE(private_visibility) { char const* sourceCode = R"( @@ -4020,6 +4082,169 @@ BOOST_AUTO_TEST_CASE(invalid_array_as_statement) BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); } +BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype) +{ + char const* text = R"( + library B { + function b() {} + } + + contract A { + using B for bytes; + + function a() { + bytes memory x; + x.b(); + } + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(invalid_fixed_point_literal) +{ + char const* text = R"( + contract A { + function a() { + .8E0; + } + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(shift_constant_left_negative_rvalue) +{ + char const* text = R"( + contract C { + uint public a = 0x42 << -8; + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(shift_constant_right_negative_rvalue) +{ + char const* text = R"( + contract C { + uint public a = 0x42 >> -8; + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(shift_constant_left_excessive_rvalue) +{ + char const* text = R"( + contract C { + uint public a = 0x42 << 0x100000000; + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(shift_constant_right_excessive_rvalue) +{ + char const* text = R"( + contract C { + uint public a = 0x42 >> 0x100000000; + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_positive_stack) +{ + char const* text = R"( + contract test { + function f() { + assembly { + 1 + } + } + } + )"; + BOOST_CHECK(expectError(text, true) == Error::Type::Warning); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_negative_stack) +{ + char const* text = R"( + contract test { + function f() { + assembly { + pop + } + } + } + )"; + BOOST_CHECK(expectError(text, true) == Error::Type::Warning); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) +{ + char const* text = R"( + contract test { + modifier m { + uint a = 1; + assembly { + a := 2 + } + _; + } + function f() m { + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_storage) +{ + char const* text = R"( + contract test { + uint x = 1; + function f() { + assembly { + x := 2 + } + } + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::DeclarationError); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) +{ + char const* text = R"( + contract test { + uint x = 1; + modifier m { + assembly { + x := 2 + } + _; + } + function f() m { + } + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::DeclarationError); +} + +BOOST_AUTO_TEST_CASE(invalid_mobile_type) +{ + char const* text = R"( + contract C { + function f() { + // Invalid number + [1, 78901234567890123456789012345678901234567890123456789345678901234567890012345678012345678901234567]; + } + } + )"; + BOOST_CHECK(expectError(text, false) == Error::Type::TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 206f23fb..562b7859 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -805,7 +805,7 @@ BOOST_AUTO_TEST_CASE(cse_empty_sha3) Instruction::SHA3 }; checkCSE(input, { - u256(sha3(bytesConstRef())) + u256(dev::keccak256(bytesConstRef())) }); } @@ -823,7 +823,7 @@ BOOST_AUTO_TEST_CASE(cse_partial_sha3) u256(0xabcd) << (256 - 16), u256(0), Instruction::MSTORE, - u256(sha3(bytes{0xab, 0xcd})) + u256(dev::keccak256(bytes{0xab, 0xcd})) }); } |