Merge pull request #5836 from ethereum/develop

Merge develop into release for 0.5.3.
This commit is contained in:
chriseth 2019-01-22 13:49:41 +01:00 committed by GitHub
commit 10d17f2458
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
388 changed files with 10087 additions and 5735 deletions

View File

@ -34,14 +34,14 @@ version: 2
jobs:
build_emscripten:
docker:
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
- image: trzeci/emscripten:sdk-tag-1.38.22-64bit
environment:
TERM: xterm
steps:
- checkout
- restore_cache:
name: Restore Boost build
key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }}
key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/build_emscripten.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }}
- run:
name: Bootstrap Boost
command: |
@ -54,7 +54,7 @@ jobs:
name: Save Boost build
key: *boost-cache-key
paths:
- boost_1_67_0
- boost_1_68_0
- store_artifacts:
path: build/libsolc/soljson.js
destination: soljson.js
@ -104,7 +104,7 @@ jobs:
test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js
build_x86_linux:
docker:
- image: buildpack-deps:artful
- image: buildpack-deps:bionic
environment:
TERM: xterm
COVERAGE: "ON"
@ -212,7 +212,7 @@ jobs:
test_check_style:
docker:
- image: buildpack-deps:artful
- image: buildpack-deps:bionic
steps:
- checkout
- run:
@ -238,7 +238,7 @@ jobs:
test_x86_linux:
docker:
- image: buildpack-deps:artful
- image: buildpack-deps:bionic
environment:
TERM: xterm
steps:
@ -315,7 +315,9 @@ jobs:
docs:
docker:
- image: buildpack-deps:artful
- image: buildpack-deps:bionic
environment:
DEBIAN_FRONTEND: noninteractive
steps:
- checkout
- run:

View File

@ -114,7 +114,7 @@ matrix:
before_install:
- nvm install 8
- nvm use 8
- docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit
- docker pull trzeci/emscripten:sdk-tag-1.38.22-64bit
env:
- SOLC_EMSCRIPTEN=On
- SOLC_INSTALL_DEPS_TRAVIS=Off
@ -122,6 +122,16 @@ matrix:
- SOLC_TESTS=Off
- ZIP_SUFFIX=emscripten
- SOLC_STOREBYTECODE=On
# Travis doesn't seem to support "dynamic" cache keys where we could include
# the hashes of certain files. Our CircleCI configuration contains the hash of
# relevant emscripten files.
#
# It is important to invalidate the cache with each emscripten update, because
# dependencies, such as boost, might be broken otherwise.
#
# This key here has no significant on anything, apart from caching. Please keep
# it in sync with the version above.
- EMSCRIPTEN_VERSION_KEY="1.38.22"
# OS X Mavericks (10.9)
# https://en.wikipedia.org/wiki/OS_X_Mavericks
@ -177,7 +187,7 @@ git:
cache:
ccache: true
directories:
- boost_1_67_0
- boost_1_68_0
- $HOME/.local
install:

View File

@ -3,13 +3,15 @@ cmake_minimum_required(VERSION 3.0.0)
set(ETH_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake" CACHE PATH "The the path to the cmake directory")
list(APPEND CMAKE_MODULE_PATH ${ETH_CMAKE_DIR})
include(EthToolchains)
# Set cmake_policies
include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
set(PROJECT_VERSION "0.5.2")
project(solidity VERSION ${PROJECT_VERSION})
set(PROJECT_VERSION "0.5.3")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES CXX)
option(LLL "Build LLL" OFF)
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)

View File

@ -1,3 +1,28 @@
### 0.5.3 (2019-01-22)
Language Features:
* Provide access to creation and runtime code of contracts via ``type(C).creationCode`` / ``type(C).runtimeCode``.
Compiler Features:
* Control Flow Graph: Warn about unreachable code.
* SMTChecker: Support basic typecasts without truncation.
* SMTChecker: Support external function calls and erase all knowledge regarding storage variables and references.
Bugfixes:
* Emscripten: Split simplification rule initialization up further to work around issues with soljson.js in some browsers.
* Type Checker: Disallow calldata structs until implemented.
* Type Checker: Return type error if fixed point encoding is attempted instead of throwing ``UnimplementedFeatureError``.
* Yul: Check that arguments to ``dataoffset`` and ``datasize`` are literals at parse time and properly take this into account in the optimizer.
* Yul: Parse number literals for detecting duplicate switch cases.
* Yul: Require switch cases to have the same type.
Build System:
* Emscripten: Upgrade to emscripten 1.38.8 on travis and circleci.
### 0.5.2 (2018-12-19)
Language Features:

View File

@ -19,6 +19,8 @@ that run on the Ethereum Virtual Machine. Smart contracts are programs that are
network where nobody has special authority over the execution and thus they allow to implement tokens of value,
ownership, voting and other kinds of logics.
When deploying contracts, you should use the latest released version of Solidity. This is because breaking changes as well as new features and bug fixes are introduced regularly. We currently use a 0.x version number [to indicate this fast pace of change](https://semver.org/#spec-item-4).
## Build and Install
Instructions about how to build and install the Solidity compiler can be found in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source)
@ -29,7 +31,7 @@ Instructions about how to build and install the Solidity compiler can be found i
A "Hello World" program in Solidity is of even less use than in other languages, but still:
```
pragma solidity ^0.4.16;
pragma solidity ^0.5.0;
contract HelloWorld {
function helloWorld() external pure returns (string memory) {

View File

@ -4,13 +4,15 @@ Checklist for making a release:
- [ ] Check that all issues and pull requests from the Github project to be released are merged to ``develop``.
- [ ] Create a commit in ``develop`` that updates the ``Changelog`` to include a release date (run ``./scripts/tests.sh`` to update the bug list). Sort the changelog entries alphabetically and correct any errors you notice.
- [ ] Create a pull request and wait for the tests, merge it.
- [ ] Create a pull request from ``develop`` to ``release``, wait for the tests, then merge it.
- [ ] Make a final check that there are no platform-dependency issues in the ``solc-test-bytecode`` repository.
- [ ] Wait for the tests for the commit on ``release``, create a release in Github, creating the tag.
- [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``).
- [ ] Create a pull request from ``develop`` to ``release``, wait for the tests, then merge it.
- [ ] Make a final check that there are no platform-dependency issues in the ``solidity-test-bytecode`` repository.
- [ ] Wait for the tests for the commit on ``release``, create a release in Github, creating the tag.
- [ ] Wait for the CI runs on the tag itself (they should push artifacts onto the Github release page).
- [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball.
- [ ] Upload the source tarball (in the upload directory) to the release page.
- [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key).
- [ ] Once the ``~ethereum/ubuntu/ethereum-static`` PPA build is finished and published for all platforms (make sure not to do this earlier), copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` while selecting ``Copy existing binaries``.
- [ ] Once the ``~ethereum/ubuntu/ethereum-static`` PPA build is finished and published for all platforms (make sure not to do this earlier), copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``.
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh release``).
- [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash)
- [ ] Update the default version on readthedocs.

View File

@ -24,10 +24,6 @@ endif()
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
# Use ISO C++14 standard language.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
# Enables all the warnings about constructions that some users consider questionable,
# and that are easy to avoid. Also enable some extra warning flags that are not
# enabled by -Wall. Finally, treat at warnings-as-errors, which forces developers
@ -78,10 +74,8 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
# into errors, which makes sense.
# http://stackoverflow.com/questions/21617158/how-to-silence-unused-command-line-argument-error-with-clang-without-disabling-i
add_compile_options(-Qunused-arguments)
endif()
if (EMSCRIPTEN)
# Do not emit a separate memory initialiser file
elseif(EMSCRIPTEN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0")
# Leave only exported symbols as public and aggressively remove others
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections -fvisibility=hidden")
@ -104,7 +98,13 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
# Abort if linking results in any undefined symbols
# Note: this is on by default in the CMake Emscripten module which we aren't using
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
add_definitions(-DETH_EMSCRIPTEN=1)
# Disallow deprecated emscripten build options.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1")
# Export the Emscripten-generated auxiliary methods which are needed by solc-js.
# Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','Pointer_stringify','lengthBytesUTF8','_malloc','stringToUTF8','setValue']")
# Do not build as a WebAssembly target - we need an asm.js output.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0")
endif()
endif()
@ -166,9 +166,8 @@ option(USE_CVC4 "Allow compiling with CVC4 SMT solver integration" ON)
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
option(USE_LD_GOLD "Use GNU gold linker" ON)
if (USE_LD_GOLD)
execute_process(COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION)
if ("${LD_VERSION}" MATCHES "GNU gold")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=gold")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=gold")
endif ()
endif ()

View File

@ -5,6 +5,9 @@ function(eth_show_dependency DEP NAME)
get_property(DISPLAYED GLOBAL PROPERTY ETH_${DEP}_DISPLAYED)
if (NOT DISPLAYED)
set_property(GLOBAL PROPERTY ETH_${DEP}_DISPLAYED TRUE)
if (NOT("${${DEP}_VERSION}" STREQUAL ""))
message(STATUS "${NAME} version: ${${DEP}_VERSION}")
endif()
message(STATUS "${NAME} headers: ${${DEP}_INCLUDE_DIRS}")
message(STATUS "${NAME} lib : ${${DEP}_LIBRARIES}")
if (NOT("${${DEP}_DLLS}" STREQUAL ""))
@ -38,6 +41,6 @@ set(ETH_SCRIPTS_DIR ${ETH_CMAKE_DIR}/scripts)
set(Boost_USE_MULTITHREADED ON)
option(Boost_USE_STATIC_LIBS "Link Boost statically" ON)
find_package(Boost 1.54.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options system)
find_package(Boost 1.65.0 QUIET REQUIRED COMPONENTS regex filesystem unit_test_framework program_options system)
eth_show_dependency(Boost boost)

View File

@ -0,0 +1,8 @@
if(NOT CMAKE_TOOLCHAIN_FILE)
# Use default toolchain file if none is provided.
set(
CMAKE_TOOLCHAIN_FILE
"${CMAKE_CURRENT_LIST_DIR}/toolchains/default.cmake"
CACHE FILEPATH "The CMake toolchain file"
)
endif()

View File

@ -35,7 +35,6 @@ ExternalProject_Add(jsoncpp-project
URL_HASH SHA256=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6
CMAKE_COMMAND ${JSONCPP_CMAKE_COMMAND}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_INSTALL_LIBDIR=lib
# Build static lib but suitable to be included in a shared lib.

View File

@ -0,0 +1,4 @@
# Require C++14.
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_EXTENSIONS OFF)

View File

@ -0,0 +1,2 @@
include("${CMAKE_CURRENT_LIST_DIR}/default.cmake")
include("$ENV{EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake")

View File

@ -171,7 +171,7 @@ Command Line and JSON Interfaces
the first 36 hex characters of the keccak256 hash of the fully qualified
library name, surrounded by ``$...$``. Previously,
just the fully qualified library name was used.
This recudes the chances of collisions, especially when long paths are used.
This reduces the chances of collisions, especially when long paths are used.
Binary files now also contain a list of mappings from these placeholders
to the fully qualified names.
@ -308,7 +308,7 @@ This will no longer compile with Solidity v0.5.0. However, you can define a comp
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
interface OldContract {
function someOldFunction(uint8 a) external;
function anotherOldFunction() external returns (bool);
@ -325,7 +325,7 @@ Given the interface defined above, you can now easily use the already deployed p
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
interface OldContract {
function someOldFunction(uint8 a) external;
@ -345,7 +345,7 @@ commandline compiler for linking):
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
library OldLibrary {
function someFunction(uint8 a) public returns(bool);
@ -430,7 +430,7 @@ New version:
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract OtherContract {
uint x;

View File

@ -471,7 +471,7 @@ For example,
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract Test {
constructor() public { b = hex"12345678901234567890123456789012"; }
@ -597,7 +597,7 @@ Strict encoding mode is the mode that leads to exactly the same encoding as defi
This means offsets have to be as small as possible while still not creating overlaps in the data areas and thus no gaps are
allowed.
Usually, ABI decoders are written in a straigthforward way just following offset pointers, but some decoders
Usually, ABI decoders are written in a straightforward way just following offset pointers, but some decoders
might enforce strict mode. The Solidity ABI decoder currently does not enforce strict mode, but the encoder
always creates data in strict mode.
@ -609,22 +609,30 @@ Through ``abi.encodePacked()``, Solidity supports a non-standard packed mode whe
- types shorter than 32 bytes are neither zero padded nor sign extended and
- dynamic types are encoded in-place and without the length.
As an example encoding ``int8, bytes1, uint16, string`` with values ``-1, 0x42, 0x2424, "Hello, world!"`` results in:
This packed mode is mainly used for indexed event parameters.
As an example, the encoding of ``int16(-1), bytes1(0x42), uint16(0x03), string("Hello, world!")`` results in:
.. code-block:: none
0xff42242448656c6c6f2c20776f726c6421
^^ int8(-1)
^^ bytes1(0x42)
^^^^ uint16(0x2424)
^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field
0xffff42000348656c6c6f2c20776f726c6421
^^^^ int16(-1)
^^ bytes1(0x42)
^^^^ uint16(0x03)
^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field
More specifically, each statically-sized type takes as many bytes as its range has
and dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded without
their length field. This means that the encoding is ambiguous as soon as there are two
dynamically-sized elements.
More specifically:
- Each value type takes as many bytes as its range has.
- The encoding of a struct or fixed-size array is the concatenation of the
encoding of its members/elements without any separator or padding.
- Mapping members of structs are ignored as usual.
- Dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded without
their length field.
In general, the encoding is ambiguous as soon as there are two dynamically-sized elements,
because of the missing length field.
If padding is needed, explicit type conversions can be used: ``abi.encodePacked(uint16(0x12)) == hex"0012"``.
Since packed encoding is not used when calling functions, there is no special support
for prepending a function selector.
for prepending a function selector. Since the encoding is ambiguous, there is no decoding function.

View File

@ -43,7 +43,7 @@
{
"name": "DelegateCallReturnValue",
"summary": "The low-level .delegatecall() does not return the execution outcome, but converts the value returned by the functioned called to a boolean instead.",
"description": "The return value of the low-level .delegatecall() function is taken from a position in memory, where the call data or the return data resides. This value is interpreted as a boolean and put onto the stack. This means if the called function returns at least 32 zero bytes, .delegatecall() returns false even if the call was successuful.",
"description": "The return value of the low-level .delegatecall() function is taken from a position in memory, where the call data or the return data resides. This value is interpreted as a boolean and put onto the stack. This means if the called function returns at least 32 zero bytes, .delegatecall() returns false even if the call was successful.",
"introduced": "0.3.0",
"fixed": "0.4.15",
"severity": "low"

View File

@ -620,5 +620,9 @@
"0.5.2": {
"bugs": [],
"released": "2018-12-19"
},
"0.5.3": {
"bugs": [],
"released": "2019-01-22"
}
}

View File

@ -28,7 +28,7 @@ become the new richest.
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract WithdrawalContract {
address public richest;
@ -65,7 +65,7 @@ This is as opposed to the more intuitive sending pattern:
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract SendContract {
address payable public richest;
@ -117,7 +117,7 @@ to read the data, so will everyone else.
You can restrict read access to your contract's state
by **other contracts**. That is actually the default
unless you declare make your state variables ``public``.
unless you declare your state variables ``public``.
Furthermore, you can restrict who can make modifications
to your contract's state or call your contract's

View File

@ -53,7 +53,7 @@ master_doc = 'index'
# General information about the project.
project = 'Solidity'
copyright = '2016-2018, Ethereum'
copyright = '2016-2019, Ethereum'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -81,7 +81,7 @@ else:
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ['_build', 'contracts', 'types', 'examples']
# The reST default role (used for this markup: `text`) to use for all
# documents.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
.. index:: ! contract;abstract, ! abstract contract
.. _abstract-contract:
******************
Abstract Contracts
******************
Contracts are marked as abstract when at least one of their functions lacks an implementation as in the following example (note that the function declaration header is terminated by ``;``)::
pragma solidity >=0.4.0 <0.6.0;
contract Feline {
function utterance() public returns (bytes32);
}
Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts::
pragma solidity >=0.4.0 <0.6.0;
contract Feline {
function utterance() public returns (bytes32);
}
contract Cat is Feline {
function utterance() public returns (bytes32) { return "miaow"; }
}
If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract.
Note that a function without implementation is different from a :ref:`Function Type <function_types>` even though their syntax looks very similar.
Example of function without implementation (a function declaration)::
function foo(address) external returns (address);
Example of a Function Type (a variable declaration, where the variable is of type ``function``)::
function(address) external returns (address) foo;
Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and
facilitating patterns like the `Template method <https://en.wikipedia.org/wiki/Template_method_pattern>`_ and removing code duplication.
Abstract contracts are useful in the same way that defining methods in an interface is useful. It is a way for the designer of the abstract contract to say "any child of mine must implement this method".

View File

@ -0,0 +1,35 @@
.. index:: ! constant
************************
Constant State Variables
************************
State variables can be declared as ``constant``. In this case, they have to be
assigned from an expression which is a constant at compile time. Any expression
that accesses storage, blockchain data (e.g. ``now``, ``address(this).balance`` or
``block.number``) or
execution data (``msg.value`` or ``gasleft()``) or makes calls to external contracts is disallowed. Expressions
that might have a side-effect on memory allocation are allowed, but those that
might have a side-effect on other memory objects are not. The built-in functions
``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod``
are allowed (even though, with the exception of ``keccak256``, they do call external contracts).
The reason behind allowing side-effects on the memory allocator is that it
should be possible to construct complex objects like e.g. lookup-tables.
This feature is not yet fully usable.
The compiler does not reserve a storage slot for these variables, and every occurrence is
replaced by the respective constant expression (which might be computed to a single value by the optimizer).
Not all types for constants are implemented at this time. The only supported types are
value types and strings.
::
pragma solidity >=0.4.0 <0.6.0;
contract C {
uint constant x = 32**22 + 8;
string constant text = "abc";
bytes32 constant myHash = keccak256("abc");
}

View File

@ -0,0 +1,117 @@
.. index:: ! contract;creation, constructor
******************
Creating Contracts
******************
Contracts can be created "from outside" via Ethereum transactions or from within Solidity contracts.
IDEs, such as `Remix <https://remix.ethereum.org/>`_, make the creation process seamless using UI elements.
Creating contracts programmatically on Ethereum is best done via using the JavaScript API `web3.js <https://github.com/ethereum/web3.js>`_.
It has a function called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_
to facilitate contract creation.
When a contract is created, its :ref:`constructor <constructor>` (a function declared with the ``constructor`` keyword) is executed once.
A constructor is optional. Only one constructor is allowed, which means
overloading is not supported.
After the constructor has executed, the final code of the contract is deployed to the
blockchain. This code includes all public and external functions and all functions
that are reachable from there through function calls. The deployed code does not
include the constructor code or internal functions only called from the constructor.
.. index:: constructor;arguments
Internally, constructor arguments are passed :ref:`ABI encoded <ABI>` after the code of
the contract itself, but you do not have to care about this if you use ``web3.js``.
If a contract wants to create another contract, the source code
(and the binary) of the created contract has to be known to the creator.
This means that cyclic creation dependencies are impossible.
::
pragma solidity >=0.4.22 <0.6.0;
contract OwnedToken {
// `TokenCreator` is a contract type that is defined below.
// It is fine to reference it as long as it is not used
// to create a new contract.
TokenCreator creator;
address owner;
bytes32 name;
// This is the constructor which registers the
// creator and the assigned name.
constructor(bytes32 _name) public {
// State variables are accessed via their name
// and not via e.g. `this.owner`. Functions can
// be accessed directly or through `this.f`,
// but the latter provides an external view
// to the function. Especially in the constructor,
// you should not access functions externally,
// because the function does not exist yet.
// See the next section for details.
owner = msg.sender;
// We do an explicit type conversion from `address`
// to `TokenCreator` and assume that the type of
// the calling contract is `TokenCreator`, there is
// no real way to check that.
creator = TokenCreator(msg.sender);
name = _name;
}
function changeName(bytes32 newName) public {
// Only the creator can alter the name --
// the comparison is possible since contracts
// are explicitly convertible to addresses.
if (msg.sender == address(creator))
name = newName;
}
function transfer(address newOwner) public {
// Only the current owner can transfer the token.
if (msg.sender != owner) return;
// We ask the creator contract if the transfer
// should proceed by using a function of the
// `TokenCreator` contract defined below. If
// the call fails (e.g. due to out-of-gas),
// the execution also fails here.
if (creator.isTokenTransferOK(owner, newOwner))
owner = newOwner;
}
}
contract TokenCreator {
function createToken(bytes32 name)
public
returns (OwnedToken tokenAddress)
{
// Create a new `Token` contract and return its address.
// From the JavaScript side, the return type is
// `address`, as this is the closest type available in
// the ABI.
return new OwnedToken(name);
}
function changeName(OwnedToken tokenAddress, bytes32 name) public {
// Again, the external type of `tokenAddress` is
// simply `address`.
tokenAddress.changeName(name);
}
// Perform checks to determine if transferring a token to the
// `OwnedToken` contract should proceed
function isTokenTransferOK(address currentOwner, address newOwner)
public
pure
returns (bool ok)
{
// Check an arbitrary condition to see if transfer should proceed
return keccak256(abi.encodePacked(currentOwner, newOwner))[0] == 0x7f;
}
}

161
docs/contracts/events.rst Normal file
View File

@ -0,0 +1,161 @@
.. index:: ! event
.. _events:
******
Events
******
Solidity events give an abstraction on top of the EVM's logging functionality.
Applications can subscribe and listen to these events through the RPC interface of an Ethereum client.
Events are inheritable members of contracts. When you call them, they cause the
arguments to be stored in the transaction's log - a special data structure
in the blockchain. These logs are associated with the address of the contract,
are incorporated into the blockchain, and stay there as long as a block is
accessible (forever as of the Frontier and Homestead releases, but this might
change with Serenity). The Log and its event data is not accessible from within
contracts (not even from the contract that created them).
It is possible to request a simple payment verification (SPV) for logs, so if
an external entity supplies a contract with such a verification, it can check
that the log actually exists inside the blockchain. You have to supply block headers
because the contract can only see the last 256 block hashes.
You can add the attribute ``indexed`` to up to three parameters which adds them
to a special data structure known as :ref:`"topics" <abi_events>` instead of
the data part of the log. If you use arrays (including ``string`` and ``bytes``)
as indexed arguments, its Keccak-256 hash is stored as a topic instead, this is
because a topic can only hold a single word (32 bytes).
All parameters without the ``indexed`` attribute are :ref:`ABI-encoded <ABI>`
into the data part of the log.
Topics allow you to search for events, for example when filtering a sequence of
blocks for certain events. You can also filter events by the address of the
contract that emitted the event.
For example, the code below uses the web3.js ``subscribe("logs")``
`method <https://web3js.readthedocs.io/en/1.0/web3-eth-subscribe.html#subscribe-logs>`_ to filter
logs that match a topic with a certain address value:
.. code-block:: javascript
var options = {
fromBlock: 0,
address: web3.eth.defaultAccount,
topics: ["0x0000000000000000000000000000000000000000000000000000000000000000", null, null]
};
web3.eth.subscribe('logs', options, function (error, result) {
if (!error)
console.log(result);
})
.on("data", function (log) {
console.log(log);
})
.on("changed", function (log) {
});
The hash of the signature of the event is one of the topics, except if you
declared the event with the ``anonymous`` specifier. This means that it is
not possible to filter for specific anonymous events by name.
::
pragma solidity >=0.4.21 <0.6.0;
contract ClientReceipt {
event Deposit(
address indexed _from,
bytes32 indexed _id,
uint _value
);
function deposit(bytes32 _id) public payable {
// Events are emitted using `emit`, followed by
// the name of the event and the arguments
// (if any) in parentheses. Any such invocation
// (even deeply nested) can be detected from
// the JavaScript API by filtering for `Deposit`.
emit Deposit(msg.sender, _id, msg.value);
}
}
The use in the JavaScript API is as follows:
::
var abi = /* abi as generated by the compiler */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceipt = ClientReceipt.at("0x1234...ab67" /* address */);
var event = clientReceipt.Deposit();
// watch for changes
event.watch(function(error, result){
// result contains non-indexed arguments and topics
// given to the `Deposit` call.
if (!error)
console.log(result);
});
// Or pass a callback to start watching immediately
var event = clientReceipt.Deposit(function(error, result) {
if (!error)
console.log(result);
});
The output of the above looks like the following (trimmed):
.. code-block:: json
{
"returnValues": {
"_from": "0x1111…FFFFCCCC",
"_id": "0x50…sd5adb20",
"_value": "0x420042"
},
"raw": {
"data": "0x7f…91385",
"topics": ["0xfd4…b4ead7", "0x7f…1a91385"]
}
}
.. index:: ! log
Low-Level Interface to Logs
===========================
It is also possible to access the low-level interface to the logging
mechanism via the functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4``.
``logi`` takes ``i + 1`` parameter of type ``bytes32``, where the first
argument will be used for the data part of the log and the others
as topics. The event call above can be performed in the same way as
::
pragma solidity >=0.4.10 <0.6.0;
contract C {
function f() public payable {
uint256 _id = 0x420042;
log3(
bytes32(msg.value),
bytes32(0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20),
bytes32(uint256(msg.sender)),
bytes32(_id)
);
}
}
where the long hexadecimal number is equal to
``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event.
Additional Resources for Understanding Events
==============================================
- `Javascript documentation <https://github.com/ethereum/wiki/wiki/JavaScript-API#contract-events>`_
- `Example usage of events <https://github.com/debris/smart-exchange/blob/master/lib/contracts/SmartExchange.sol>`_
- `How to access them in js <https://github.com/debris/smart-exchange/blob/master/lib/exchange_transactions.js>`_

View File

@ -0,0 +1,111 @@
.. index:: ! function;modifier
.. _modifiers:
******************
Function Modifiers
******************
Modifiers can be used to easily change the behaviour of functions. For example,
they can automatically check a condition prior to executing the function. Modifiers are
inheritable properties of contracts and may be overridden by derived contracts.
::
pragma solidity ^0.5.0;
contract owned {
constructor() public { owner = msg.sender; }
address payable owner;
// This contract only defines a modifier but does not use
// it: it will be used in derived contracts.
// The function body is inserted where the special symbol
// `_;` in the definition of a modifier appears.
// This means that if the owner calls this function, the
// function is executed and otherwise, an exception is
// thrown.
modifier onlyOwner {
require(
msg.sender == owner,
"Only owner can call this function."
);
_;
}
}
contract mortal is owned {
// This contract inherits the `onlyOwner` modifier from
// `owned` and applies it to the `close` function, which
// causes that calls to `close` only have an effect if
// they are made by the stored owner.
function close() public onlyOwner {
selfdestruct(owner);
}
}
contract priced {
// Modifiers can receive arguments:
modifier costs(uint price) {
if (msg.value >= price) {
_;
}
}
}
contract Register is priced, owned {
mapping (address => bool) registeredAddresses;
uint price;
constructor(uint initialPrice) public { price = initialPrice; }
// It is important to also provide the
// `payable` keyword here, otherwise the function will
// automatically reject all Ether sent to it.
function register() public payable costs(price) {
registeredAddresses[msg.sender] = true;
}
function changePrice(uint _price) public onlyOwner {
price = _price;
}
}
contract Mutex {
bool locked;
modifier noReentrancy() {
require(
!locked,
"Reentrant call."
);
locked = true;
_;
locked = false;
}
/// This function is protected by a mutex, which means that
/// reentrant calls from within `msg.sender.call` cannot call `f` again.
/// The `return 7` statement assigns 7 to the return value but still
/// executes the statement `locked = false` in the modifier.
function f() public noReentrancy returns (uint) {
(bool success,) = msg.sender.call("");
require(success);
return 7;
}
}
Multiple modifiers are applied to a function by specifying them in a
whitespace-separated list and are evaluated in the order presented.
.. warning::
In an earlier version of Solidity, ``return`` statements in functions
having modifiers behaved differently.
Explicit returns from a modifier or function body only leave the current
modifier or function body. Return variables are assigned and
control flow continues after the "_" in the preceding modifier.
Arbitrary expressions are allowed for modifier arguments and in this context,
all symbols visible from the function are visible in the modifier. Symbols
introduced in the modifier are not visible in the function (as they might
change by overriding).

View File

@ -0,0 +1,398 @@
.. index:: ! functions
.. _functions:
*********
Functions
*********
.. _function-parameters-return-variables:
Function Parameters and Return Variables
========================================
As in JavaScript, functions may take parameters as input. Unlike in JavaScript
and C, functions may also return an arbitrary number of values as output.
Function Parameters
-------------------
Function parameters are declared the same way as variables, and the name of
unused parameters can be omitted.
For example, if you want your contract to accept one kind of external call
with two integers, you would use something like::
pragma solidity >=0.4.16 <0.6.0;
contract Simple {
uint sum;
function taker(uint _a, uint _b) public {
sum = _a + _b;
}
}
Function parameters can be used as any other local variable and they can also be assigned to.
.. note::
An :ref:`external function<external-function-calls>` cannot accept a
multi-dimensional array as an input
parameter. This functionality is possible if you enable the new
experimental ``ABIEncoderV2`` feature by adding ``pragma experimental ABIEncoderV2;`` to your source file.
An :ref:`internal function<external-function-calls>` can accept a
multi-dimensional array without enabling the feature.
.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct
Return Variables
----------------
Function return variables are declared with the same syntax after the
``returns`` keyword.
For example, suppose you want to return two results: the sum and the product of
two integers passed as function parameters, then you use something like::
pragma solidity >=0.4.16 <0.6.0;
contract Simple {
function arithmetic(uint _a, uint _b)
public
pure
returns (uint o_sum, uint o_product)
{
o_sum = _a + _b;
o_product = _a * _b;
}
}
The names of return variables can be omitted.
Return variables can be used as any other local variable and they
are initialized with their :ref:`default value <default-value>` and have that value unless explicitly set.
You can either explicitly assign to return variables and
then leave the function using ``return;``,
or you can provide return values
(either a single or :ref:`multiple ones<multi-return>`) directly with the ``return``
statement::
pragma solidity >=0.4.16 <0.6.0;
contract Simple {
function arithmetic(uint _a, uint _b)
public
pure
returns (uint o_sum, uint o_product)
{
return (_a + _b, _a * _b);
}
}
This form is equivalent to first assigning values to the
return variables and then using ``return;`` to leave the function.
.. note::
You cannot return some types from non-internal functions, notably
multi-dimensional dynamic arrays and structs. If you enable the
new experimental ``ABIEncoderV2`` feature by adding ``pragma experimental
ABIEncoderV2;`` to your source file then more types are available, but
``mapping`` types are still limited to inside a single contract and you
cannot transfer them.
.. _multi-return:
Returning Multiple Values
-------------------------
When a function has multiple return types, the statement ``return (v0, v1, ..., vn)`` can be used to return multiple values.
The number of components must be the same as the number of return types.
.. index:: ! view function, function;view
.. _view-functions:
View Functions
==============
Functions can be declared ``view`` in which case they promise not to modify the state.
.. note::
If the compiler's EVM target is Byzantium or newer (default) the opcode
``STATICCALL`` is used for ``view`` functions which enforces the state
to stay unmodified as part of the EVM execution. For library ``view`` functions
``DELEGATECALL`` is used, because there is no combined ``DELEGATECALL`` and ``STATICCALL``.
This means library ``view`` functions do not have run-time checks that prevent state
modifications. This should not impact security negatively because library code is
usually known at compile-time and the static checker performs compile-time checks.
The following statements are considered modifying the state:
#. Writing to state variables.
#. :ref:`Emitting events <events>`.
#. :ref:`Creating other contracts <creating-contracts>`.
#. Using ``selfdestruct``.
#. Sending Ether via calls.
#. Calling any function not marked ``view`` or ``pure``.
#. Using low-level calls.
#. Using inline assembly that contains certain opcodes.
::
pragma solidity ^0.5.0;
contract C {
function f(uint a, uint b) public view returns (uint) {
return a * (b + 42) + now;
}
}
.. note::
``constant`` on functions used to be an alias to ``view``, but this was dropped in version 0.5.0.
.. note::
Getter methods are automatically marked ``view``.
.. note::
Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode
for ``view`` functions.
This enabled state modifications in ``view`` functions through the use of
invalid explicit type conversions.
By using ``STATICCALL`` for ``view`` functions, modifications to the
state are prevented on the level of the EVM.
.. index:: ! pure function, function;pure
.. _pure-functions:
Pure Functions
==============
Functions can be declared ``pure`` in which case they promise not to read from or modify the state.
.. note::
If the compiler's EVM target is Byzantium or newer (default) the opcode ``STATICCALL`` is used,
which does not guarantee that the state is not read, but at least that it is not modified.
In addition to the list of state modifying statements explained above, the following are considered reading from the state:
#. Reading from state variables.
#. Accessing ``address(this).balance`` or ``<address>.balance``.
#. Accessing any of the members of ``block``, ``tx``, ``msg`` (with the exception of ``msg.sig`` and ``msg.data``).
#. Calling any function not marked ``pure``.
#. Using inline assembly that contains certain opcodes.
::
pragma solidity ^0.5.0;
contract C {
function f(uint a, uint b) public pure returns (uint) {
return a * (b + 42);
}
}
Pure functions are able to use the `revert()` and `require()` functions to revert
potential state changes when an :ref:`error occurs <assert-and-require>`.
Reverting a state change is not considered a "state modification", as only changes to the
state made previously in code that did not have the ``view`` or ``pure`` restriction
are reverted and that code has the option to catch the ``revert`` and not pass it on.
This behaviour is also in line with the ``STATICCALL`` opcode.
.. warning::
It is not possible to prevent functions from reading the state at the level
of the EVM, it is only possible to prevent them from writing to the state
(i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not).
.. note::
Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode
for ``pure`` functions.
This enabled state modifications in ``pure`` functions through the use of
invalid explicit type conversions.
By using ``STATICCALL`` for ``pure`` functions, modifications to the
state are prevented on the level of the EVM.
.. note::
Prior to version 0.4.17 the compiler did not enforce that ``pure`` is not reading the state.
It is a compile-time type check, which can be circumvented doing invalid explicit conversions
between contract types, because the compiler can verify that the type of the contract does
not do state-changing operations, but it cannot check that the contract that will be called
at runtime is actually of that type.
.. index:: ! fallback function, function;fallback
.. _fallback-function:
Fallback Function
=================
A contract can have exactly one unnamed function. This function cannot have
arguments, cannot return anything and has to have ``external`` visibility.
It is executed on a call to the contract if none of the other
functions match the given function identifier (or if no data was supplied at
all).
Furthermore, this function is executed whenever the contract receives plain
Ether (without data). To receive Ether and add it to the total balance of the contract, the fallback function
must be marked ``payable``. If no such function exists, the contract cannot receive
Ether through regular transactions and throws an exception.
In the worst case, the fallback function can only rely on 2300 gas being
available (for example when `send` or `transfer` is used), leaving little
room to perform other operations except basic logging. The following operations
will consume more gas than the 2300 gas stipend:
- Writing to storage
- Creating a contract
- Calling an external function which consumes a large amount of gas
- Sending Ether
Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it.
.. note::
Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve
any payload supplied with the call.
.. warning::
The fallback function is also executed if the caller meant to call
a function that is not available. If you want to implement the fallback
function only to receive ether, you should add a check
like ``require(msg.data.length == 0)`` to prevent invalid calls.
.. warning::
Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``)
but do not define a fallback function
throw an exception, sending back the Ether (this was different
before Solidity v0.4.0). So if you want your contract to receive Ether,
you have to implement a payable fallback function.
.. warning::
A contract without a payable fallback function can receive Ether as a recipient of a `coinbase transaction` (aka `miner block reward`)
or as a destination of a ``selfdestruct``.
A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it.
It also means that ``address(this).balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the fallback function).
::
pragma solidity ^0.5.0;
contract Test {
// This function is called for all messages sent to
// this contract (there is no other function).
// Sending Ether to this contract will cause an exception,
// because the fallback function does not have the `payable`
// modifier.
function() external { x = 1; }
uint x;
}
// This contract keeps all Ether sent to it with no way
// to get it back.
contract Sink {
function() external payable { }
}
contract Caller {
function callTest(Test test) public returns (bool) {
(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
require(success);
// results in test.x becoming == 1.
// address(test) will not allow to call ``send`` directly, since ``test`` has no payable
// fallback function. It has to be converted to the ``address payable`` type via an
// intermediate conversion to ``uint160`` to even allow calling ``send`` on it.
address payable testPayable = address(uint160(address(test)));
// If someone sends ether to that contract,
// the transfer will fail, i.e. this returns false here.
return testPayable.send(2 ether);
}
}
.. index:: ! overload
.. _overload-function:
Function Overloading
====================
A contract can have multiple functions of the same name but with different parameter
types.
This process is called "overloading" and also applies to inherited functions.
The following example shows overloading of the function
``f`` in the scope of contract ``A``.
::
pragma solidity >=0.4.16 <0.6.0;
contract A {
function f(uint _in) public pure returns (uint out) {
out = _in;
}
function f(uint _in, bool _really) public pure returns (uint out) {
if (_really)
out = _in;
}
}
Overloaded functions are also present in the external interface. It is an error if two
externally visible functions differ by their Solidity types but not by their external types.
::
pragma solidity >=0.4.16 <0.6.0;
// This will not compile
contract A {
function f(B _in) public pure returns (B out) {
out = _in;
}
function f(address _in) public pure returns (address out) {
out = _in;
}
}
contract B {
}
Both ``f`` function overloads above end up accepting the address type for the ABI although
they are considered different inside Solidity.
Overload resolution and Argument matching
-----------------------------------------
Overloaded functions are selected by matching the function declarations in the current scope
to the arguments supplied in the function call. Functions are selected as overload candidates
if all arguments can be implicitly converted to the expected types. If there is not exactly one
candidate, resolution fails.
.. note::
Return parameters are not taken into account for overload resolution.
::
pragma solidity >=0.4.16 <0.6.0;
contract A {
function f(uint8 _in) public pure returns (uint8 out) {
out = _in;
}
function f(uint256 _in) public pure returns (uint256 out) {
out = _in;
}
}
Calling ``f(50)`` would create a type error since ``50`` can be implicitly converted both to ``uint8``
and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly
converted to ``uint8``.

View File

@ -0,0 +1,299 @@
.. index:: ! inheritance, ! base class, ! contract;base, ! deriving
***********
Inheritance
***********
Solidity supports multiple inheritance including polymorphism.
All function calls are virtual, which means that the most derived function
is called, except when the contract name is explicitly given or the
``super`` keyword is used.
When a contract inherits from other contracts, only a single
contract is created on the blockchain, and the code from all the base contracts
is compiled into the created contract.
The general inheritance system is very similar to
`Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_,
especially concerning multiple inheritance, but there are also
some :ref:`differences <multi-inheritance>`.
Details are given in the following example.
::
pragma solidity ^0.5.0;
contract owned {
constructor() public { owner = msg.sender; }
address payable owner;
}
// Use `is` to derive from another contract. Derived
// contracts can access all non-private members including
// internal functions and state variables. These cannot be
// accessed externally via `this`, though.
contract mortal is owned {
function kill() public {
if (msg.sender == owner) selfdestruct(owner);
}
}
// These abstract contracts are only provided to make the
// interface known to the compiler. Note the function
// without body. If a contract does not implement all
// functions it can only be used as an interface.
contract Config {
function lookup(uint id) public returns (address adr);
}
contract NameReg {
function register(bytes32 name) public;
function unregister() public;
}
// Multiple inheritance is possible. Note that `owned` is
// also a base class of `mortal`, yet there is only a single
// instance of `owned` (as for virtual inheritance in C++).
contract named is owned, mortal {
constructor(bytes32 name) public {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).register(name);
}
// Functions can be overridden by another function with the same name and
// the same number/types of inputs. If the overriding function has different
// types of output parameters, that causes an error.
// Both local and message-based function calls take these overrides
// into account.
function kill() public {
if (msg.sender == owner) {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).unregister();
// It is still possible to call a specific
// overridden function.
mortal.kill();
}
}
}
// If a constructor takes an argument, it needs to be
// provided in the header (or modifier-invocation-style at
// the constructor of the derived contract (see below)).
contract PriceFeed is owned, mortal, named("GoldFeed") {
function updateInfo(uint newInfo) public {
if (msg.sender == owner) info = newInfo;
}
function get() public view returns(uint r) { return info; }
uint info;
}
Note that above, we call ``mortal.kill()`` to "forward" the
destruction request. The way this is done is problematic, as
seen in the following example::
pragma solidity >=0.4.22 <0.6.0;
contract owned {
constructor() public { owner = msg.sender; }
address payable owner;
}
contract mortal is owned {
function kill() public {
if (msg.sender == owner) selfdestruct(owner);
}
}
contract Base1 is mortal {
function kill() public { /* do cleanup 1 */ mortal.kill(); }
}
contract Base2 is mortal {
function kill() public { /* do cleanup 2 */ mortal.kill(); }
}
contract Final is Base1, Base2 {
}
A call to ``Final.kill()`` will call ``Base2.kill`` as the most
derived override, but this function will bypass
``Base1.kill``, basically because it does not even know about
``Base1``. The way around this is to use ``super``::
pragma solidity >=0.4.22 <0.6.0;
contract owned {
constructor() public { owner = msg.sender; }
address payable owner;
}
contract mortal is owned {
function kill() public {
if (msg.sender == owner) selfdestruct(owner);
}
}
contract Base1 is mortal {
function kill() public { /* do cleanup 1 */ super.kill(); }
}
contract Base2 is mortal {
function kill() public { /* do cleanup 2 */ super.kill(); }
}
contract Final is Base1, Base2 {
}
If ``Base2`` calls a function of ``super``, it does not simply
call this function on one of its base contracts. Rather, it
calls this function on the next base contract in the final
inheritance graph, so it will call ``Base1.kill()`` (note that
the final inheritance sequence is -- starting with the most
derived contract: Final, Base2, Base1, mortal, owned).
The actual function that is called when using super is
not known in the context of the class where it is used,
although its type is known. This is similar for ordinary
virtual method lookup.
.. index:: ! constructor
.. _constructor:
Constructors
============
A constructor is an optional function declared with the ``constructor`` keyword
which is executed upon contract creation, and where you can run contract
initialisation code.
Before the constructor code is executed, state variables are initialised to
their specified value if you initialise them inline, or zero if you do not.
After the constructor has run, the final code of the contract is deployed
to the blockchain. The deployment of
the code costs additional gas linear to the length of the code.
This code includes all functions that are part of the public interface
and all functions that are reachable from there through function calls.
It does not include the constructor code or internal functions that are
only called from the constructor.
Constructor functions can be either ``public`` or ``internal``. If there is no
constructor, the contract will assume the default constructor, which is
equivalent to ``constructor() public {}``. For example:
::
pragma solidity ^0.5.0;
contract A {
uint public a;
constructor(uint _a) internal {
a = _a;
}
}
contract B is A(1) {
constructor() public {}
}
A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`.
.. warning ::
Prior to version 0.4.22, constructors were defined as functions with the same name as the contract.
This syntax was deprecated and is not allowed anymore in version 0.5.0.
.. index:: ! base;constructor
Arguments for Base Constructors
===============================
The constructors of all the base contracts will be called following the
linearization rules explained below. If the base constructors have arguments,
derived contracts need to specify all of them. This can be done in two ways::
pragma solidity >=0.4.22 <0.6.0;
contract Base {
uint x;
constructor(uint _x) public { x = _x; }
}
// Either directly specify in the inheritance list...
contract Derived1 is Base(7) {
constructor() public {}
}
// or through a "modifier" of the derived constructor.
contract Derived2 is Base {
constructor(uint _y) Base(_y * _y) public {}
}
One way is directly in the inheritance list (``is Base(7)``). The other is in
the way a modifier is invoked as part of
the derived constructor (``Base(_y * _y)``). The first way to
do it is more convenient if the constructor argument is a
constant and defines the behaviour of the contract or
describes it. The second way has to be used if the
constructor arguments of the base depend on those of the
derived contract. Arguments have to be given either in the
inheritance list or in modifier-style in the derived constructor.
Specifying arguments in both places is an error.
If a derived contract does not specify the arguments to all of its base
contracts' constructors, it will be abstract.
.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization
.. _multi-inheritance:
Multiple Inheritance and Linearization
======================================
Languages that allow multiple inheritance have to deal with
several problems. One is the `Diamond Problem <https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem>`_.
Solidity is similar to Python in that it uses "`C3 Linearization <https://en.wikipedia.org/wiki/C3_linearization>`_"
to force a specific order in the directed acyclic graph (DAG) of base classes. This
results in the desirable property of monotonicity but
disallows some inheritance graphs. Especially, the order in
which the base classes are given in the ``is`` directive is
important: You have to list the direct base contracts
in the order from "most base-like" to "most derived".
Note that this order is the reverse of the one used in Python.
Another simplifying way to explain this is that when a function is called that
is defined multiple times in different contracts, the given bases
are searched from right to left (left to right in Python) in a depth-first manner,
stopping at the first match. If a base contract has already been searched, it is skipped.
In the following code, Solidity will give the
error "Linearization of inheritance graph impossible".
::
pragma solidity >=0.4.0 <0.6.0;
contract X {}
contract A is X {}
// This will not compile
contract C is A, X {}
The reason for this is that ``C`` requests ``X`` to override ``A``
(by specifying ``A, X`` in this order), but ``A`` itself
requests to override ``X``, which is a contradiction that
cannot be resolved.
Inheriting Different Kinds of Members of the Same Name
======================================================
When the inheritance results in a contract with a function and a modifier of the same name, it is considered as an error.
This error is produced also by an event and a modifier of the same name, and a function and an event of the same name.
As an exception, a state variable getter can override a public function.

View File

@ -0,0 +1,36 @@
.. index:: ! contract;interface, ! interface contract
.. _interfaces:
**********
Interfaces
**********
Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions:
- They cannot inherit other contracts or interfaces.
- All declared functions must be external.
- They cannot declare a constructor.
- They cannot declare state variables.
Some of these restrictions might be lifted in the future.
Interfaces are basically limited to what the Contract ABI can represent, and the conversion between the ABI and
an interface should be possible without any information loss.
Interfaces are denoted by their own keyword:
::
pragma solidity ^0.5.0;
interface Token {
enum TokenType { Fungible, NonFungible }
struct Coin { string obverse; string reverse; }
function transfer(address recipient, uint amount) external;
}
Contracts can inherit interfaces as they would inherit other contracts.
Types defined inside interfaces and other contract-like structures
can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``.

View File

@ -0,0 +1,230 @@
.. index:: ! library, callcode, delegatecall
.. _libraries:
*********
Libraries
*********
Libraries are similar to contracts, but their purpose is that they are deployed
only once at a specific address and their code is reused using the ``DELEGATECALL``
(``CALLCODE`` until Homestead)
feature of the EVM. This means that if library functions are called, their code
is executed in the context of the calling contract, i.e. ``this`` points to the
calling contract, and especially the storage from the calling contract can be
accessed. As a library is an isolated piece of source code, it can only access
state variables of the calling contract if they are explicitly supplied (it
would have no way to name them, otherwise). Library functions can only be
called directly (i.e. without the use of ``DELEGATECALL``) if they do not modify
the state (i.e. if they are ``view`` or ``pure`` functions),
because libraries are assumed to be stateless. In particular, it is
not possible to destroy a library.
.. note::
Until version 0.4.20, it was possible to destroy libraries by
circumventing Solidity's type system. Starting from that version,
libraries contain a :ref:`mechanism<call-protection>` that
disallows state-modifying functions
to be called directly (i.e. without ``DELEGATECALL``).
Libraries can be seen as implicit base contracts of the contracts that use them.
They will not be explicitly visible in the inheritance hierarchy, but calls
to library functions look just like calls to functions of explicit base
contracts (``L.f()`` if ``L`` is the name of the library). Furthermore,
``internal`` functions of libraries are visible in all contracts, just as
if the library were a base contract. Of course, calls to internal functions
use the internal calling convention, which means that all internal types
can be passed and types :ref:`stored in memory <data-location>` will be passed by reference and not copied.
To realize this in the EVM, code of internal library functions
and all functions called from therein will at compile time be pulled into the calling
contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``.
.. index:: using for, set
The following example illustrates how to use libraries (but manual method
be sure to check out :ref:`using for <using-for>` for a
more advanced example to implement a set).
::
pragma solidity >=0.4.22 <0.6.0;
library Set {
// We define a new struct datatype that will be used to
// hold its data in the calling contract.
struct Data { mapping(uint => bool) flags; }
// Note that the first parameter is of type "storage
// reference" and thus only its storage address and not
// its contents is passed as part of the call. This is a
// special feature of library functions. It is idiomatic
// to call the first parameter `self`, if the function can
// be seen as a method of that object.
function insert(Data storage self, uint value)
public
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
public
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value)
public
view
returns (bool)
{
return self.flags[value];
}
}
contract C {
Set.Data knownValues;
function register(uint value) public {
// The library functions can be called without a
// specific instance of the library, since the
// "instance" will be the current contract.
require(Set.insert(knownValues, value));
}
// In this contract, we can also directly access knownValues.flags, if we want.
}
Of course, you do not have to follow this way to use
libraries: they can also be used without defining struct
data types. Functions also work without any storage
reference parameters, and they can have multiple storage reference
parameters and in any position.
The calls to ``Set.contains``, ``Set.insert`` and ``Set.remove``
are all compiled as calls (``DELEGATECALL``) to an external
contract/library. If you use libraries, be aware 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, because of the use of ``CALLCODE``, ``msg.sender`` and
``msg.value`` changed, though).
The following example shows how to use :ref:`types stored in memory <data-location>` and
internal functions in libraries in order to implement
custom types without the overhead of external function calls:
::
pragma solidity >=0.4.16 <0.6.0;
library BigInt {
struct bigint {
uint[] limbs;
}
function fromUint(uint x) internal pure returns (bigint memory r) {
r.limbs = new uint[](1);
r.limbs[0] = x;
}
function add(bigint memory _a, bigint memory _b) internal pure returns (bigint memory r) {
r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length));
uint carry = 0;
for (uint i = 0; i < r.limbs.length; ++i) {
uint a = limb(_a, i);
uint b = limb(_b, i);
r.limbs[i] = a + b + carry;
if (a + b < a || (a + b == uint(-1) && carry > 0))
carry = 1;
else
carry = 0;
}
if (carry > 0) {
// too bad, we have to add a limb
uint[] memory newLimbs = new uint[](r.limbs.length + 1);
uint i;
for (i = 0; i < r.limbs.length; ++i)
newLimbs[i] = r.limbs[i];
newLimbs[i] = carry;
r.limbs = newLimbs;
}
}
function limb(bigint memory _a, uint _limb) internal pure returns (uint) {
return _limb < _a.limbs.length ? _a.limbs[_limb] : 0;
}
function max(uint a, uint b) private pure returns (uint) {
return a > b ? a : b;
}
}
contract C {
using BigInt for BigInt.bigint;
function f() public pure {
BigInt.bigint memory x = BigInt.fromUint(7);
BigInt.bigint memory y = BigInt.fromUint(uint(-1));
BigInt.bigint memory z = x.add(y);
assert(z.limb(1) > 0);
}
}
As the compiler cannot know where the library will be
deployed at, these addresses have to be filled into the
final bytecode by a linker
(see :ref:`commandline-compiler` for how to use the
commandline compiler for linking). If the addresses are not
given as arguments to the compiler, the compiled hex code
will contain placeholders of the form ``__Set______`` (where
``Set`` is the name of the library). The address can be filled
manually by replacing all those 40 symbols by the hex
encoding of the address of the library contract.
.. note::
Manually linking libraries on the generated bytecode is discouraged, because
it is restricted to 36 characters.
You should ask the compiler to link the libraries at the time
a contract is compiled by either using
the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use
the standard-JSON interface to the compiler.
Restrictions for libraries in comparison to contracts:
- No state variables
- Cannot inherit nor be inherited
- Cannot receive Ether
(These might be lifted at a later point.)
.. _call-protection:
Call Protection For Libraries
=============================
As mentioned in the introduction, if a library's code is executed
using a ``CALL`` instead of a ``DELEGATECALL`` or ``CALLCODE``,
it will revert unless a ``view`` or ``pure`` function is called.
The EVM does not provide a direct way for a contract to detect
whether it was called using ``CALL`` or not, but a contract
can use the ``ADDRESS`` opcode to find out "where" it is
currently running. The generated code compares this address
to the address used at construction time to determine the mode
of calling.
More specifically, the runtime code of a library always starts
with a push instruction, which is a zero of 20 bytes at
compilation time. When the deploy code runs, this constant
is replaced in memory by the current address and this
modified code is stored in the contract. At runtime,
this causes the deploy time address to be the first
constant to be pushed onto the stack and the dispatcher
code compares the current address against this constant
for any non-view and non-pure function.

View File

@ -0,0 +1,119 @@
.. index:: ! using for, library
.. _using-for:
*********
Using For
*********
The directive ``using A for B;`` can be used to attach library
functions (from the library ``A``) to any type (``B``).
These functions will receive the object they are called on
as their first parameter (like the ``self`` variable in Python).
The effect of ``using A for *;`` is that the functions from
the library ``A`` are attached to *any* type.
In both situations, *all* functions in the library are attached,
even those where the type of the first parameter does not
match the type of the object. The type is checked at the
point the function is called and function overload
resolution is performed.
The ``using A for B;`` directive is active only within the current
contract, including within all of its functions, and has no effect
outside of the contract in which it is used. The directive
may only be used inside a contract, not inside any of its functions.
By including a library, its data types including library functions are
available without having to add further code.
Let us rewrite the set example from the
:ref:`libraries` in this way::
pragma solidity >=0.4.16 <0.6.0;
// This is the same code as before, just without comments
library Set {
struct Data { mapping(uint => bool) flags; }
function insert(Data storage self, uint value)
public
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
public
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value)
public
view
returns (bool)
{
return self.flags[value];
}
}
contract C {
using Set for Set.Data; // this is the crucial change
Set.Data knownValues;
function register(uint value) public {
// Here, all variables of type Set.Data have
// corresponding member functions.
// The following function call is identical to
// `Set.insert(knownValues, value)`
require(knownValues.insert(value));
}
}
It is also possible to extend elementary types in that way::
pragma solidity >=0.4.16 <0.6.0;
library Search {
function indexOf(uint[] storage self, uint value)
public
view
returns (uint)
{
for (uint i = 0; i < self.length; i++)
if (self[i] == value) return i;
return uint(-1);
}
}
contract C {
using Search for uint[];
uint[] data;
function append(uint value) public {
data.push(value);
}
function replace(uint _old, uint _new) public {
// This performs the library function call
uint index = data.indexOf(_old);
if (index == uint(-1))
data.push(_new);
else
data[index] = _new;
}
}
Note that all library calls are actual EVM function calls. This means that
if you pass memory or value types, a copy will be performed, even of the
``self`` variable. The only situation where no copy will be performed
is when storage reference variables are used.

View File

@ -0,0 +1,198 @@
.. index:: ! visibility, external, public, private, internal
.. _visibility-and-getters:
**********************
Visibility and Getters
**********************
Since Solidity knows two kinds of function calls (internal
ones that do not create an actual EVM call (also called
a "message call") and external
ones that do), there are four types of visibilities for
functions and state variables.
Functions have to be specified as being ``external``,
``public``, ``internal`` or ``private``.
For state variables, ``external`` is not possible.
``external``:
External functions are part of the contract interface,
which means they can be called from other contracts and
via transactions. An external function ``f`` cannot be called
internally (i.e. ``f()`` does not work, but ``this.f()`` works).
External functions are sometimes more efficient when
they receive large arrays of data.
``public``:
Public functions are part of the contract interface
and can be either called internally or via
messages. For public state variables, an automatic getter
function (see below) is generated.
``internal``:
Those functions and state variables can only be
accessed internally (i.e. from within the current contract
or contracts deriving from it), without using ``this``.
``private``:
Private functions and state variables are only
visible for the contract they are defined in and not in
derived contracts.
.. note::
Everything that is inside a contract is visible to
all observers external to the blockchain. Making something ``private``
only prevents other contracts from accessing and modifying
the information, but it will still be visible to the
whole world outside of the blockchain.
The visibility specifier is given after the type for
state variables and between parameter list and
return parameter list for functions.
::
pragma solidity >=0.4.16 <0.6.0;
contract C {
function f(uint a) private pure returns (uint b) { return a + 1; }
function setData(uint a) internal { data = a; }
uint public data;
}
In the following example, ``D``, can call ``c.getData()`` to retrieve the value of
``data`` in state storage, but is not able to call ``f``. Contract ``E`` is derived from
``C`` and, thus, can call ``compute``.
::
pragma solidity >=0.4.0 <0.6.0;
contract C {
uint private data;
function f(uint a) private pure returns(uint b) { return a + 1; }
function setData(uint a) public { data = a; }
function getData() public view returns(uint) { return data; }
function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
// This will not compile
contract D {
function readData() public {
C c = new C();
uint local = c.f(7); // error: member `f` is not visible
c.setData(3);
local = c.getData();
local = c.compute(3, 5); // error: member `compute` is not visible
}
}
contract E is C {
function g() public {
C c = new C();
uint val = compute(3, 5); // access to internal member (from derived to parent contract)
}
}
.. index:: ! getter;function, ! function;getter
.. _getter-functions:
Getter Functions
================
The compiler automatically creates getter functions for
all **public** state variables. For the contract given below, the compiler will
generate a function called ``data`` that does not take any
arguments and returns a ``uint``, the value of the state
variable ``data``. State variables can be initialized
when they are declared.
::
pragma solidity >=0.4.0 <0.6.0;
contract C {
uint public data = 42;
}
contract Caller {
C c = new C();
function f() public view returns (uint) {
return c.data();
}
}
The getter functions have external visibility. If the
symbol is accessed internally (i.e. without ``this.``),
it evaluates to a state variable. If it is accessed externally
(i.e. with ``this.``), it evaluates to a function.
::
pragma solidity >=0.4.0 <0.6.0;
contract C {
uint public data;
function x() public returns (uint) {
data = 3; // internal access
return this.data(); // external access
}
}
If you have a ``public`` state variable of array type, then you can only retrieve
single elements of the array via the generated getter function. This mechanism
exists to avoid high gas costs when returning an entire array. You can use
arguments to specify which individual element to return, for example
``data(0)``. If you want to return an entire array in one call, then you need
to write a function, for example:
::
pragma solidity >=0.4.0 <0.6.0;
contract arrayExample {
// public state variable
uint[] public myArray;
// Getter function generated by the compiler
/*
function myArray(uint i) returns (uint) {
return myArray[i];
}
*/
// function that returns entire array
function getArray() returns (uint[] memory) {
return myArray;
}
}
Now you can use ``getArray()`` to retrieve the entire array, instead of
``myArray(i)``, which returns a single element per call.
The next example is more complex:
::
pragma solidity >=0.4.0 <0.6.0;
contract Complex {
struct Data {
uint a;
bytes3 b;
mapping (uint => uint) map;
}
mapping (uint => mapping(bool => Data[])) public data;
}
It generates a function of the following form. The mapping in the struct is omitted
because there is no good way to provide the key for the mapping:
::
function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) {
a = data[arg1][arg2][arg3].a;
b = data[arg1][arg2][arg3].b;
}

View File

@ -112,7 +112,7 @@ For example, you could run the following command in your ``build`` folder:
cmake -DCMAKE_BUILD_TYPE=Debug ..
make
This will create symbols such that when you debug a test using the ``--debug`` flag, you will have acecess to functions and varialbes in which you can break or print with.
This will create symbols such that when you debug a test using the ``--debug`` flag, you will have access to functions and variables in which you can break or print with.
The script ``./scripts/tests.sh`` also runs commandline tests and compilation tests

View File

@ -168,7 +168,7 @@ is compiled so recursive creation-dependencies are not possible.
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract D {
uint public x;
@ -264,6 +264,31 @@ Complications for Arrays and Structs
The semantics of assignments are a bit more complicated for non-value types like arrays and structs.
Assigning *to* a state variable always creates an independent copy. On the other hand, assigning to a local variable creates an independent copy only for elementary types, i.e. static types that fit into 32 bytes. If structs or arrays (including ``bytes`` and ``string``) are assigned from a state variable to a local variable, the local variable holds a reference to the original state variable. A second assignment to the local variable does not modify the state but only changes the reference. Assignments to members (or elements) of the local variable *do* change the state.
In the example below the call to ``g(x)`` has no effect on ``x`` because it needs
to create an independent copy of the storage value in memory. However ``h(x)`` modifies ``x`` because a reference and
not a copy is passed.
::
pragma solidity >=0.4.16 <0.6.0;
contract C {
uint[20] x;
function f() public {
g(x);
h(x);
}
function g(uint[20] memory y) internal pure {
y[2] = 3;
}
function h(uint[20] storage y) internal {
y[3] = 4;
}
}
.. index:: ! scoping, declarations, default value
.. _default-value:
@ -291,7 +316,7 @@ the two variables have the same name but disjoint scopes.
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract C {
function minimalScoping() pure public {
{
@ -312,7 +337,7 @@ In any case, you will get a warning about the outer variable being shadowed.
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
// This will report a warning
contract C {
function f() pure public returns (uint) {
@ -332,7 +357,7 @@ In any case, you will get a warning about the outer variable being shadowed.
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
// This will not compile
contract C {
function f() pure public returns (uint) {
@ -379,7 +404,7 @@ a message string for ``require``, but not for ``assert``.
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract Sharer {
function sendHalf(address payable addr) public payable returns (uint balance) {
@ -425,7 +450,7 @@ The following example shows how an error string can be used together with revert
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract VendingMachine {
function buy(uint amount) public payable {

View File

@ -0,0 +1,339 @@
.. index:: auction;blind, auction;open, blind auction, open auction
*************
Blind Auction
*************
In this section, we will show how easy it is to create a
completely blind auction contract on Ethereum.
We will start with an open auction where everyone
can see the bids that are made and then extend this
contract into a blind auction where it is not
possible to see the actual bid until the bidding
period ends.
.. _simple_auction:
Simple Open Auction
===================
The general idea of the following simple auction contract
is that everyone can send their bids during
a bidding period. The bids already include sending
money / ether in order to bind the bidders to their
bid. If the highest bid is raised, the previously
highest bidder gets her money back.
After the end of the bidding period, the
contract has to be called manually for the
beneficiary to receive their money - contracts cannot
activate themselves.
::
pragma solidity >=0.4.22 <0.6.0;
contract SimpleAuction {
// Parameters of the auction. Times are either
// absolute unix timestamps (seconds since 1970-01-01)
// or time periods in seconds.
address payable public beneficiary;
uint public auctionEndTime;
// Current state of the auction.
address public highestBidder;
uint public highestBid;
// Allowed withdrawals of previous bids
mapping(address => uint) pendingReturns;
// Set to true at the end, disallows any change.
// By default initialized to `false`.
bool ended;
// Events that will be emitted on changes.
event HighestBidIncreased(address bidder, uint amount);
event AuctionEnded(address winner, uint amount);
// The following is a so-called natspec comment,
// recognizable by the three slashes.
// It will be shown when the user is asked to
// confirm a transaction.
/// Create a simple auction with `_biddingTime`
/// seconds bidding time on behalf of the
/// beneficiary address `_beneficiary`.
constructor(
uint _biddingTime,
address payable _beneficiary
) public {
beneficiary = _beneficiary;
auctionEndTime = now + _biddingTime;
}
/// Bid on the auction with the value sent
/// together with this transaction.
/// The value will only be refunded if the
/// auction is not won.
function bid() public payable {
// No arguments are necessary, all
// information is already part of
// the transaction. The keyword payable
// is required for the function to
// be able to receive Ether.
// Revert the call if the bidding
// period is over.
require(
now <= auctionEndTime,
"Auction already ended."
);
// If the bid is not higher, send the
// money back.
require(
msg.value > highestBid,
"There already is a higher bid."
);
if (highestBid != 0) {
// Sending back the money by simply using
// highestBidder.send(highestBid) is a security risk
// because it could execute an untrusted contract.
// It is always safer to let the recipients
// withdraw their money themselves.
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
emit HighestBidIncreased(msg.sender, msg.value);
}
/// Withdraw a bid that was overbid.
function withdraw() public returns (bool) {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
// It is important to set this to zero because the recipient
// can call this function again as part of the receiving call
// before `send` returns.
pendingReturns[msg.sender] = 0;
if (!msg.sender.send(amount)) {
// No need to call throw here, just reset the amount owing
pendingReturns[msg.sender] = amount;
return false;
}
}
return true;
}
/// End the auction and send the highest bid
/// to the beneficiary.
function auctionEnd() public {
// It is a good guideline to structure functions that interact
// with other contracts (i.e. they call functions or send Ether)
// into three phases:
// 1. checking conditions
// 2. performing actions (potentially changing conditions)
// 3. interacting with other contracts
// If these phases are mixed up, the other contract could call
// back into the current contract and modify the state or cause
// effects (ether payout) to be performed multiple times.
// If functions called internally include interaction with external
// contracts, they also have to be considered interaction with
// external contracts.
// 1. Conditions
require(now >= auctionEndTime, "Auction not yet ended.");
require(!ended, "auctionEnd has already been called.");
// 2. Effects
ended = true;
emit AuctionEnded(highestBidder, highestBid);
// 3. Interaction
beneficiary.transfer(highestBid);
}
}
Blind Auction
=============
The previous open auction is extended to a blind auction
in the following. The advantage of a blind auction is
that there is no time pressure towards the end of
the bidding period. Creating a blind auction on a
transparent computing platform might sound like a
contradiction, but cryptography comes to the rescue.
During the **bidding period**, a bidder does not
actually send her bid, but only a hashed version of it.
Since it is currently considered practically impossible
to find two (sufficiently long) values whose hash
values are equal, the bidder commits to the bid by that.
After the end of the bidding period, the bidders have
to reveal their bids: They send their values
unencrypted and the contract checks that the hash value
is the same as the one provided during the bidding period.
Another challenge is how to make the auction
**binding and blind** at the same time: The only way to
prevent the bidder from just not sending the money
after they won the auction is to make her send it
together with the bid. Since value transfers cannot
be blinded in Ethereum, anyone can see the value.
The following contract solves this problem by
accepting any value that is larger than the highest
bid. Since this can of course only be checked during
the reveal phase, some bids might be **invalid**, and
this is on purpose (it even provides an explicit
flag to place invalid bids with high value transfers):
Bidders can confuse competition by placing several
high or low invalid bids.
::
pragma solidity >0.4.23 <0.6.0;
contract BlindAuction {
struct Bid {
bytes32 blindedBid;
uint deposit;
}
address payable public beneficiary;
uint public biddingEnd;
uint public revealEnd;
bool public ended;
mapping(address => Bid[]) public bids;
address public highestBidder;
uint public highestBid;
// Allowed withdrawals of previous bids
mapping(address => uint) pendingReturns;
event AuctionEnded(address winner, uint highestBid);
/// Modifiers are a convenient way to validate inputs to
/// functions. `onlyBefore` is applied to `bid` below:
/// The new function body is the modifier's body where
/// `_` is replaced by the old function body.
modifier onlyBefore(uint _time) { require(now < _time); _; }
modifier onlyAfter(uint _time) { require(now > _time); _; }
constructor(
uint _biddingTime,
uint _revealTime,
address payable _beneficiary
) public {
beneficiary = _beneficiary;
biddingEnd = now + _biddingTime;
revealEnd = biddingEnd + _revealTime;
}
/// Place a blinded bid with `_blindedBid` =
/// keccak256(abi.encodePacked(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
/// ether sent together with the bid is at least "value" and
/// "fake" is not true. Setting "fake" to true and sending
/// not the exact amount are ways to hide the real bid but
/// still make the required deposit. The same address can
/// place multiple bids.
function bid(bytes32 _blindedBid)
public
payable
onlyBefore(biddingEnd)
{
bids[msg.sender].push(Bid({
blindedBid: _blindedBid,
deposit: msg.value
}));
}
/// Reveal your blinded bids. You will get a refund for all
/// correctly blinded invalid bids and for all bids except for
/// the totally highest.
function reveal(
uint[] memory _values,
bool[] memory _fake,
bytes32[] memory _secret
)
public
onlyAfter(biddingEnd)
onlyBefore(revealEnd)
{
uint length = bids[msg.sender].length;
require(_values.length == length);
require(_fake.length == length);
require(_secret.length == length);
uint refund;
for (uint i = 0; i < length; i++) {
Bid storage bidToCheck = bids[msg.sender][i];
(uint value, bool fake, bytes32 secret) =
(_values[i], _fake[i], _secret[i]);
if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) {
// Bid was not actually revealed.
// Do not refund deposit.
continue;
}
refund += bidToCheck.deposit;
if (!fake && bidToCheck.deposit >= value) {
if (placeBid(msg.sender, value))
refund -= value;
}
// Make it impossible for the sender to re-claim
// the same deposit.
bidToCheck.blindedBid = bytes32(0);
}
msg.sender.transfer(refund);
}
// This is an "internal" function which means that it
// can only be called from the contract itself (or from
// derived contracts).
function placeBid(address bidder, uint value) internal
returns (bool success)
{
if (value <= highestBid) {
return false;
}
if (highestBidder != address(0)) {
// Refund the previously highest bidder.
pendingReturns[highestBidder] += highestBid;
}
highestBid = value;
highestBidder = bidder;
return true;
}
/// Withdraw a bid that was overbid.
function withdraw() public {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
// It is important to set this to zero because the recipient
// can call this function again as part of the receiving call
// before `transfer` returns (see the remark above about
// conditions -> effects -> interaction).
pendingReturns[msg.sender] = 0;
msg.sender.transfer(amount);
}
}
/// End the auction and send the highest bid
/// to the beneficiary.
function auctionEnd()
public
onlyAfter(revealEnd)
{
require(!ended);
emit AuctionEnded(highestBidder, highestBid);
ended = true;
beneficiary.transfer(highestBid);
}
}

View File

@ -0,0 +1,429 @@
********************
Micropayment Channel
********************
In this section we will learn how to build an example implementation
of a payment channel. It uses cryptographic signatures to make
repeated transfers of Ether between the same parties secure, instantaneous, and
without transaction fees. For the example, we need to understand how to
sign and verify signatures, and setup the payment channel.
Creating and verifying signatures
=================================
Imagine Alice wants to send a quantity of Ether to Bob, i.e.
Alice is the sender and the Bob is the recipient.
Alice only needs to send cryptographically signed messages off-chain
(e.g. via email) to Bob and it is similar to writing checks.
Alice and Bob use signatures to authorise transactions, which is possible with smart contracts on Ethereum.
Alice will build a simple smart contract that lets her transmit Ether, but instead of calling a function herself
to initiate a payment, she will let Bob do that, and therefore pay the transaction fee.
The contract will work as follows:
1. Alice deploys the ``ReceiverPays`` contract, attaching enough Ether to cover the payments that will be made.
2. Alice authorises a payment by signing a message with their private key.
3. Alice sends the cryptographically signed message to Bob. The message does not need to be kept secret
(explained later), and the mechanism for sending it does not matter.
4. Bob claims their payment by presenting the signed message to the smart contract, it verifies the
authenticity of the message and then releases the funds.
Creating the signature
----------------------
Alice does not need to interact with the Ethereum network to sign the transaction, the process is completely offline.
In this tutorial, we will sign messages in the browser using `web3.js <https://github.com/ethereum/web3.js>`_ and `MetaMask <https://metamask.io>`_, using the method described in `EIP-762 <https://github.com/ethereum/EIPs/pull/712>`_,
as it provides a number of other security benefits.
::
/// Hashing first makes things easier
var hash = web3.utils.sha3("message to sign");
web3.eth.personal.sign(hash, web3.eth.defaultAccount, function () { console.log("Signed"); });
.. note::
The ``web3.eth.personal.sign`` prepends the length of the message to the signed data. Since we hash first, the message will always be exactly 32 bytes long, and thus this length prefix is always the same.
What to Sign
------------
For a contract that fulfils payments, the signed message must include:
1. The recipient's address.
2. The amount to be transferred.
3. Protection against replay attacks.
A replay attack is when a signed message is reused to claim authorization for
a second action.
To avoid replay attacks we use the same as in Ethereum transactions
themselves, a so-called nonce, which is the number of transactions sent by an
account.
The smart contract checks if a nonce is used multiple times.
Another type of replay attack can occur when the owner deploys a ``ReceiverPays`` smart contract, makes some payments, and then destroys the contract. Later, they decide to deploy the ``RecipientPays`` smart contract again, but the new contract does not know the nonces used in the previous deployment, so the attacker can use the old messages again.
Alice can protect against this attack by including the contract's address in the message, and only messages containing the contract's address itself will be accepted. You can find an example of this in the first two lines of the ``claimPayment()`` function of the full contract at the end of this section.
Packing arguments
-----------------
Now that we have identified what information to include in the signed message,
we are ready to put the message together, hash it, and sign it. For simplicity,
we concatenate the data. The `ethereumjs-abi <https://github.com/ethereumjs/ethereumjs-abi>`_
library provides a function called ``soliditySHA3`` that mimics the behaviour of
Solidity's ``keccak256`` function applied to arguments encoded using ``abi.encodePacked``.
Here is a JavaScript function that creates the proper signature for the ``ReceiverPays`` example:
::
// recipient is the address that should be paid.
// amount, in wei, specifies how much ether should be sent.
// nonce can be any unique number to prevent replay attacks
// contractAddress is used to prevent cross-contract replay attacks
function signPayment(recipient, amount, nonce, contractAddress, callback) {
var hash = "0x" + abi.soliditySHA3(
["address", "uint256", "uint256", "address"],
[recipient, amount, nonce, contractAddress]
).toString("hex");
web3.eth.personal.sign(hash, web3.eth.defaultAccount, callback);
}
Recovering the Message Signer in Solidity
-----------------------------------------
In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. Signatures in Ethereum include a third parameter called ``v``, that you can use to verify which account's private key was used to sign the message, and the transaction's sender. Solidity provides a built-in function `ecrecover <mathematical-and-cryptographic-functions>`_ that accepts a message along with the ``r``, ``s`` and ``v`` parameters and returns the address that was used to sign the message.
Extracting the Signature Parameters
-----------------------------------
Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into component parts is a mess, so we use `inline assembly <assembly>`_ to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section).
Computing the Message Hash
--------------------------
The smart contract needs to know exactly what parameters were signed, and so it
must recreate the message from the parameters and use that for signature verification.
The functions ``prefixed`` and ``recoverSigner`` do this in the ``claimPayment`` function.
The full contract
-----------------
::
pragma solidity >=0.4.24 <0.6.0;
contract ReceiverPays {
address owner = msg.sender;
mapping(uint256 => bool) usedNonces;
constructor() public payable {}
function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public {
require(!usedNonces[nonce]);
usedNonces[nonce] = true;
// this recreates the message that was signed on the client
bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this)));
require(recoverSigner(message, signature) == owner);
msg.sender.transfer(amount);
}
/// destroy the contract and reclaim the leftover funds.
function kill() public {
require(msg.sender == owner);
selfdestruct(msg.sender);
}
/// signature methods.
function splitSignature(bytes memory sig)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
{
require(sig.length == 65);
assembly {
// first 32 bytes, after the length prefix.
r := mload(add(sig, 32))
// second 32 bytes.
s := mload(add(sig, 64))
// final byte (first byte of the next 32 bytes).
v := byte(0, mload(add(sig, 96)))
}
return (v, r, s);
}
function recoverSigner(bytes32 message, bytes memory sig)
internal
pure
returns (address)
{
(uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);
return ecrecover(message, v, r, s);
}
/// builds a prefixed hash to mimic the behavior of eth_sign.
function prefixed(bytes32 hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}
Writing a Simple Payment Channel
================================
Alice now builds a simple but complete implementation of a payment channel. Payment channels use cryptographic signatures to make repeated transfers of Ether securely, instantaneously, and without transaction fees.
What is a Payment Channel?
--------------------------
Payment channels allow participants to make repeated transfers of Ether without using transactions. This means that you can avoid the delays and fees associated with transactions. We are going to explore a simple unidirectional payment channel between two parties (Alice and Bob). It involves three steps:
1. Alice funds a smart contract with Ether. This "opens" the payment channel.
2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment.
3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender.
.. note::
Only steps 1 and 3 require Ethereum transactions, step 2 means that the sender transmits a cryptographically signed message to the recipient via off chain methods (e.g. email). This means only two transactions are required to support any number of transfers.
Bob is guaranteed to receive their funds because the smart contract escrows the Ether and honours a valid signed message. The smart contract also enforces a timeout, so Alice is guaranteed to eventually recover their funds even if the recipient refuses to close the channel. It is up to the participants in a payment channel to decide how long to keep it open. For a short-lived transaction, such as paying an internet café for each minute of network access, or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years.
Opening the Payment Channel
---------------------------
To open the payment channel, Alice deploys the smart contract, attaching the Ether to be escrowed and specifying the intended recipient and a maximum duration for the channel to exist. This is the function ``SimplePaymentChannel`` in the contract, at the end of this section.
Making Payments
---------------
Alice makes payments by sending signed messages to Bob.
This step is performed entirely outside of the Ethereum network.
Messages are cryptographically signed by the sender and then transmitted directly to the recipient.
Each message includes the following information:
* The smart contract's address, used to prevent cross-contract replay attacks.
* The total amount of Ether that is owed the recipient so far.
A payment channel is closed just once, at the end of a series of transfers.
Because of this, only one of the messages sent is redeemed. This is why
each message specifies a cumulative total amount of Ether owed, rather than the
amount of the individual micropayment. The recipient will naturally choose to
redeem the most recent message because that is the one with the highest total.
The nonce per-message is not needed anymore, because the smart contract only honors a single message. The address of the smart contract is still used
to prevent a message intended for one payment channel from being used for a different channel.
Here is the modified JavaScript code to cryptographically sign a message from the previous section:
::
function constructPaymentMessage(contractAddress, amount) {
return abi.soliditySHA3(
["address", "uint256"],
[contractAddress, amount]
);
}
function signMessage(message, callback) {
web3.eth.personal.sign(
"0x" + message.toString("hex"),
web3.eth.defaultAccount,
callback
);
}
// contractAddress is used to prevent cross-contract replay attacks.
// amount, in wei, specifies how much Ether should be sent.
function signPayment(contractAddress, amount, callback) {
var message = constructPaymentMessage(contractAddress, amount);
signMessage(message, callback);
}
Closing the Payment Channel
---------------------------
When Bob is ready to receive their funds, it is time to
close the payment channel by calling a ``close`` function on the smart contract.
Closing the channel pays the recipient the Ether they are owed and destroys the contract, sending any remaining Ether back to Alice. To close the channel, Bob needs to provide a message signed by Alice.
The smart contract must verify that the message contains a valid signature from the sender.
The process for doing this verification is the same as the process the recipient uses.
The Solidity functions ``isValidSignature`` and ``recoverSigner`` work just like their
JavaScript counterparts in the previous section, with the latter function borrowed from the ``ReceiverPays`` contract.
Only the payment channel recipient can call the ``close`` function,
who naturally passes the most recent payment message because that message
carries the highest total owed. If the sender were allowed to call this function,
they could provide a message with a lower amount and cheat the recipient out of what they are owed.
The function verifies the signed message matches the given parameters.
If everything checks out, the recipient is sent their portion of the Ether,
and the sender is sent the rest via a ``selfdestruct``.
You can see the ``close`` function in the full contract.
Channel Expiration
-------------------
Bob can close the payment channel at any time, but if they fail to do so,
Alice needs a way to recover their escrowed funds. An *expiration* time was set
at the time of contract deployment. Once that time is reached, Alice can call
``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the full contract.
After this function is called, Bob can no longer receive any Ether,
so it is important that Bob closes the channel before the expiration is reached.
The full contract
-----------------
::
pragma solidity >=0.4.24 <0.6.0;
contract SimplePaymentChannel {
address payable public sender; // The account sending payments.
address payable public recipient; // The account receiving the payments.
uint256 public expiration; // Timeout in case the recipient never closes.
constructor (address payable _recipient, uint256 duration)
public
payable
{
sender = msg.sender;
recipient = _recipient;
expiration = now + duration;
}
function isValidSignature(uint256 amount, bytes memory signature)
internal
view
returns (bool)
{
bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount)));
// check that the signature is from the payment sender
return recoverSigner(message, signature) == sender;
}
/// the recipient can close the channel at any time by presenting a
/// signed amount from the sender. the recipient will be sent that amount,
/// and the remainder will go back to the sender
function close(uint256 amount, bytes memory signature) public {
require(msg.sender == recipient);
require(isValidSignature(amount, signature));
recipient.transfer(amount);
selfdestruct(sender);
}
/// the sender can extend the expiration at any time
function extend(uint256 newExpiration) public {
require(msg.sender == sender);
require(newExpiration > expiration);
expiration = newExpiration;
}
/// if the timeout is reached without the recipient closing the channel,
/// then the Ether is released back to the sender.
function claimTimeout() public {
require(now >= expiration);
selfdestruct(sender);
}
/// All functions below this are just taken from the chapter
/// 'creating and verifying signatures' chapter.
function splitSignature(bytes memory sig)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
{
require(sig.length == 65);
assembly {
// first 32 bytes, after the length prefix
r := mload(add(sig, 32))
// second 32 bytes
s := mload(add(sig, 64))
// final byte (first byte of the next 32 bytes)
v := byte(0, mload(add(sig, 96)))
}
return (v, r, s);
}
function recoverSigner(bytes32 message, bytes memory sig)
internal
pure
returns (address)
{
(uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);
return ecrecover(message, v, r, s);
}
/// builds a prefixed hash to mimic the behavior of eth_sign.
function prefixed(bytes32 hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}
.. note::
The function ``splitSignature`` does not use all security
checks. A real implementation should use a more rigorously tested library,
such as openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code.
Verifying Payments
------------------
Unlike in the previous section, messages in a payment channel aren't
redeemed right away. The recipient keeps track of the latest message and
redeems it when it's time to close the payment channel. This means it's
critical that the recipient perform their own verification of each message.
Otherwise there is no guarantee that the recipient will be able to get paid
in the end.
The recipient should verify each message using the following process:
1. Verify that the contact address in the message matches the payment channel.
2. Verify that the new total is the expected amount.
3. Verify that the new total does not exceed the amount of Ether escrowed.
4. Verify that the signature is valid and comes from the payment channel sender.
We'll use the `ethereumjs-util <https://github.com/ethereumjs/ethereumjs-util>`_
library to write this verification. The final step can be done a number of ways,
and we use JavaScript. The following code borrows the `constructMessage` function from the signing **JavaScript code** above:
::
// this mimics the prefixing behavior of the eth_sign JSON-RPC method.
function prefixed(hash) {
return ethereumjs.ABI.soliditySHA3(
["string", "bytes32"],
["\x19Ethereum Signed Message:\n32", hash]
);
}
function recoverSigner(message, signature) {
var split = ethereumjs.Util.fromRpcSig(signature);
var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s);
var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex");
return signer;
}
function isValidSignature(contractAddress, amount, signature, expectedSigner) {
var message = prefixed(constructPaymentMessage(contractAddress, amount));
var signer = recoverSigner(message, signature);
return signer.toLowerCase() ==
ethereumjs.Util.stripHexPrefix(expectedSigner).toLowerCase();
}

View File

@ -0,0 +1,107 @@
.. index:: purchase, remote purchase, escrow
********************
Safe Remote Purchase
********************
::
pragma solidity >=0.4.22 <0.6.0;
contract Purchase {
uint public value;
address payable public seller;
address payable public buyer;
enum State { Created, Locked, Inactive }
State public state;
// Ensure that `msg.value` is an even number.
// Division will truncate if it is an odd number.
// Check via multiplication that it wasn't an odd number.
constructor() public payable {
seller = msg.sender;
value = msg.value / 2;
require((2 * value) == msg.value, "Value has to be even.");
}
modifier condition(bool _condition) {
require(_condition);
_;
}
modifier onlyBuyer() {
require(
msg.sender == buyer,
"Only buyer can call this."
);
_;
}
modifier onlySeller() {
require(
msg.sender == seller,
"Only seller can call this."
);
_;
}
modifier inState(State _state) {
require(
state == _state,
"Invalid state."
);
_;
}
event Aborted();
event PurchaseConfirmed();
event ItemReceived();
/// Abort the purchase and reclaim the ether.
/// Can only be called by the seller before
/// the contract is locked.
function abort()
public
onlySeller
inState(State.Created)
{
emit Aborted();
state = State.Inactive;
seller.transfer(address(this).balance);
}
/// Confirm the purchase as buyer.
/// Transaction has to include `2 * value` ether.
/// The ether will be locked until confirmReceived
/// is called.
function confirmPurchase()
public
inState(State.Created)
condition(msg.value == (2 * value))
payable
{
emit PurchaseConfirmed();
buyer = msg.sender;
state = State.Locked;
}
/// Confirm that you (the buyer) received the item.
/// This will release the locked ether.
function confirmReceived()
public
onlyBuyer
inState(State.Locked)
{
emit ItemReceived();
// It is important to change the state first because
// otherwise, the contracts called using `send` below
// can call in again here.
state = State.Inactive;
// NOTE: This actually allows both the buyer and the seller to
// block the refund - the withdraw pattern should be used.
buyer.transfer(value);
seller.transfer(address(this).balance);
}
}

191
docs/examples/voting.rst Normal file
View File

@ -0,0 +1,191 @@
.. index:: voting, ballot
.. _voting:
******
Voting
******
The following contract is quite complex, but showcases
a lot of Solidity's features. It implements a voting
contract. Of course, the main problems of electronic
voting is how to assign voting rights to the correct
persons and how to prevent manipulation. We will not
solve all problems here, but at least we will show
how delegated voting can be done so that vote counting
is **automatic and completely transparent** at the
same time.
The idea is to create one contract per ballot,
providing a short name for each option.
Then the creator of the contract who serves as
chairperson will give the right to vote to each
address individually.
The persons behind the addresses can then choose
to either vote themselves or to delegate their
vote to a person they trust.
At the end of the voting time, ``winningProposal()``
will return the proposal with the largest number
of votes.
::
pragma solidity >=0.4.22 <0.6.0;
/// @title Voting with delegation.
contract Ballot {
// This declares a new complex type which will
// be used for variables later.
// It will represent a single voter.
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
// This is a type for a single proposal.
struct Proposal {
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
address public chairperson;
// This declares a state variable that
// stores a `Voter` struct for each possible address.
mapping(address => Voter) public voters;
// A dynamically-sized array of `Proposal` structs.
Proposal[] public proposals;
/// Create a new ballot to choose one of `proposalNames`.
constructor(bytes32[] memory proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
// For each of the provided proposal names,
// create a new proposal object and add it
// to the end of the array.
for (uint i = 0; i < proposalNames.length; i++) {
// `Proposal({...})` creates a temporary
// Proposal object and `proposals.push(...)`
// appends it to the end of `proposals`.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
// Give `voter` the right to vote on this ballot.
// May only be called by `chairperson`.
function giveRightToVote(address voter) public {
// If the first argument of `require` evaluates
// to `false`, execution terminates and all
// changes to the state and to Ether balances
// are reverted.
// This used to consume all gas in old EVM versions, but
// not anymore.
// It is often a good idea to use `require` to check if
// functions are called correctly.
// As a second argument, you can also provide an
// explanation about what went wrong.
require(
msg.sender == chairperson,
"Only chairperson can give right to vote."
);
require(
!voters[voter].voted,
"The voter already voted."
);
require(voters[voter].weight == 0);
voters[voter].weight = 1;
}
/// Delegate your vote to the voter `to`.
function delegate(address to) public {
// assigns reference
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted.");
require(to != msg.sender, "Self-delegation is disallowed.");
// Forward the delegation as long as
// `to` also delegated.
// In general, such loops are very dangerous,
// because if they run too long, they might
// need more gas than is available in a block.
// In this case, the delegation will not be executed,
// but in other situations, such loops might
// cause a contract to get "stuck" completely.
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
require(to != msg.sender, "Found loop in delegation.");
}
// Since `sender` is a reference, this
// modifies `voters[msg.sender].voted`
sender.voted = true;
sender.delegate = to;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight;
}
}
/// Give your vote (including votes delegated to you)
/// to proposal `proposals[proposal].name`.
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote");
require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = proposal;
// If `proposal` is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}
/// @dev Computes the winning proposal taking all
/// previous votes into account.
function winningProposal() public view
returns (uint winningProposal_)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}
// Calls winningProposal() function to get the index
// of the winner contained in the proposals array and then
// returns the name of the winner
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
Possible Improvements
=====================
Currently, many transactions are needed to assign the rights
to vote to all participants. Can you think of a better way?

View File

@ -16,11 +16,6 @@ Enums are not supported by the ABI, they are just supported by Solidity.
You have to do the mapping yourself for now, we might provide some help
later.
How do structs work?
====================
See `struct_and_for_loop_tester.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/65_struct_and_for_loop_tester.sol>`_.
What are some examples of basic string manipulation (``substring``, ``indexOf``, ``charAt``, etc)?
==================================================================================================
@ -59,44 +54,10 @@ Yes, you can use ``abi.encodePacked``::
}
}
Why is the low-level function ``.call()`` less favorable than instantiating a contract with a variable (``ContractB b;``) and executing its functions (``b.doSomething();``)?
=============================================================================================================================================================================
If you use actual functions, the compiler will tell you if the types
or your arguments do not match, if the function does not exist
or is not visible and it will do the packing of the
arguments for you.
See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_ and
`pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_.
Are comments included with deployed contracts and do they increase deployment gas?
==================================================================================
No, everything that is not needed for execution is removed during compilation.
This includes, among others, comments, variable names and type names.
What happens if you send ether along with a function call to a contract?
========================================================================
It gets added to the total balance of the contract, just like when you send ether when creating a contract.
You can only send ether along to a function that has the ``payable`` modifier,
otherwise an exception is thrown.
******************
Advanced Questions
******************
How do you get a random number in a contract? (Implement a self-returning gambling contract.)
=============================================================================================
Getting randomness right is often the crucial part in a crypto project and
most failures result from bad random number generators.
If you do not want it to be safe, you build something similar to the `coin flipper <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/35_coin_flipper.sol>`_
but otherwise, rather use a contract that supplies randomness, like the `RANDAO <https://github.com/randao/randao>`_.
Get return value from non-constant function from another contract
=================================================================
@ -105,23 +66,6 @@ The key point is that the calling contract needs to know about the function it i
See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_
and `pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_.
How do you create 2-dimensional arrays?
=======================================
See `2D_array.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/55_2D_array.sol>`_.
Note that filling a 10x10 square of ``uint8`` + contract creation took more than ``800,000``
gas at the time of this writing. 17x17 took ``2,000,000`` gas. With the limit at
3.14 million... well, theres a pretty low ceiling for what you can create right
now.
Note that merely "creating" the array is free, the costs are in filling it.
Note2: Optimizing storage access can pull the gas costs down considerably, because
32 ``uint8`` values can be stored in a single slot. The problem is that these optimizations
currently do not work across loops and also have a problem with bounds checking.
You might get much better results in the future, though.
How do I initialize a contract with only a specific amount of wei?
==================================================================
@ -131,7 +75,7 @@ In the case of a ``contract A`` calling a new instance of ``contract B``, parent
You will need to make sure that you have both contracts aware of each other's presence and that ``contract B`` has a ``payable`` constructor.
In this example::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract B {
constructor() public payable {}
@ -145,69 +89,6 @@ In this example::
}
}
Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) to another contract?
=====================================================================================================
Sure. Take care that if you cross the memory / storage boundary,
independent copies will be created::
pragma solidity >=0.4.16 <0.6.0;
contract C {
uint[20] x;
function f() public {
g(x);
h(x);
}
function g(uint[20] memory y) internal pure {
y[2] = 3;
}
function h(uint[20] storage y) internal {
y[3] = 4;
}
}
The call to ``g(x)`` will not have an effect on ``x`` because it needs
to create an independent copy of the storage value in memory.
On the other hand, ``h(x)`` successfully modifies ``x`` because only
a reference and not a copy is passed.
What does the following strange check do in the Custom Token contract?
======================================================================
::
require((balanceOf[_to] + _value) >= balanceOf[_to]);
Integers in Solidity (and most other machine-related programming languages) are restricted to a certain range.
For ``uint256``, this is ``0`` up to ``2**256 - 1``. If the result of some operation on those numbers
does not fit inside this range, it is truncated. These truncations can have
`serious consequences <https://en.bitcoin.it/wiki/Value_overflow_incident>`_, so code like the one
above is necessary to avoid certain attacks.
Why are explicit conversions between fixed-size bytes types and integer types failing?
======================================================================================
Since version 0.5.0 explicit conversions between fixed-size byte arrays and integers are only allowed,
if both types have the same size. This prevents unexpected behaviour when truncating or padding.
Such conversions are still possible, but intermediate casts are required that make the desired
truncation and padding convention explicit. See :ref:`types-conversion-elementary-types` for a full
explanation and examples.
Why can number literals not be converted to fixed-size bytes types?
===================================================================
Since version 0.5.0 only hexadecimal number literals can be converted to fixed-size bytes
types and only if the number of hex digits matches the size of the type. See :ref:`types-conversion-literals`
for a full explanation and examples.
More Questions?
===============

View File

@ -19,6 +19,8 @@ user-defined types among other features.
With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions,
and multi-signature wallets.
When deploying contracts, you should use the latest released version of Solidity. This is because breaking changes as well as new features and bug fixes are introduced regularly. We currently use a 0.x version number [to indicate this fast pace of change](https://semver.org/#spec-item-4).
Language Documentation
----------------------

View File

@ -81,7 +81,7 @@ registering with username and password — all you need is an Ethereum keypair.
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
contract Coin {
// The keyword "public" makes those variables

View File

@ -37,12 +37,12 @@ breaking changes, those releases will always have versions of the form
The version pragma is used as follows::
pragma solidity ^0.4.0;
pragma solidity ^0.5.2;
Such a source file will not compile with a compiler earlier than version 0.4.0
and it will also not work on a compiler starting from version 0.5.0 (this
Such a source file will not compile with a compiler earlier than version 0.5.2
and it will also not work on a compiler starting from version 0.6.0 (this
second condition is added by using ``^``). The idea behind this is that
there will be no breaking changes until version ``0.5.0``, so we can always
there will be no breaking changes until version ``0.6.0``, so we can always
be sure that our code will compile the way we intended it to. We do not fix
the exact version of the compiler, so that bugfix releases are still possible.

View File

@ -385,6 +385,8 @@ Global Variables
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure
- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.
.. note::
Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness,
@ -445,7 +447,7 @@ These keywords are reserved in Solidity. They might become part of the syntax in
``abstract``, ``after``, ``alias``, ``apply``, ``auto``, ``case``, ``catch``, ``copyof``, ``default``,
``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
``mutable``, ``null``, ``of``, ``override``, ``partial``, ``promise``, ``reference``, ``relocatable``,
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``type``, ``typedef``, ``typeof``,
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``typedef``, ``typeof``,
``unchecked``.
Language Grammar

View File

@ -183,7 +183,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract TxUserWallet {
@ -203,7 +203,7 @@ Now someone tricks you into sending ether to the address of this attack wallet:
::
pragma solidity >0.4.99 <0.6.0;
pragma solidity ^0.5.0;
interface TxUserWallet {
function transferTo(address payable dest, uint amount) external;
@ -223,7 +223,7 @@ Now someone tricks you into sending ether to the address of this attack wallet:
If your wallet had checked ``msg.sender`` for authorization, it would get the address of the attack wallet, instead of the owner address. But by checking ``tx.origin``, it gets the original address that kicked off the transaction, which is still the owner address. The attack wallet instantly drains all your funds.
.. _underflow-overflow:
Two's Complement / Underflows / Overflows
=========================================
@ -241,9 +241,11 @@ more special edge cases for signed numbers.
Try to use ``require`` to limit the size of inputs to a reasonable range and use the
:ref:`SMT checker<smt_checker>` to find potential overflows, or
use a library like
`SafeMath<https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol>`
`SafeMath <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol>`_
if you want all overflows to cause a revert.
Code such as ``require((balanceOf[_to] + _value) >= balanceOf[_to])`` can also help you check if values are what you expect.
Minor Details
=============

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

127
docs/types/conversion.rst Normal file
View File

@ -0,0 +1,127 @@
.. index:: ! type;conversion, ! cast
.. _types-conversion-elementary-types:
Conversions between Elementary Types
====================================
Implicit Conversions
--------------------
If an operator is applied to different types, the compiler tries to
implicitly convert one of the operands to the type of the other (the same is
true for assignments). In general, an implicit conversion between value-types
is possible if it
makes sense semantically and no information is lost: ``uint8`` is convertible to
``uint16`` and ``int128`` to ``int256``, but ``int8`` is not convertible to ``uint256``
(because ``uint256`` cannot hold e.g. ``-1``).
For more details, please consult the sections about the types themselves.
Explicit Conversions
--------------------
If the compiler does not allow implicit conversion but you know what you are
doing, an explicit type conversion is sometimes possible. Note that this may
give you some unexpected behaviour and allows you to bypass some security
features of the compiler, so be sure to test that the
result is what you want! Take the following example where you are converting
a negative ``int8`` to a ``uint``:
::
int8 y = -3;
uint x = uint(y);
At the end of this code snippet, ``x`` will have the value ``0xfffff..fd`` (64 hex
characters), which is -3 in the two's complement representation of 256 bits.
If an integer is explicitly converted to a smaller type, higher-order bits are
cut off::
uint32 a = 0x12345678;
uint16 b = uint16(a); // b will be 0x5678 now
If an integer is explicitly converted to a larger type, it is padded on the left (i.e. at the higher order end).
The result of the conversion will compare equal to the original integer::
uint16 a = 0x1234;
uint32 b = uint32(a); // b will be 0x00001234 now
assert(a == b);
Fixed-size bytes types behave differently during conversions. They can be thought of as
sequences of individual bytes and converting to a smaller type will cut off the
sequence::
bytes2 a = 0x1234;
bytes1 b = bytes1(a); // b will be 0x12
If a fixed-size bytes type is explicitly converted to a larger type, it is padded on
the right. Accessing the byte at a fixed index will result in the same value before and
after the conversion (if the index is still in range)::
bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b will be 0x12340000
assert(a[0] == b[0]);
assert(a[1] == b[1]);
Since integers and fixed-size byte arrays behave differently when truncating or
padding, explicit conversions between integers and fixed-size byte arrays are only allowed,
if both have the same size. If you want to convert between integers and fixed-size byte arrays of
different size, you have to use intermediate conversions that make the desired truncation and padding
rules explicit::
bytes2 a = 0x1234;
uint32 b = uint16(a); // b will be 0x00001234
uint32 c = uint32(bytes4(a)); // c will be 0x12340000
uint8 d = uint8(uint16(a)); // d will be 0x34
uint8 e = uint8(bytes1(a)); // e will be 0x12
.. _types-conversion-literals:
Conversions between Literals and Elementary Types
=================================================
Integer Types
-------------
Decimal and hexadecimal number literals can be implicitly converted to any integer type
that is large enough to represent it without truncation::
uint8 a = 12; // fine
uint32 b = 1234; // fine
uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456
Fixed-Size Byte Arrays
----------------------
Decimal number literals cannot be implicitly converted to fixed-size byte arrays. Hexadecimal
number literals can be, but only if the number of hex digits exactly fits the size of the bytes
type. As an exception both decimal and hexadecimal literals which have a value of zero can be
converted to any fixed-size bytes type::
bytes2 a = 54321; // not allowed
bytes2 b = 0x12; // not allowed
bytes2 c = 0x123; // not allowed
bytes2 d = 0x1234; // fine
bytes2 e = 0x0012; // fine
bytes4 f = 0; // fine
bytes4 g = 0x0; // fine
String literals and hex string literals can be implicitly converted to fixed-size byte arrays,
if their number of characters matches the size of the bytes type::
bytes2 a = hex"1234"; // fine
bytes2 b = "xy"; // fine
bytes2 c = hex"12"; // not allowed
bytes2 d = hex"123"; // not allowed
bytes2 e = "x"; // not allowed
bytes2 f = "xyz"; // not allowed
Addresses
---------
As described in :ref:`address_literals`, hex literals of the correct size that pass the checksum
test are of ``address`` type. No other literals can be implicitly converted to the ``address`` type.
Explicit conversions from ``bytes20`` or any integer type to ``address`` result in ``address payable``.

View File

@ -0,0 +1,58 @@
.. index:: !mapping
.. _mapping-types:
Mapping Types
=============
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
The ``_KeyType`` can be any elementary type. This means it can be any of
the built-in value types plus ``bytes`` and ``string``. User-defined
or complex types like contract types, enums, mappings, structs and any array type
apart from ``bytes`` and ``string`` are not allowed.
``_ValueType`` can be any type, including mappings.
You can think of mappings as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_, which are virtually initialised
such that every possible key exists and is mapped to a value whose
byte-representation is all zeros, a type's :ref:`default value <default-value>`. The similarity ends there, the key data is not stored in a
mapping, only its ``keccak256`` hash is used to look up the value.
Because of this, mappings do not have a length or a concept of a key or
value being set.
Mappings can only have a data location of ``storage`` and thus
are allowed for state variables, as storage reference types
in functions, or as parameters for library functions.
They cannot be used as parameters or return parameters
of contract functions that are publicly visible.
You can mark variables of mapping type as ``public`` and Solidity creates a
:ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a
parameter for the getter. If ``_ValueType`` is a value type or a struct,
the getter returns ``_ValueType``.
If ``_ValueType`` is an array or a mapping, the getter has one parameter for
each ``_KeyType``, recursively. For example with a mapping:
::
pragma solidity >=0.4.0 <0.6.0;
contract MappingExample {
mapping(address => uint) public balances;
function update(uint newBalance) public {
balances[msg.sender] = newBalance;
}
}
contract MappingUser {
function f() public returns (uint) {
MappingExample m = new MappingExample();
m.update(100);
return m.balances(address(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>`_.

47
docs/types/operators.rst Normal file
View File

@ -0,0 +1,47 @@
.. index:: assignment, ! delete, lvalue
Operators Involving LValues
===========================
If ``a`` is an LValue (i.e. a variable or something that can be assigned to), the following operators are available as shorthands:
``a += e`` is equivalent to ``a = a + e``. The operators ``-=``, ``*=``, ``/=``, ``%=``, ``|=``, ``&=`` and ``^=`` are defined accordingly. ``a++`` and ``a--`` are equivalent to ``a += 1`` / ``a -= 1`` but the expression itself still has the previous value of ``a``. In contrast, ``--a`` and ``++a`` have the same effect on ``a`` but return the value after the change.
delete
------
``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is
equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic
array of length zero or a static array of the same length with all elements set to their
initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves
all other elements and the length of the array untouched. This especially means that it leaves
a gap in the array. If you plan to remove items, a mapping is probably a better choice.
For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat:
``delete`` has no effect on mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted: If ``a`` is a mapping, then ``delete a[x]`` will delete the value stored at ``x``.
It is important to note that ``delete a`` really behaves like an assignment to ``a``, i.e. it stores a new object in ``a``.
This distinction is visible when ``a`` is reference variable: It will only reset ``a`` itself, not the
value it referred to previously.
::
pragma solidity >=0.4.0 <0.6.0;
contract DeleteExample {
uint data;
uint[] dataArray;
function f() public {
uint x = data;
delete x; // sets x to 0, does not affect data
delete data; // sets data to 0, does not affect x
uint[] storage y = dataArray;
delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also
// y is affected which is an alias to the storage object
// On the other hand: "delete y" is not valid, as assignments to local variables
// referencing storage objects can only be made from existing storage objects.
assert(y.length == 0);
}
}

View File

@ -0,0 +1,409 @@
.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct
.. _reference-types:
Reference Types
===============
Values of reference type can be modified through multiple different names.
Contrast this with value types where you get an independent copy whenever
a variable of value type is used. Because of that, reference types have to be handled
more carefully than value types. Currently, reference types comprise structs,
arrays and mappings. If you use a reference type, you always have to explicitly
provide the data area where the type is stored: ``memory`` (whose lifetime is limited
to a function call), ``storage`` (the location where the state variables are stored)
or ``calldata`` (special data location that contains the function arguments,
only available for external function call parameters).
An assignment or type conversion that changes the data location will always incur an automatic copy operation,
while assignments inside the same data location only copy in some cases for storage types.
.. _data-location:
Data location
-------------
Every reference type, i.e. *arrays* and *structs*, has an additional
annotation, the "data location", about where it is stored. There are three data locations:
``memory``, ``storage`` and ``calldata``. Calldata is only valid for parameters of external contract
functions and is required for this type of parameter. Calldata is a non-modifiable,
non-persistent area where function arguments are stored, and behaves mostly like memory.
.. note::
Prior to version 0.5.0 the data location could be omitted, and would default to different locations
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
data location.
.. _data-location-assignment:
Data location and assignment behaviour
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Data locations are not only relevant for persistency of data, but also for the semantics of assignments:
* Assignments between ``storage`` and ``memory`` (or from ``calldata``) always create an independent copy.
* Assignments from ``memory`` to ``memory`` only create references. This means that changes to one memory variable are also visible in all other memory variables that refer to the same data.
* Assignments from ``storage`` to a local storage variable also only assign a reference.
* All other assignments to ``storage`` always copy. Examples for this case are assignments to state variables or to members of local variables of storage struct type, even if the local variable itself is just a reference.
::
pragma solidity >=0.4.0 <0.6.0;
contract C {
uint[] x; // the data location of x is storage
// the data location of memoryArray is memory
function f(uint[] memory memoryArray) public {
x = memoryArray; // works, copies the whole array to storage
uint[] storage y = x; // works, assigns a pointer, data location of y is storage
y[7]; // fine, returns the 8th element
y.length = 2; // fine, modifies x through y
delete x; // fine, clears the array, also modifies y
// The following does not work; it would need to create a new temporary /
// unnamed array in storage, but storage is "statically" allocated:
// y = memoryArray;
// This does not work either, since it would "reset" the pointer, but there
// is no sensible location it could point to.
// delete y;
g(x); // calls g, handing over a reference to x
h(x); // calls h and creates an independent, temporary copy in memory
}
function g(uint[] storage) internal pure {}
function h(uint[] memory) public pure {}
}
.. index:: ! array
.. _arrays:
Arrays
------
Arrays can have a compile-time fixed size, or they can have a dynamic size.
The type of an array of fixed size ``k`` and element type ``T`` is written as ``T[k]``,
and an array of dynamic size as ``T[]``.
For example, an array of 5 dynamic arrays of ``uint`` is written as
``uint[][5]``. The notation is reversed compared to some other languages. In
Solidity, ``X[3]`` is always an array containing three elements of type ``X``,
even if ``X`` is itself an array. This is not the case in other languages such
as C.
Indices are zero-based, and access is in the opposite direction of the
declaration.
For example, if you have a variable ``uint[][5] x memory``, you access the
second ``uint`` in the third dynamic array using ``x[2][1]``, and to access the
third dynamic array, use ``x[2]``. Again,
if you have an array ``T[5] a`` for a type ``T`` that can also be an array,
then ``a[2]`` always has type ``T``.
Array elements can be of any type, including mapping or struct. The general
restrictions for types apply, in that mappings can only be stored in the
``storage`` data location and publicly-visible functions need parameters that are :ref:`ABI types <ABI>`.
Accessing an array past its end causes a failing assertion. You can use the ``.push()`` method to append a new element at the end or assign to the ``.length`` :ref:`member <array-members>` to change the size (see below for caveats).
method or increase the ``.length`` :ref:`member <array-members>` to add elements.
Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``,
but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow
length or index access.
You should use ``bytes`` over ``byte[]`` because it is cheaper, since ``byte[]`` adds 31 padding bytes between the elements. As a general rule,
use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length
string (UTF-8) data. If you can limit the length to a certain number of bytes,
always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper.
.. note::
If you want to access the byte-representation of a string ``s``, use
``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind
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 a :ref:`getter <visibility-and-getters>`.
The numeric index becomes a required parameter for the getter.
.. index:: ! array;allocating, new
Allocating Memory Arrays
^^^^^^^^^^^^^^^^^^^^^^^^
You must use the ``new`` keyword to create arrays with a runtime-dependent length in memory.
As opposed to storage arrays, it is **not** possible to resize memory arrays (e.g. by assigning to
the ``.length`` member). You either have to calculate the required size in advance
or create a new memory array and copy every element.
::
pragma solidity >=0.4.16 <0.6.0;
contract C {
function f(uint len) public pure {
uint[] memory a = new uint[](7);
bytes memory b = new bytes(len);
assert(a.length == 7);
assert(b.length == len);
a[6] = 8;
}
}
.. index:: ! array;literals, ! inline;arrays
Array Literals
^^^^^^^^^^^^^^
An array literal is a comma-separated list of one or more expressions, enclosed
in square brackets (``[...]``). For example ``[1, a, f(3)]``. There must be a
common type all elements can be implicitly converted to. This is the elementary
type of the array.
Array literals are always statically-sized memory arrays.
In the example below, the type of ``[1, 2, 3]`` is
``uint8[3] memory``. Because the type of each of these constants is ``uint8``, if you want the result to be a ``uint[3] memory`` type, you need to convert the first element to ``uint``.
::
pragma solidity >=0.4.16 <0.6.0;
contract C {
function f() public pure {
g([uint(1), 2, 3]);
}
function g(uint[3] memory) public pure {
// ...
}
}
Fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible:
::
pragma solidity >=0.4.0 <0.6.0;
// This will not compile.
contract C {
function f() public {
// The next line creates a type error because uint[3] memory
// cannot be converted to uint[] memory.
uint[] memory x = [uint(1), 3, 4];
}
}
It is planned to remove this restriction in the future, but it creates some
complications because of how arrays are passed in the ABI.
.. index:: ! array;length, length, push, pop, !array;push, !array;pop
.. _array-members:
Array Members
^^^^^^^^^^^^^
**length**:
Arrays have a ``length`` member that contains their number of elements.
The length of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created.
For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array.
Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion.
Increasing the length adds new zero-initialised elements to the array.
Reducing the length performs an implicit :ref:``delete`` on each of the
removed elements. If you try to resize a non-dynamic array that isn't in
storage, you receive a ``Value must be an lvalue`` error.
**push**:
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length.
**pop**:
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that you can use to remove an element from the end of the array. This also implicitly calls :ref:``delete`` on the removed element.
.. warning::
If you use ``.length--`` on an empty array, it causes an underflow and
thus sets the length to ``2**256-1``.
.. note::
Increasing the length of a storage array has constant gas costs because
storage is assumed to be zero-initialised, while decreasing
the length has at least linear cost (but in most cases worse than linear),
because it includes explicitly clearing the removed
elements similar to calling :ref:``delete`` on them.
.. note::
It is not yet possible to use arrays of arrays in external functions
(but they are supported in public functions).
.. note::
In EVM versions before Byzantium, it was not possible to access
dynamic arrays return from function calls. If you call functions
that return dynamic arrays, make sure to use an EVM that is set to
Byzantium mode.
::
pragma solidity >=0.4.16 <0.6.0;
contract ArrayContract {
uint[2**20] m_aLotOfIntegers;
// Note that the following is not a pair of dynamic arrays but a
// dynamic array of pairs (i.e. of fixed size arrays of length two).
// Because of that, T[] is always a dynamic array of T, even if T
// itself is an array.
// Data location for all state variables is storage.
bool[2][] m_pairsOfFlags;
// newPairs is stored in memory - the only possibility
// for public contract function arguments
function setAllFlagPairs(bool[2][] memory newPairs) public {
// assignment to a storage array performs a copy of ``newPairs`` and
// replaces the complete array ``m_pairsOfFlags``.
m_pairsOfFlags = newPairs;
}
struct StructType {
uint[] contents;
uint moreInfo;
}
StructType s;
function f(uint[] memory c) public {
// stores a reference to ``s`` in ``g``
StructType storage g = s;
// also changes ``s.moreInfo``.
g.moreInfo = 2;
// assigns a copy because ``g.contents``
// is not a local variable, but a member of
// a local variable.
g.contents = c;
}
function setFlagPair(uint index, bool flagA, bool flagB) public {
// access to a non-existing index will throw an exception
m_pairsOfFlags[index][0] = flagA;
m_pairsOfFlags[index][1] = flagB;
}
function changeFlagArraySize(uint newSize) public {
// if the new size is smaller, removed array elements will be cleared
m_pairsOfFlags.length = newSize;
}
function clear() public {
// these clear the arrays completely
delete m_pairsOfFlags;
delete m_aLotOfIntegers;
// identical effect here
m_pairsOfFlags.length = 0;
}
bytes m_byteData;
function byteArrays(bytes memory data) public {
// byte arrays ("bytes") are different as they are stored without padding,
// but can be treated identical to "uint8[]"
m_byteData = data;
m_byteData.length += 7;
m_byteData[3] = 0x08;
delete m_byteData[2];
}
function addFlag(bool[2] memory flag) public returns (uint) {
return m_pairsOfFlags.push(flag);
}
function createMemoryArray(uint size) public pure returns (bytes memory) {
// Dynamic memory arrays are created using `new`:
uint[2][] memory arrayOfPairs = new uint[2][](size);
// Inline arrays are always statically-sized and if you only
// use literals, you have to provide at least one type.
arrayOfPairs[0] = [uint(1), 2];
// Create a dynamic byte array:
bytes memory b = new bytes(200);
for (uint i = 0; i < b.length; i++)
b[i] = byte(uint8(i));
return b;
}
}
.. index:: ! struct, ! type;struct
.. _structs:
Structs
-------
Solidity provides a way to define new types in the form of structs, which is
shown in the following example:
::
pragma solidity >=0.4.11 <0.6.0;
contract CrowdFunding {
// Defines a new type with two fields.
struct Funder {
address addr;
uint amount;
}
struct Campaign {
address payable beneficiary;
uint fundingGoal;
uint numFunders;
uint amount;
mapping (uint => Funder) funders;
}
uint numCampaigns;
mapping (uint => Campaign) campaigns;
function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
campaignID = numCampaigns++; // campaignID is return variable
// Creates new struct in memory and copies it to storage.
// We leave out the mapping type, because it is not valid in memory.
// If structs are copied (even from storage to storage), mapping types
// are always omitted, because they cannot be enumerated.
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
}
function contribute(uint campaignID) public payable {
Campaign storage c = campaigns[campaignID];
// Creates a new temporary memory struct, initialised with the given values
// and copies it over to storage.
// Note that you can also use Funder(msg.sender, msg.value) to initialise.
c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});
c.amount += msg.value;
}
function checkGoalReached(uint campaignID) public returns (bool reached) {
Campaign storage c = campaigns[campaignID];
if (c.amount < c.fundingGoal)
return false;
uint amount = c.amount;
c.amount = 0;
c.beneficiary.transfer(amount);
return true;
}
}
The contract does not provide the full functionality of a crowdfunding
contract, but it contains the basic concepts necessary to understand structs.
Struct types can be used inside mappings and arrays and they can itself
contain mappings and arrays.
It is not possible for a struct to contain a member of its own type,
although the struct itself can be the value type of a mapping member
or it can contain a dynamically-sized array of its type.
This restriction is necessary, as the size of the struct has to be finite.
Note how in all the functions, a struct type is assigned to a local variable
with data location ``storage``.
This does not copy the struct but only stores a reference so that assignments to
members of the local variable actually write to the state.
Of course, you can also directly access the members of the struct without
assigning it to a local variable, as in
``campaigns[campaignID].amount = 0``.

716
docs/types/value-types.rst Normal file
View File

@ -0,0 +1,716 @@
.. index:: ! value type, ! type;value
.. _value-types:
Value Types
===========
The following types are also called value types because variables of these
types will always be passed by value, i.e. they are always copied when they
are used as function arguments or in assignments.
.. index:: ! bool, ! true, ! false
Booleans
--------
``bool``: The possible values are constants ``true`` and ``false``.
Operators:
* ``!`` (logical negation)
* ``&&`` (logical conjunction, "and")
* ``||`` (logical disjunction, "or")
* ``==`` (equality)
* ``!=`` (inequality)
The operators ``||`` and ``&&`` apply the common short-circuiting rules. This means that in the expression ``f(x) || g(y)``, if ``f(x)`` evaluates to ``true``, ``g(y)`` will not be evaluated even if it may have side-effects.
.. index:: ! uint, ! int, ! integer
Integers
--------
``int`` / ``uint``: Signed and unsigned integers of various sizes. Keywords ``uint8`` to ``uint256`` in steps of ``8`` (unsigned of 8 up to 256 bits) and ``int8`` to ``int256``. ``uint`` and ``int`` are aliases for ``uint256`` and ``int256``, respectively.
Operators:
* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``)
* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation)
* Shift operators: ``<<`` (left shift), ``>>`` (right shift)
* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation)
.. warning::
Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``.
If the result of some operation on those numbers does not fit inside this range, it is truncated. These truncations can have
serious consequences that you should :ref:`be aware of and mitigate against<underflow-overflow>`.
Comparisons
^^^^^^^^^^^
The value of a comparison is the one obtained by comparing the integer value.
Bit operations
^^^^^^^^^^^^^^
Bit operations are performed on the two's complement representation of the number.
This means that, for example ``~int256(0) == int256(-1)``.
Shifts
^^^^^^
The result of a shift operation has the type of the left operand. The
expression ``x << y`` is equivalent to ``x * 2**y``, and, for positive integers,
``x >> y`` is equivalent to ``x / 2**y``. For negative ``x``, ``x >> y``
is equivalent to dividing by a power of ``2`` while rounding down (towards negative infinity).
Shifting by a negative amount throws a runtime exception.
.. warning::
Before version ``0.5.0`` a right shift ``x >> y`` for negative ``x`` was equivalent to ``x / 2**y``,
i.e. right shifts used rounding towards zero instead of rounding towards negative infinity.
Addition, Subtraction and Multiplication
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Addition, subtraction and multiplication have the usual semantics.
They wrap in two's complement representation, meaning that
for example ``uint256(0) - uint256(1) == 2**256 - 1``. You have to take these overflows
into account when designing safe smart contracts.
The expression ``-x`` is equivalent to ``(T(0) - x)`` where
``T`` is the type of ``x``. This means that ``-x`` will not be negative
if the type of ``x`` is an unsigned integer type. Also, ``-x`` can be
positive if ``x`` is negative. There is another caveat also resulting
from two's complement representation::
int x = -2**255;
assert(-x == x);
This means that even if a number is negative, you cannot assume that
its negation will be positive.
Division
^^^^^^^^
Since the type of the result of an operation is always the type of one of
the operands, division on integers always results in an integer.
In Solidity, division rounds towards zero. This mean that ``int256(-5) / int256(2) == int256(-2)``.
Note that in contrast, division on :ref:`literals<rational_literals>` results in fractional values
of arbitrary precision.
.. note::
Division by zero causes a failing assert.
Modulo
^^^^^^
The modulo operation ``a % n`` yields the remainder ``r`` after the division of the operand ``a``
by the operand ``n``, where ``q = int(a / n)`` and ``r = a - (n * q)``. This means that modulo
results in the same sign as its left operand (or zero) and ``a % n == -(abs(a) % n)`` holds for negative ``a``:
* ``int256(5) % int256(2) == int256(1)``
* ``int256(5) % int256(-2) == int256(1)``
* ``int256(-5) % int256(2) == int256(-1)``
* ``int256(-5) % int256(-2) == int256(-1)``
.. note::
Modulo with zero causes a failing assert.
Exponentiation
^^^^^^^^^^^^^^
Exponentiation is only available for unsigned types. Please take care that the types
you are using are large enough to hold the result and prepare for potential wrapping behaviour.
.. note::
Note that ``0**0`` is defined by the EVM as ``1``.
.. index:: ! ufixed, ! fixed, ! fixed point number
Fixed Point Numbers
-------------------
.. warning::
Fixed point numbers are not fully supported by Solidity yet. They can be declared, but
cannot be assigned to or from.
``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represents the number of bits taken by
the type and ``N`` represents how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive.
``ufixed`` and ``fixed`` are aliases for ``ufixed128x18`` and ``fixed128x18``, respectively.
Operators:
* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``)
* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo)
.. note::
The main difference between floating point (``float`` and ``double`` in many languages, more precisely IEEE 754 numbers) and fixed point numbers is
that the number of bits used for the integer and the fractional part (the part after the decimal dot) is flexible in the former, while it is strictly
defined in the latter. Generally, in floating point almost the entire space is used to represent the number, while only a small number of bits define
where the decimal point is.
.. index:: address, balance, send, call, callcode, delegatecall, staticcall, transfer
.. _address:
Address
-------
The address type comes in two flavours, which are largely identical:
- ``address``: Holds a 20 byte value (size of an Ethereum address).
- ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``.
The idea behind this distinction is that ``address payable`` is an address you can send Ether to,
while a plain ``address`` cannot be sent Ether.
Type conversions:
Implicit conversions from ``address payable`` to ``address`` are allowed, whereas conversions from ``address`` to ``address payable`` are
not possible (the only way to perform such a conversion is by using an intermediate conversion to ``uint160``).
:ref:`Address literals<address_literals>` can be implicitly converted to ``address payable``.
Explicit conversions to and from ``address`` are allowed for integers, integer literals, ``bytes20`` and contract types with the following
caveat:
Conversions of the form ``address payable(x)`` are not allowed. Instead the result of a conversion of the form ``address(x)``
has the type ``address payable``, if ``x`` is of integer or fixed bytes type, a literal or a contract with a payable fallback function.
If ``x`` is a contract without payable fallback function, then ``address(x)`` will be of type ``address``.
In external function signatures ``address`` is used for both the ``address`` and the ``address payable`` type.
.. note::
It might very well be that you do not need to care about the distinction between ``address``
and ``address payable`` and just use ``address`` everywhere. For example,
if you are using the :ref:`withdrawal pattern<withdrawal_pattern>`, you can (and should) store the
address itself as ``address``, because you invoke the ``transfer`` function on
``msg.sender``, which is an ``address payable``.
Operators:
* ``<=``, ``<``, ``==``, ``!=``, ``>=`` and ``>``
.. warning::
If you convert a type that uses a larger byte size to an ``address``, for example ``bytes32``, then the ``address`` is truncated.
To reduce conversion ambiguity version 0.4.24 and higher of the compiler force you make the truncation explicit in the conversion.
Take for example the address ``0x111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFFCCCC``.
You can use ``address(uint160(bytes20(b)))``, which results in ``0x111122223333444455556666777788889999aAaa``,
or you can use ``address(uint160(uint256(b)))``, which results in ``0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc``.
.. note::
The distinction between ``address`` and ``address payable`` was introduced with version 0.5.0.
Also starting from that version, contracts do not derive from the address type, but can still be explicitly converted to
``address`` or to ``address payable``, if they have a payable fallback function.
.. _members-of-addresses:
Members of Addresses
^^^^^^^^^^^^^^^^^^^^
For a quick reference of all members of address, see :ref:`address_related`.
* ``balance`` and ``transfer``
It is possible to query the balance of an address using the property ``balance``
and to send Ether (in units of wei) to a payable address using the ``transfer`` function:
::
address payable x = address(0x123);
address myAddress = address(this);
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
The ``transfer`` function fails if the balance of the current contract is not large enough
or if the Ether transfer is rejected by the receiving account. The ``transfer`` function
reverts on failure.
.. note::
If ``x`` is a contract address, its code (more specifically: its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception.
* ``send``
Send is the low-level counterpart of ``transfer``. If the execution fails, the current contract will not stop with an exception, but ``send`` will return ``false``.
.. warning::
There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024
(this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order
to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better:
use a pattern where the recipient withdraws the money.
* ``call``, ``delegatecall`` and ``staticcall``
In order to interface with contracts that do not adhere to the ABI,
or to get more direct control over the encoding,
the functions ``call``, ``delegatecall`` and ``staticcall`` are provided.
They all take a single ``bytes memory`` parameter and
return the success condition (as a ``bool``) and the returned data
(``bytes memory``).
The functions ``abi.encode``, ``abi.encodePacked``, ``abi.encodeWithSelector``
and ``abi.encodeWithSignature`` can be used to encode structured data.
Example::
bytes memory payload = abi.encodeWithSignature("register(string)", "MyName");
(bool success, bytes memory returnData) = address(nameReg).call(payload);
require(success);
.. warning::
All these functions are low-level functions and should be used with care.
Specifically, any unknown contract might be malicious and if you call it, you
hand over control to that contract which could in turn call back into
your contract, so be prepared for changes to your state variables
when the call returns. The regular way to interact with other contracts
is to call a function on a contract object (``x.f()``).
.. note::
Previous versions of Solidity allowed these functions to receive
arbitrary arguments and would also handle a first argument of type
``bytes4`` differently. These edge cases were removed in version 0.5.0.
It is possible to adjust the supplied gas with the ``.gas()`` modifier::
address(nameReg).call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName"));
Similarly, the supplied Ether value can be controlled too::
address(nameReg).call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
Lastly, these modifiers can be combined. Their order does not matter::
address(nameReg).call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used.
.. note::
Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. This function was removed in version 0.5.0.
Since byzantium ``staticcall`` can be used as well. This is basically the same as ``call``, but will revert if the called function modifies the state in any way.
All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``.
.. note::
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
current contract using ``address(this).balance``.
.. index:: ! contract type, ! type; contract
.. _contract_types:
Contract Types
--------------
Every :ref:`contract<contracts>` defines its own type.
You can implicitly convert contracts to contracts they inherit from.
Contracts can be explicitly converted to and from all other contract types
and the ``address`` type.
Explicit conversion to and from the ``address payable`` type
is only possible if the contract type has a payable fallback function.
The conversion is still performed using ``address(x)`` and not
using ``address payable(x)``. You can find more information in the section about
the :ref:`address type<address>`.
.. note::
Before version 0.5.0, contracts directly derived from the address type
and there was no distinction between ``address`` and ``address payable``.
If you declare a local variable of contract type (`MyContract c`), you can call
functions on that contract. Take care to assign it from somewhere that is the
same contract type.
You can also instantiate contracts (which means they are newly created). You
can find more details in the :ref:`'Contracts via new'<creating-contracts>`
section.
The data representation of a contract is identical to that of the ``address``
type and this type is also used in the :ref:`ABI<ABI>`.
Contracts do not support any operators.
The members of contract types are the external functions of the contract
including public state variables.
For a contract ``C`` you can use ``type(C)`` to access
:ref:`type information<meta-type>` about the contract.
.. index:: byte array, bytes32
Fixed-size byte arrays
----------------------
The value types ``bytes1``, ``bytes2``, ``bytes3``, ..., ``bytes32``
hold a sequence of bytes from one to up to 32.
``byte`` is an alias for ``bytes1``.
Operators:
* Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``)
* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation)
* Shift operators: ``<<`` (left shift), ``>>`` (right shift)
* Index access: If ``x`` is of type ``bytesI``, then ``x[k]`` for ``0 <= k < I`` returns the ``k`` th byte (read-only).
The shifting operator works with any integer type as right operand (but
returns the type of the left operand), which denotes the number of bits to shift by.
Shifting by a negative amount causes a runtime exception.
Members:
* ``.length`` yields the fixed length of the byte array (read-only).
.. note::
The type ``byte[]`` is an array of bytes, but due to padding rules, it wastes
31 bytes of space for each element (except in storage). It is better to use the ``bytes``
type instead.
Dynamically-sized byte array
----------------------------
``bytes``:
Dynamically-sized byte array, see :ref:`arrays`. Not a value-type!
``string``:
Dynamically-sized UTF-8-encoded string, see :ref:`arrays`. Not a value-type!
.. index:: address, literal;address
.. _address_literals:
Address Literals
----------------
Hexadecimal literals that pass the address checksum test, for example
``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address payable`` type.
Hexadecimal literals that are between 39 and 41 digits
long and do not pass the checksum test produce
a warning and are treated as regular rational number literals.
.. note::
The mixed-case address checksum format is defined in `EIP-55 <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md>`_.
.. index:: literal, literal;rational
.. _rational_literals:
Rational and Integer Literals
-----------------------------
Integer literals are formed from a sequence of numbers in the range 0-9.
They are interpreted as decimals. For example, ``69`` means sixty nine.
Octal literals do not exist in Solidity and leading zeros are invalid.
Decimal fraction literals are formed by a ``.`` with at least one number on
one side. Examples include ``1.``, ``.1`` and ``1.3``.
Scientific notation is also supported, where the base can have fractions, while the exponent cannot.
Examples include ``2e10``, ``-2e10``, ``2e-10``, ``2.5e1``.
Underscores can be used to separate the digits of a numeric literal to aid readability.
For example, decimal ``123_000``, hexadecimal ``0x2eff_abde``, scientific decimal notation ``1_2e345_678`` are all valid.
Underscores are only allowed between two digits and only one consecutive underscore is allowed.
There is no additional semantic meaning added to a number literal containing underscores,
the underscores are ignored.
Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by
using them together with a non-literal expression or by explicit conversion).
This means that computations do not overflow and divisions do not truncate
in number literal expressions.
For example, ``(2**800 + 1) - 2**800`` results in the constant ``1`` (of type ``uint8``)
although intermediate results would not even fit the machine word size. Furthermore, ``.5 * 8`` results
in the integer ``4`` (although non-integers were used in between).
Any operator that can be applied to integers can also be applied to number literal expressions as
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).
.. note::
Solidity has a number literal type for each rational number.
Integer literals and rational number literals belong to number literal types.
Moreover, all number literal expressions (i.e. the expressions that
contain only number literals and operators) belong to number literal
types. So the number literal expressions ``1 + 2`` and ``2 + 1`` both
belong to the same number literal type for the rational number three.
.. warning::
Division on integer literals used to truncate in Solidity prior to version 0.4.0, but it now converts into a rational number, i.e. ``5 / 2`` is not equal to ``2``, but to ``2.5``.
.. note::
Number literal expressions are converted into a non-literal type as soon as they are used with non-literal
expressions. Disregarding types, the value of the expression assigned to ``b``
below evaluates to an integer. Because ``a`` is of type ``uint128``, the
expression ``2.5 + a`` has to have a proper type, though. Since there is no common type
for the type of ``2.5`` and ``uint128``, the Solidity compiler does not accept
this code.
::
uint128 a = 1;
uint128 b = 2.5 + a + 0.5;
.. index:: literal, literal;string, string
.. _string_literals:
String Literals and Types
-------------------------
String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``.
For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type.
String literals support the following escape characters:
- ``\<newline>`` (escapes an actual newline)
- ``\\`` (backslash)
- ``\'`` (single quote)
- ``\"`` (double quote)
- ``\b`` (backspace)
- ``\f`` (form feed)
- ``\n`` (newline)
- ``\r`` (carriage return)
- ``\t`` (tab)
- ``\v`` (vertical tab)
- ``\xNN`` (hex escape, see below)
- ``\uNNNN`` (unicode escape, see below)
``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence.
The string in the following example has a length of ten bytes.
It starts with a newline byte, followed by a double quote, a single
quote a backslash character and then (without separator) the
character sequence ``abcdef``.
::
"\n\"\'\\abc\
def"
Any unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to
terminate the string literal. Newline only terminates the string literal if it is not preceded by a ``\``.
.. index:: literal, bytes
Hexadecimal Literals
--------------------
Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values.
Hexadecimal literals behave like :ref:`string literals <string_literals>` and have the same convertibility restrictions.
.. index:: enum
.. _enums:
Enums
-----
Enums are one way to create a user-defined type in Solidity. They are explicitly convertible
to and from all integer types but implicit conversion is not allowed. The explicit conversion
from integer checks at runtime that the value lies inside the range of the enum and causes a failing assert otherwise.
Enums needs at least one member.
The data representation is the same as for enums in C: The options are represented by
subsequent unsigned integer values starting from ``0``.
::
pragma solidity >=0.4.16 <0.6.0;
contract test {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
ActionChoices choice;
ActionChoices constant defaultChoice = ActionChoices.GoStraight;
function setGoStraight() public {
choice = ActionChoices.GoStraight;
}
// Since enum types are not part of the ABI, the signature of "getChoice"
// will automatically be changed to "getChoice() returns (uint8)"
// for all matters external to Solidity. The integer type used is just
// large enough to hold all enum values, i.e. if you have more than 256 values,
// `uint16` will be used and so on.
function getChoice() public view returns (ActionChoices) {
return choice;
}
function getDefaultChoice() public pure returns (uint) {
return uint(defaultChoice);
}
}
.. index:: ! function type, ! type; function
.. _function_types:
Function Types
--------------
Function types are the types of functions. Variables of function type
can be assigned from functions and function parameters of function type
can be used to pass functions to and return functions from function calls.
Function types come in two flavours - *internal* and *external* functions:
Internal functions can only be called inside the current contract (more specifically,
inside the current code unit, which also includes internal library functions
and inherited functions) because they cannot be executed outside of the
context of the current contract. Calling an internal function is realized
by jumping to its entry label, just like when calling a function of the current
contract internally.
External functions consist of an address and a function signature and they can
be passed via and returned from external function calls.
Function types are notated as follows::
function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]
In contrast to the parameter types, the return types cannot be empty - if the
function type should not return anything, the whole ``returns (<return types>)``
part has to be omitted.
By default, function types are internal, so the ``internal`` keyword can be
omitted. Note that this only applies to function types. Visibility has
to be specified explicitly for functions defined in contracts, they
do not have a default.
Conversions:
A value of external function type can be explicitly converted to ``address``
resulting in the address of the contract of the function.
A function type ``A`` is implicitly convertible to a function type ``B`` if and only if
their parameter types are identical, their return types are identical,
their internal/external property is identical and the state mutability of ``A``
is not more restrictive than the state mutability of ``B``. In particular:
- ``pure`` functions can be converted to ``view`` and ``non-payable`` functions
- ``view`` functions can be converted to ``non-payable`` functions
- ``payable`` functions can be converted to ``non-payable`` functions
No other conversions between function types are possible.
The rule about ``payable`` and ``non-payable`` might be a little
confusing, but in essence, if a function is ``payable``, this means that it
also accepts a payment of zero Ether, so it also is ``non-payable``.
On the other hand, a ``non-payable`` function will reject Ether sent to it,
so ``non-payable`` functions cannot be converted to ``payable`` functions.
If a function type variable is not initialised, calling it results
in a failed assertion. The same happens if you call a function after using ``delete``
on it.
If external function types are used outside of the context of Solidity,
they are treated as the ``function`` type, which encodes the address
followed by the function identifier together in a single ``bytes24`` type.
Note that public functions of the current contract can be used both as an
internal and as an external function. To use ``f`` as an internal function,
just use ``f``, if you want to use its external form, use ``this.f``.
Members:
Public (or external) functions also have a special member called ``selector``,
which returns the :ref:`ABI function selector <abi_function_selector>`::
pragma solidity >=0.4.16 <0.6.0;
contract Selector {
function f() public pure returns (bytes4) {
return this.f.selector;
}
}
Example that shows how to use internal function types::
pragma solidity >=0.4.16 <0.6.0;
library ArrayUtils {
// internal functions can be used in internal library functions because
// they will be part of the same code context
function map(uint[] memory self, function (uint) pure returns (uint) f)
internal
pure
returns (uint[] memory r)
{
r = new uint[](self.length);
for (uint i = 0; i < self.length; i++) {
r[i] = f(self[i]);
}
}
function reduce(
uint[] memory self,
function (uint, uint) pure returns (uint) f
)
internal
pure
returns (uint r)
{
r = self[0];
for (uint i = 1; i < self.length; i++) {
r = f(r, self[i]);
}
}
function range(uint length) internal pure returns (uint[] memory r) {
r = new uint[](length);
for (uint i = 0; i < r.length; i++) {
r[i] = i;
}
}
}
contract Pyramid {
using ArrayUtils for *;
function pyramid(uint l) public pure returns (uint) {
return ArrayUtils.range(l).map(square).reduce(sum);
}
function square(uint x) internal pure returns (uint) {
return x * x;
}
function sum(uint x, uint y) internal pure returns (uint) {
return x + y;
}
}
Another example that uses external function types::
pragma solidity >=0.4.22 <0.6.0;
contract Oracle {
struct Request {
bytes data;
function(uint) external callback;
}
Request[] requests;
event NewRequest(uint);
function query(bytes memory data, function(uint) external callback) public {
requests.push(Request(data, callback));
emit NewRequest(requests.length - 1);
}
function reply(uint requestID, uint response) public {
// Here goes the check that the reply comes from a trusted source
requests[requestID].callback(response);
}
}
contract OracleUser {
Oracle constant oracle = Oracle(0x1234567); // known contract
uint exchangeRate;
function buySomething() public {
oracle.query("USD", this.oracleResponse);
}
function oracleResponse(uint response) public {
require(
msg.sender == address(oracle),
"Only oracle can call this."
);
exchangeRate = response;
}
}
.. note::
Lambda or inline functions are planned but not yet supported.

View File

@ -103,11 +103,11 @@ Block and Transaction Properties
values will be zero.
.. note::
The function ``blockhash`` was previously known as ``block.blockhash``. It was deprecated in
The function ``blockhash`` was previously known as ``block.blockhash``, which was deprecated in
version 0.4.22 and removed in version 0.5.0.
.. note::
The function ``gasleft`` was previously known as ``msg.gas``. It was deprecated in
The function ``gasleft`` was previously known as ``msg.gas``, which was deprecated in
version 0.4.21 and removed in version 0.5.0.
.. index:: abi, encoding, packed
@ -199,6 +199,10 @@ Members of Address Types
For more information, see the section on :ref:`address`.
.. warning::
You should avoid using ``.call()`` whenever possible when executing another contract function as it bypasses type checking,
function existence check, and argument packing.
.. warning::
There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024
(this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order
@ -240,3 +244,33 @@ Furthermore, all functions of the current contract are callable directly includi
.. note::
Prior to version 0.5.0, there was a function called ``suicide`` with the same
semantics as ``selfdestruct``.
.. index:: type, creationCode, runtimeCode
.. _meta-type:
Type Information
----------------
The expression ``type(X)`` can be used to retrieve information about the
type ``X``. Currently, there is limited support for this feature, but
it might be expanded in the future. The following properties are
available for a conract type ``C``:
``type(C).creationCode``:
Memory byte array that contains the creation bytecode of the contract.
This can be used in inline assembly to build custom creation routines,
especially by using the ``create2`` opcode.
This property can **not** be accessed in the contract itself or any
derived contract. It causes the bytecode to be included in the bytecode
of the call site and thus circular references like that are not possible.
``type(C).runtimeCode``:
Memory byte array that contains the runtime bytecode of the contract.
This is the code that is usually deployed by the constructor of ``C``.
If ``C`` has a constructor that uses inline assembly, this might be
different from the actually deployed bytecode. Also note that libraries
modify their runtime bytecode at time of deployment to guard against
regular calls.
The same restrictions as with ``.creationCode`` also apply for this
property.

View File

@ -130,9 +130,10 @@ Restrictions on the Grammar
---------------------------
Switches must have at least one case (including the default case).
If all possible values of the expression is covered, the default case should
not be allowed (i.e. a switch with a ``bool`` expression and having both a
true and false case should not allow a default case).
If all possible values of the expression are covered, a default case should
not be allowed (i.e. a switch with a ``bool`` expression that has both a
true and a false case should not allow a default case). All case values need to
have the same type.
Every expression evaluates to zero or more values. Identifiers and Literals
evaluate to exactly
@ -171,7 +172,7 @@ As an exception, identifiers defined in the "init" part of the for-loop
(the first block) are visible in all other parts of the for-loop
(but not outside of the loop).
Identifiers declared in the other parts of the for loop respect the regular
syntatical scoping rules.
syntactical scoping rules.
The parameters and return parameters of functions are visible in the
function body and their names cannot overlap.

View File

@ -75,4 +75,47 @@ private:
V const* m_firstCycleVertex = nullptr;
};
/**
* Generic breadth first search.
*
* Example: Gather all (recursive) children in a graph starting at (and including) ``root``:
*
* Node const* root = ...;
* std::set<Node> allNodes = BreadthFirstSearch<Node>{{root}}.run([](Node const& _node, auto&& _addChild) {
* // Potentially process ``_node``.
* for (Node const& _child: _node.children())
* // Potentially filter the children to be visited.
* _addChild(_child);
* }).visited;
*
* Note that the order of the traversal is *non-deterministic* (the children are stored in a std::set of pointers).
*/
template<typename V>
struct BreadthFirstSearch
{
/// Runs the breadth first search. The verticesToTraverse member of the struct needs to be initialized.
/// @param _forEachChild is a callable of the form [...](V const& _node, auto&& _addChild) { ... }
/// that is called for each visited node and is supposed to call _addChild(childNode) for every child
/// node of _node.
template<typename ForEachChild>
BreadthFirstSearch& run(ForEachChild&& _forEachChild)
{
while (!verticesToTraverse.empty())
{
V const* v = *verticesToTraverse.begin();
verticesToTraverse.erase(verticesToTraverse.begin());
visited.insert(v);
_forEachChild(*v, [this](V const& _vertex) {
if (!visited.count(&_vertex))
verticesToTraverse.insert(&_vertex);
});
}
return *this;
}
std::set<V const*> verticesToTraverse;
std::set<V const*> visited{};
};
}

View File

@ -1,7 +1,6 @@
set(sources
Algorithms.h
Assertions.h
boost_multiprecision_number_compare_bug_workaround.hpp
Common.h
CommonData.cpp
CommonData.h

View File

@ -39,26 +39,13 @@
#include <libdevcore/vector_ref.h>
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif // defined(__GNUC__)
// See https://github.com/ethereum/libweb3core/commit/90680a8c25bfb48b24371b4abcacde56c181517c
// See https://svn.boost.org/trac/boost/ticket/11328
// Bob comment - perhaps we should just HARD FAIL here with Boost-1.58.00?
// It is quite old now, and requiring end-users to use a newer Boost release is probably not unreasonable.
#include <boost/version.hpp>
#if (BOOST_VERSION == 105800)
#include "boost_multiprecision_number_compare_bug_workaround.hpp"
#endif // (BOOST_VERSION == 105800)
#if (BOOST_VERSION < 106500)
#error "Unsupported Boost version. At least 1.65 required."
#endif
#include <boost/multiprecision/cpp_int.hpp>
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif // defined(__GNUC__)
#include <map>
#include <vector>
#include <functional>

View File

@ -263,6 +263,59 @@ void iterateReplacing(std::vector<T>& _vector, const F& _f)
_vector = std::move(modifiedVector);
}
namespace detail
{
template <typename T, typename F, std::size_t... I>
void iterateReplacingWindow(std::vector<T>& _vector, F const& _f, std::index_sequence<I...>)
{
// Concept: _f must be Callable, must accept sizeof...(I) parameters of type T&, must return optional<vector<T>>
bool useModified = false;
std::vector<T> modifiedVector;
size_t i = 0;
for (; i + sizeof...(I) <= _vector.size(); ++i)
{
if (boost::optional<std::vector<T>> r = _f(_vector[i + I]...))
{
if (!useModified)
{
std::move(_vector.begin(), _vector.begin() + i, back_inserter(modifiedVector));
useModified = true;
}
modifiedVector += std::move(*r);
i += sizeof...(I) - 1;
}
else if (useModified)
modifiedVector.emplace_back(std::move(_vector[i]));
}
if (useModified)
{
for (; i < _vector.size(); ++i)
modifiedVector.emplace_back(std::move(_vector[i]));
_vector = std::move(modifiedVector);
}
}
}
/// Function that iterates over the vector @param _vector,
/// calling the function @param _f on sequences of @tparam N of its
/// elements. If @param _f returns a vector, these elements are replaced by
/// the returned vector and the iteration continues with the next @tparam N elements.
/// If the function does not return a vector, the iteration continues with an overlapping
/// sequence of @tparam N elements that starts with the second element of the previous
/// iteration.
/// During the iteration, the original vector is only valid
/// on the current element and after that. The actual replacement takes
/// place at the end, but already visited elements might be invalidated.
/// If nothing is replaced, no copy is performed.
template <std::size_t N, typename T, typename F>
void iterateReplacingWindow(std::vector<T>& _vector, F const& _f)
{
// Concept: _f must be Callable, must accept N parameters of type T&, must return optional<vector<T>>
detail::iterateReplacingWindow(_vector, _f, std::make_index_sequence<N>{});
}
/// @returns true iff @a _str passess the hex address checksum test.
/// @param _strict if false, hex strings with only uppercase or only lowercase letters
/// are considered valid.
@ -275,4 +328,10 @@ std::string getChecksummedAddress(std::string const& _addr);
bool isValidHex(std::string const& _string);
bool isValidDecimal(std::string const& _string);
template<typename Container, typename Compare>
bool containerEqual(Container const& _lhs, Container const& _rhs, Compare&& _compare)
{
return std::equal(std::begin(_lhs), std::end(_lhs), std::begin(_rhs), std::end(_rhs), std::forward<Compare>(_compare));
}
}

View File

@ -128,26 +128,6 @@ int dev::readStandardInputChar()
return cin.get();
}
boost::filesystem::path dev::weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path)
{
if (boost::filesystem::exists(_path))
return boost::filesystem::canonical(_path);
else
{
boost::filesystem::path head(_path);
boost::filesystem::path tail;
for (auto it = --_path.end(); !head.empty(); --it)
{
if (boost::filesystem::exists(head))
break;
tail = (*it) / tail;
head.remove_filename();
}
head = boost::filesystem::canonical(head);
return head / tail;
}
}
string dev::absolutePath(string const& _path, string const& _reference)
{
boost::filesystem::path p(_path);

View File

@ -50,10 +50,6 @@ std::string toString(_T const& _t)
return o.str();
}
/// Partial implementation of boost::filesystem::weakly_canonical (available in boost>=1.60).
/// Should be replaced by the boost implementation as soon as support for boost<1.60 can be dropped.
boost::filesystem::path weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path);
/// @returns the absolute path corresponding to @a _path relative to @a _reference.
std::string absolutePath(std::string const& _path, std::string const& _reference);

View File

@ -1,520 +0,0 @@
// This is a copy of boost/multiprecision/detail/number_compare.hpp from boost 1.59 to replace buggy version from 1.58.
#ifdef BOOST_MP_COMPARE_HPP
#error This bug workaround header must be included before original boost/multiprecision/detail/number_compare.hpp
#endif
///////////////////////////////////////////////////////////////////////////////
// Copyright 2012 John Maddock. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_MP_COMPARE_HPP
#define BOOST_MP_COMPARE_HPP
// A copy of boost/multiprecision/traits/is_backend.hpp
#ifndef BOOST_MP_IS_BACKEND_HPP
#define BOOST_MP_IS_BACKEND_HPP
#include <boost/mpl/has_xxx.hpp>
#include <boost/type_traits/conditional.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/multiprecision/detail/number_base.hpp>
#include <boost/multiprecision/detail/generic_interconvert.hpp>
namespace boost{ namespace multiprecision{ namespace detail{
BOOST_MPL_HAS_XXX_TRAIT_DEF(signed_types)
BOOST_MPL_HAS_XXX_TRAIT_DEF(unsigned_types)
BOOST_MPL_HAS_XXX_TRAIT_DEF(float_types)
template <class T>
struct is_backend
{
static const bool value = has_signed_types<T>::value && has_unsigned_types<T>::value && has_float_types<T>::value;
};
template <class Backend>
struct other_backend
{
typedef typename boost::conditional<
boost::is_same<number<Backend>, number<Backend, et_on> >::value,
number<Backend, et_off>, number<Backend, et_on> >::type type;
};
template <class B, class V>
struct number_from_backend
{
typedef typename boost::conditional <
boost::is_convertible<V, number<B> >::value,
number<B>,
typename other_backend<B>::type > ::type type;
};
template <bool b, class T, class U>
struct is_first_backend_imp{ static const bool value = false; };
template <class T, class U>
struct is_first_backend_imp<true, T, U>{ static const bool value = is_convertible<U, number<T, et_on> >::value || is_convertible<U, number<T, et_off> >::value; };
template <class T, class U>
struct is_first_backend : is_first_backend_imp<is_backend<T>::value, T, U> {};
template <bool b, class T, class U>
struct is_second_backend_imp{ static const bool value = false; };
template <class T, class U>
struct is_second_backend_imp<true, T, U>{ static const bool value = is_convertible<T, number<U> >::value || is_convertible<T, number<U, et_off> >::value; };
template <class T, class U>
struct is_second_backend : is_second_backend_imp<is_backend<U>::value, T, U> {};
}
}
}
#endif // BOOST_MP_IS_BACKEND_HPP
//
// Comparison operators for number.
//
namespace boost{ namespace multiprecision{
namespace default_ops{
template <class B>
inline bool eval_eq(const B& a, const B& b)
{
return a.compare(b) == 0;
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_eq(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_eq(t.backend(), b);
}
template <class B>
inline bool eval_lt(const B& a, const B& b)
{
return a.compare(b) < 0;
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_lt(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_lt(t.backend(), b);
}
template <class B>
inline bool eval_gt(const B& a, const B& b)
{
return a.compare(b) > 0;
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_gt(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_gt(t.backend(), b);
}
} // namespace default_ops
namespace detail{
template <class Num, class Val>
struct is_valid_mixed_compare : public mpl::false_ {};
template <class B, expression_template_option ET, class Val>
struct is_valid_mixed_compare<number<B, ET>, Val> : public is_convertible<Val, number<B, ET> > {};
template <class B, expression_template_option ET>
struct is_valid_mixed_compare<number<B, ET>, number<B, ET> > : public mpl::false_ {};
template <class B, expression_template_option ET, class tag, class Arg1, class Arg2, class Arg3, class Arg4>
struct is_valid_mixed_compare<number<B, ET>, expression<tag, Arg1, Arg2, Arg3, Arg4> >
: public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {};
template <class tag, class Arg1, class Arg2, class Arg3, class Arg4, class B, expression_template_option ET>
struct is_valid_mixed_compare<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >
: public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {};
template <class Backend, expression_template_option ExpressionTemplates>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value != number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>&)
{
return false;
}
template <class Backend, expression_template_option ExpressionTemplates>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value == number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>& a)
{
using default_ops::eval_fpclassify;
return eval_fpclassify(a.backend()) == FP_NAN;
}
template <class Arithmetic>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value != number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic&)
{
return false;
}
template <class Arithmetic>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value == number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic& a)
{
return (boost::math::isnan)(a);
}
template <class T, class U>
inline BOOST_CONSTEXPR bool is_unordered_comparison(const T& a, const U& b)
{
return is_unordered_value(a) || is_unordered_value(b);
}
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator == (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator == (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator == (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator == (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return eval_eq(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return eval_eq(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_eq;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return eval_eq(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator != (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator != (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(a.backend(), number<Backend, et_on>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator != (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(b.backend(), number<Backend, et_on>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator != (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return true;
return !eval_eq(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_eq;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return true;
return !eval_eq(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_eq;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return true;
return !eval_eq(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator < (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator < (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator < (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator < (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return eval_gt(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return eval_lt(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_lt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return eval_lt(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator > (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator > (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator > (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator > (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return a > t;
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return t > b;
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_gt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return t > t2;
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator <= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator <= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator <= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator <= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
if(detail::is_unordered_value(a) || detail::is_unordered_value(b))
return false;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return !eval_lt(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return !eval_gt(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_gt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return !eval_gt(t.backend(), t2.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator >= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator >= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
operator >= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator >= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_gt;
result_type t(b);
if(detail::is_unordered_comparison(a, t)) return false;
return !eval_gt(t.backend(), result_type::canonical_value(a));
}
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
using default_ops::eval_lt;
result_type t(a);
if(detail::is_unordered_comparison(t, b)) return false;
return !eval_lt(t.backend(), result_type::canonical_value(b));
}
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
{
using default_ops::eval_lt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return !eval_lt(t.backend(), t2.backend());
}
}} // namespaces
#endif // BOOST_MP_COMPARE_HPP

View File

@ -44,7 +44,7 @@ template <class S> S modWorkaround(S const& _a, S const& _b)
return (S)(bigint(_a) % bigint(_b));
}
// This part of simplificationRuleList below was split out to prevent
// simplificationRuleList below was split up into parts to prevent
// stack overflows in the JavaScript optimizer for emscripten builds
// that affected certain browser versions.
template <class Pattern>
@ -52,8 +52,8 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
Pattern A,
Pattern B,
Pattern C,
Pattern X,
Pattern Y
Pattern,
Pattern
)
{
return std::vector<SimplificationRule<Pattern>> {
@ -96,8 +96,20 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
if (A.d() > 255)
return u256(0);
return B.d() >> unsigned(A.d());
}, false},
}, false}
};
}
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
Pattern,
Pattern,
Pattern,
Pattern X,
Pattern
)
{
return std::vector<SimplificationRule<Pattern>> {
// invariants involving known constants
{{Instruction::ADD, {X, 0}}, [=]{ return X; }, false},
{{Instruction::ADD, {0, X}}, [=]{ return X; }, false},
@ -128,7 +140,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
{{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true},
{{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
{{Instruction::EQ, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
};
}
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart3(
Pattern,
Pattern,
Pattern,
Pattern X,
Pattern
)
{
return std::vector<SimplificationRule<Pattern>> {
// operations involving an expression and itself
{{Instruction::AND, {X, X}}, [=]{ return X; }, true},
{{Instruction::OR, {X, X}}, [=]{ return X; }, true},
@ -139,8 +163,20 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
{{Instruction::SLT, {X, X}}, [=]{ return u256(0); }, true},
{{Instruction::GT, {X, X}}, [=]{ return u256(0); }, true},
{{Instruction::SGT, {X, X}}, [=]{ return u256(0); }, true},
{{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true},
{{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true}
};
}
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4(
Pattern,
Pattern,
Pattern,
Pattern X,
Pattern Y
)
{
return std::vector<SimplificationRule<Pattern>> {
// logical instruction combinations
{{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }, false},
{{Instruction::XOR, {X, {Instruction::XOR, {X, Y}}}}, [=]{ return Y; }, true},
@ -163,16 +199,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
}
// This part of simplificationRuleList below was split out to prevent
// stack overflows in the JavaScript optimizer for emscripten builds
// that affected certain browser versions.
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
Pattern A,
Pattern B,
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
Pattern,
Pattern,
Pattern,
Pattern X,
Pattern Y
Pattern
)
{
std::vector<SimplificationRule<Pattern>> rules;
@ -207,7 +240,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
false
});
}
return rules;
}
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart6(
Pattern,
Pattern,
Pattern,
Pattern X,
Pattern Y
)
{
std::vector<SimplificationRule<Pattern>> rules;
// Double negation of opcodes with boolean result
for (auto const& op: std::vector<Instruction>{
Instruction::EQ,
@ -234,6 +279,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
false
});
return rules;
}
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
Pattern A,
Pattern B,
Pattern,
Pattern X,
Pattern Y
)
{
std::vector<SimplificationRule<Pattern>> rules;
// Associative operations
for (auto const& opFun: std::vector<std::pair<Instruction,std::function<u256(u256 const&,u256 const&)>>>{
{Instruction::ADD, std::plus<u256>()},
@ -274,6 +332,20 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
}
}
return rules;
}
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart8(
Pattern A,
Pattern,
Pattern,
Pattern X,
Pattern Y
)
{
std::vector<SimplificationRule<Pattern>> rules;
// move constants across subtractions
rules += std::vector<SimplificationRule<Pattern>>{
{
@ -322,6 +394,12 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
std::vector<SimplificationRule<Pattern>> rules;
rules += simplificationRuleListPart1(A, B, C, X, Y);
rules += simplificationRuleListPart2(A, B, C, X, Y);
rules += simplificationRuleListPart3(A, B, C, X, Y);
rules += simplificationRuleListPart4(A, B, C, X, Y);
rules += simplificationRuleListPart5(A, B, C, X, Y);
rules += simplificationRuleListPart6(A, B, C, X, Y);
rules += simplificationRuleListPart7(A, B, C, X, Y);
rules += simplificationRuleListPart8(A, B, C, X, Y);
return rules;
}

View File

@ -49,6 +49,26 @@ struct SourceLocation
bool isEmpty() const { return start == -1 && end == -1; }
/// @returns the smallest SourceLocation that contains both @param _a and @param _b.
/// Assumes that @param _a and @param _b refer to the same source (exception: if the source of either one
/// is unset, the source of the other will be used for the result, even if that is unset as well).
/// Invalid start and end positions (with value of -1) are ignored (if start or end are -1 for both @param _a and
/// @param _b, then start resp. end of the result will be -1 as well).
static SourceLocation smallestCovering(SourceLocation _a, SourceLocation const& _b)
{
if (!_a.source)
_a.source = _b.source;
if (_a.start < 0)
_a.start = _b.start;
else if (_b.start >= 0 && _b.start < _a.start)
_a.start = _b.start;
if (_b.end > _a.end)
_a.end = _b.end;
return _a;
}
int start = -1;
int end = -1;
std::shared_ptr<CharStream> source;

View File

@ -58,7 +58,9 @@ SourceReference SourceReferenceExtractor::extract(SourceLocation const* _locatio
int locationLength = isMultiline ? line.length() - start.column : end.column - start.column;
if (locationLength > 150)
{
line = line.substr(0, start.column + 35) + " ... " + line.substr(end.column - 35);
int const lhs = start.column + 35;
int const rhs = (isMultiline ? line.length() : end.column) - 35;
line = line.substr(0, lhs) + " ... " + line.substr(rhs);
end.column = start.column + 75;
locationLength = 75;
}

View File

@ -180,6 +180,7 @@ namespace langutil
K(CallData, "calldata", 0) \
K(Struct, "struct", 0) \
K(Throw, "throw", 0) \
K(Type, "type", 0) \
K(Using, "using", 0) \
K(Var, "var", 0) \
K(View, "view", 0) \
@ -256,7 +257,6 @@ namespace langutil
K(Supports, "supports", 0) \
K(Switch, "switch", 0) \
K(Try, "try", 0) \
K(Type, "type", 0) \
K(Typedef, "typedef", 0) \
K(TypeOf, "typeof", 0) \
K(Unchecked, "unchecked", 0) \

View File

@ -1,4 +1,7 @@
if (EMSCRIPTEN)
# Specify which functions to export in soljson.js.
# Note that additional Emscripten-generated methods needed by solc-js are
# defined to be exported in cmake/EthCompilerSettings.cmake.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\"]' -s RESERVED_FUNCTION_POINTERS=20")
add_executable(soljson libsolc.cpp libsolc.h)
target_link_libraries(soljson PRIVATE solidity)

View File

@ -18,6 +18,7 @@
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
#include <liblangutil/SourceLocation.h>
#include <libdevcore/Algorithms.h>
#include <boost/range/algorithm/sort.hpp>
using namespace std;
@ -36,6 +37,7 @@ bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function)
{
auto const& functionFlow = m_cfg.functionFlow(_function);
checkUninitializedAccess(functionFlow.entry, functionFlow.exit);
checkUnreachable(functionFlow.entry, functionFlow.exit, functionFlow.revert);
}
return false;
}
@ -145,3 +147,35 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
}
}
}
void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert) const
{
// collect all nodes reachable from the entry point
std::set<CFGNode const*> reachable = BreadthFirstSearch<CFGNode>{{_entry}}.run(
[](CFGNode const& _node, auto&& _addChild) {
for (CFGNode const* exit: _node.exits)
_addChild(*exit);
}
).visited;
// traverse all paths backwards from exit and revert
// and extract (valid) source locations of unreachable nodes into sorted set
std::set<SourceLocation> unreachable;
BreadthFirstSearch<CFGNode>{{_exit, _revert}}.run(
[&](CFGNode const& _node, auto&& _addChild) {
if (!reachable.count(&_node) && !_node.location.isEmpty())
unreachable.insert(_node.location);
for (CFGNode const* entry: _node.entries)
_addChild(*entry);
}
);
for (auto it = unreachable.begin(); it != unreachable.end();)
{
SourceLocation location = *it++;
// Extend the location, as long as the next location overlaps (unreachable is sorted).
for (; it != unreachable.end() && it->start <= location.end; ++it)
location.end = std::max(location.end, it->end);
m_errorReporter.warning(location, "Unreachable code.");
}
}

View File

@ -38,6 +38,9 @@ public:
private:
/// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit.
void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const;
/// Checks for unreachable code, i.e. code ending in @param _exit or @param _revert
/// that can not be reached from @param _entry.
void checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert) const;
CFG const& m_cfg;
langutil::ErrorReporter& m_errorReporter;

View File

@ -18,6 +18,7 @@
#include <libsolidity/analysis/ControlFlowBuilder.h>
using namespace dev;
using namespace langutil;
using namespace solidity;
using namespace std;
@ -53,6 +54,7 @@ bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
case Token::Or:
case Token::And:
{
visitNode(_operation);
appendControlFlow(_operation.leftExpression());
auto nodes = splitFlow<2>();
@ -62,14 +64,14 @@ bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
return false;
}
default:
break;
return ASTConstVisitor::visit(_operation);
}
return ASTConstVisitor::visit(_operation);
}
bool ControlFlowBuilder::visit(Conditional const& _conditional)
{
solAssert(!!m_currentNode, "");
visitNode(_conditional);
_conditional.condition().accept(*this);
@ -86,6 +88,7 @@ bool ControlFlowBuilder::visit(Conditional const& _conditional)
bool ControlFlowBuilder::visit(IfStatement const& _ifStatement)
{
solAssert(!!m_currentNode, "");
visitNode(_ifStatement);
_ifStatement.condition().accept(*this);
@ -106,6 +109,7 @@ bool ControlFlowBuilder::visit(IfStatement const& _ifStatement)
bool ControlFlowBuilder::visit(ForStatement const& _forStatement)
{
solAssert(!!m_currentNode, "");
visitNode(_forStatement);
if (_forStatement.initializationExpression())
_forStatement.initializationExpression()->accept(*this);
@ -139,6 +143,7 @@ bool ControlFlowBuilder::visit(ForStatement const& _forStatement)
bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement)
{
solAssert(!!m_currentNode, "");
visitNode(_whileStatement);
if (_whileStatement.isDoWhile())
{
@ -183,28 +188,31 @@ bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement)
return false;
}
bool ControlFlowBuilder::visit(Break const&)
bool ControlFlowBuilder::visit(Break const& _break)
{
solAssert(!!m_currentNode, "");
solAssert(!!m_breakJump, "");
visitNode(_break);
connect(m_currentNode, m_breakJump);
m_currentNode = newLabel();
return false;
}
bool ControlFlowBuilder::visit(Continue const&)
bool ControlFlowBuilder::visit(Continue const& _continue)
{
solAssert(!!m_currentNode, "");
solAssert(!!m_continueJump, "");
visitNode(_continue);
connect(m_currentNode, m_continueJump);
m_currentNode = newLabel();
return false;
}
bool ControlFlowBuilder::visit(Throw const&)
bool ControlFlowBuilder::visit(Throw const& _throw)
{
solAssert(!!m_currentNode, "");
solAssert(!!m_revertNode, "");
visitNode(_throw);
connect(m_currentNode, m_revertNode);
m_currentNode = newLabel();
return false;
@ -232,6 +240,7 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
{
case FunctionType::Kind::Revert:
solAssert(!!m_revertNode, "");
visitNode(_functionCall);
_functionCall.expression().accept(*this);
ASTNode::listAccept(_functionCall.arguments(), *this);
connect(m_currentNode, m_revertNode);
@ -241,6 +250,7 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::Assert:
{
solAssert(!!m_revertNode, "");
visitNode(_functionCall);
_functionCall.expression().accept(*this);
ASTNode::listAccept(_functionCall.arguments(), *this);
connect(m_currentNode, m_revertNode);
@ -314,6 +324,7 @@ bool ControlFlowBuilder::visit(Return const& _return)
{
solAssert(!!m_currentNode, "");
solAssert(!!m_returnNode, "");
visitNode(_return);
if (_return.expression())
{
appendControlFlow(*_return.expression());
@ -327,11 +338,12 @@ bool ControlFlowBuilder::visit(Return const& _return)
}
connect(m_currentNode, m_returnNode);
m_currentNode = newLabel();
return true;
return false;
}
bool ControlFlowBuilder::visit(FunctionTypeName const&)
bool ControlFlowBuilder::visit(FunctionTypeName const& _functionTypeName)
{
visitNode(_functionTypeName);
// Do not visit the parameters and return values of a function type name.
// We do not want to consider them as variable declarations for the control flow graph.
return false;
@ -340,6 +352,7 @@ bool ControlFlowBuilder::visit(FunctionTypeName const&)
bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly)
{
solAssert(!!m_currentNode, "");
visitNode(_inlineAssembly);
for (auto const& ref: _inlineAssembly.annotation().externalReferences)
{
if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
@ -355,6 +368,7 @@ bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly)
bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration)
{
solAssert(!!m_currentNode, "");
visitNode(_variableDeclaration);
m_currentNode->variableOccurrences.emplace_back(
_variableDeclaration,
@ -382,6 +396,7 @@ bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration)
bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
{
solAssert(!!m_currentNode, "");
visitNode(_variableDeclarationStatement);
for (auto const& var: _variableDeclarationStatement.declarations())
if (var)
@ -417,6 +432,7 @@ bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDecl
bool ControlFlowBuilder::visit(Identifier const& _identifier)
{
solAssert(!!m_currentNode, "");
visitNode(_identifier);
if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
m_currentNode->variableOccurrences.emplace_back(
@ -430,7 +446,12 @@ bool ControlFlowBuilder::visit(Identifier const& _identifier)
return true;
}
bool ControlFlowBuilder::visitNode(ASTNode const& _node)
{
solAssert(!!m_currentNode, "");
m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, _node.location());
return true;
}
void ControlFlowBuilder::appendControlFlow(ASTNode const& _node)
{

View File

@ -66,6 +66,11 @@ private:
bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
bool visit(Identifier const& _identifier) override;
protected:
bool visitNode(ASTNode const&) override;
private:
/// Appends the control flow of @a _node to the current control flow.
void appendControlFlow(ASTNode const& _node);
@ -77,9 +82,6 @@ private:
/// Creates an arc from @a _from to @a _to.
static void connect(CFGNode* _from, CFGNode* _to);
private:
/// Splits the control flow starting at the current node into n paths.
/// m_currentNode is set to nullptr and has to be set manually or
/// using mergeFlow later.

View File

@ -20,6 +20,7 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/SourceLocation.h>
#include <map>
#include <memory>
@ -98,6 +99,8 @@ struct CFGNode
/// Variable occurrences in the node.
std::vector<VariableOccurrence> variableOccurrences;
// Source location of this control flow block.
langutil::SourceLocation location;
};
/** Describes the control flow of a function. */

View File

@ -61,7 +61,14 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction))
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
make_shared<MagicVariableDeclaration>("type", make_shared<FunctionType>(
strings{"address"} /* accepts any contract type, handled by the type checker */,
strings{} /* returns a MagicType, handled by the type checker */,
FunctionType::Kind::MetaType,
false,
StateMutability::Pure
)),
})
{
}

View File

@ -32,6 +32,56 @@ using namespace dev;
using namespace langutil;
using namespace dev::solidity;
/**
* Helper class that determines whether a contract's constructor uses inline assembly.
*/
class dev::solidity::ConstructorUsesAssembly
{
public:
/// @returns true if and only if the contract's or any of its bases' constructors
/// use inline assembly.
bool check(ContractDefinition const& _contract)
{
for (auto const* base: _contract.annotation().linearizedBaseContracts)
if (checkInternal(*base))
return true;
return false;
}
private:
class Checker: public ASTConstVisitor
{
public:
Checker(FunctionDefinition const& _f) { _f.accept(*this); }
bool visit(InlineAssembly const&) override { assemblySeen = true; return false; }
bool assemblySeen = false;
};
bool checkInternal(ContractDefinition const& _contract)
{
if (!m_usesAssembly.count(&_contract))
{
bool usesAssembly = false;
if (_contract.constructor())
usesAssembly = Checker{*_contract.constructor()}.assemblySeen;
m_usesAssembly[&_contract] = usesAssembly;
}
return m_usesAssembly[&_contract];
}
map<ContractDefinition const*, bool> m_usesAssembly;
};
StaticAnalyzer::StaticAnalyzer(ErrorReporter& _errorReporter):
m_errorReporter(_errorReporter)
{
}
StaticAnalyzer::~StaticAnalyzer()
{
}
bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit)
{
_sourceUnit.accept(*this);
@ -152,6 +202,18 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
_memberAccess.location(),
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
);
else if (type->kind() == MagicType::Kind::MetaType && _memberAccess.memberName() == "runtimeCode")
{
if (!m_constructorUsesAssembly)
m_constructorUsesAssembly = make_unique<ConstructorUsesAssembly>();
ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument());
if (m_constructorUsesAssembly->check(contract.contractDefinition()))
m_errorReporter.warning(
_memberAccess.location(),
"The constructor of the contract (or its base) uses inline assembly. "
"Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode."
);
}
}
if (_memberAccess.memberName() == "callcode")

View File

@ -38,6 +38,8 @@ namespace dev
namespace solidity
{
class ConstructorUsesAssembly;
/**
* The module that performs static analysis on the AST.
@ -49,7 +51,8 @@ class StaticAnalyzer: private ASTConstVisitor
{
public:
/// @param _errorReporter provides the error logging functionality.
explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter);
~StaticAnalyzer();
/// Performs static analysis on the given source unit and all of its sub-nodes.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
@ -85,6 +88,10 @@ private:
/// when traversing.
std::map<std::pair<size_t, VariableDeclaration const*>, int> m_localVarUseCount;
/// Cache that holds information about whether a contract's constructor
/// uses inline assembly.
std::unique_ptr<ConstructorUsesAssembly> m_constructorUsesAssembly;
FunctionDefinition const* m_currentFunction = nullptr;
/// Flag that indicates a constructor.

View File

@ -262,7 +262,7 @@ bool SyntaxChecker::visit(PlaceholderStatement const&)
bool SyntaxChecker::visit(ContractDefinition const& _contract)
{
m_isInterface = _contract.contractKind() == ContractDefinition::ContractKind::Interface;
m_isInterface = _contract.isInterface();
ASTString const& contractName = _contract.name();
for (FunctionDefinition const* function: _contract.definedFunctions())

View File

@ -199,12 +199,44 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
return components;
}
TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall)
{
vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
if (arguments.size() != 1)
{
m_errorReporter.typeError(
_functionCall.location(),
"This function takes one argument, but " +
toString(arguments.size()) +
" were provided."
);
return {};
}
TypePointer firstArgType = type(*arguments.front());
if (
firstArgType->category() != Type::Category::TypeType ||
dynamic_cast<TypeType const&>(*firstArgType).actualType()->category() != TypeType::Category::Contract
)
{
m_errorReporter.typeError(
arguments.front()->location(),
"Invalid type for argument in function call. "
"Contract type required, but " +
type(*arguments.front())->toString(true) +
" provided."
);
return {};
}
return {MagicType::metaType(dynamic_cast<TypeType const&>(*firstArgType).actualType())};
}
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
{
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
solAssert(base, "Base contract not available.");
if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
if (m_scope->isInterface())
m_errorReporter.typeError(_inheritance.location(), "Interfaces cannot inherit.");
if (base->isLibrary())
@ -212,7 +244,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
auto const& arguments = _inheritance.arguments();
TypePointers parameterTypes;
if (base->contractKind() != ContractDefinition::ContractKind::Interface)
if (!base->isInterface())
// Interfaces do not have constructors, so there are zero parameters.
parameterTypes = ContractType(*base).newExpressionType()->parameterTypes();
@ -299,33 +331,50 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface())
m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable.");
}
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
if (type(*var)->category() == Type::Category::Mapping)
auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) {
if (type(var)->category() == Type::Category::Mapping)
{
if (!type(*var)->dataStoredIn(DataLocation::Storage))
m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"." );
if (!type(var)->dataStoredIn(DataLocation::Storage))
m_errorReporter.typeError(var.location(), "Mapping types can only have a data location of \"storage\"." );
else if (!isLibraryFunction && _function.isPublic())
m_errorReporter.typeError(var->location(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
m_errorReporter.typeError(var.location(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
}
else
{
if (!type(*var)->canLiveOutsideStorage() && _function.isPublic())
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.isPublic() && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
if (!type(var)->canLiveOutsideStorage() && _function.isPublic())
m_errorReporter.typeError(var.location(), "Type is required to live outside storage.");
if (_function.isPublic() && !(type(var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var.location(), "Internal or recursive type is not allowed for public or external functions.");
}
if (
_function.isPublic() &&
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
!typeSupportedByOldABIEncoder(*type(*var))
!typeSupportedByOldABIEncoder(*type(var))
)
m_errorReporter.typeError(
var->location(),
var.location(),
"This type is only supported in the new experimental ABI encoder. "
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
);
};
for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
{
TypePointer baseType = type(*var);
while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType.get()))
baseType = arrayType->baseType();
if (
!m_scope->isInterface() &&
baseType->category() == Type::Category::Struct &&
baseType->dataStoredIn(DataLocation::CallData)
)
m_errorReporter.typeError(var->location(), "Calldata structs are not yet supported.");
checkArgumentAndReturnParameter(*var);
var->accept(*this);
}
for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters())
{
checkArgumentAndReturnParameter(*var);
var->accept(*this);
}
set<Declaration const*> modifiers;
@ -346,7 +395,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else
modifiers.insert(decl);
}
if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
if (m_scope->isInterface())
{
if (_function.isImplemented())
m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot have an implementation.");
@ -375,7 +424,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
// * a function's input/output parameters,
// * or inside of a struct definition.
if (
m_scope->contractKind() == ContractDefinition::ContractKind::Interface
m_scope->isInterface()
&& !_variable.isCallableParameter()
&& !m_insideStruct
)
@ -1482,7 +1531,16 @@ void TypeChecker::typeCheckABIEncodeFunctions(
if (argType->category() == Type::Category::RationalNumber)
{
if (!argType->mobileType())
auto const& rationalType = dynamic_cast<RationalNumberType const&>(*argType);
if (rationalType.isFractional())
{
m_errorReporter.typeError(
arguments[i]->location(),
"Fractional numbers cannot yet be encoded."
);
continue;
}
else if (!argType->mobileType())
{
m_errorReporter.typeError(
arguments[i]->location(),
@ -1822,6 +1880,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
returnTypes = functionType->returnParameterTypes();
break;
}
case FunctionType::Kind::MetaType:
returnTypes = typeCheckMetaTypeFunctionAndRetrieveReturnType(_functionCall);
break;
default:
{
typeCheckFunctionCall(_functionCall, functionType);
@ -1862,7 +1923,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (!contract)
m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
if (contract->contractKind() == ContractDefinition::ContractKind::Interface)
if (contract->isInterface())
m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface.");
if (!contract->annotation().unimplementedFunctions.empty())
{
@ -2062,8 +2123,24 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
if (tt->actualType()->category() == Type::Category::Enum)
annotation.isPure = true;
if (auto magicType = dynamic_cast<MagicType const*>(exprType.get()))
{
if (magicType->kind() == MagicType::Kind::ABI)
annotation.isPure = true;
else if (magicType->kind() == MagicType::Kind::MetaType && (
memberName == "creationCode" || memberName == "runtimeCode"
))
{
annotation.isPure = true;
m_scope->annotation().contractDependencies.insert(
&dynamic_cast<ContractType const&>(*magicType->typeArgument()).contractDefinition()
);
if (contractDependenciesAreCyclic(*m_scope))
m_errorReporter.typeError(
_memberAccess.location(),
"Circular reference for contract code access."
);
}
}
return false;
}

View File

@ -81,6 +81,8 @@ private:
bool _abiEncoderV2
);
TypePointers typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall);
/// Performs type checks and determines result types for type conversion FunctionCall nodes.
TypePointer typeCheckTypeConversionAndRetrieveReturnType(
FunctionCall const& _functionCall

View File

@ -338,7 +338,9 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{MagicType::Kind::ABI, "encodeWithSignature"},
{MagicType::Kind::Block, "blockhash"},
{MagicType::Kind::Message, "data"},
{MagicType::Kind::Message, "sig"}
{MagicType::Kind::Message, "sig"},
{MagicType::Kind::MetaType, "creationCode"},
{MagicType::Kind::MetaType, "runtimeCode"}
};
set<MagicMember> static const payableMembers{
{MagicType::Kind::Message, "value"}

View File

@ -138,6 +138,11 @@ bool ContractDefinition::constructorIsPublic() const
return !f || f->isPublic();
}
bool ContractDefinition::canBeDeployed() const
{
return constructorIsPublic() && annotation().unimplementedFunctions.empty();
}
FunctionDefinition const* ContractDefinition::fallbackFunction() const
{
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)

View File

@ -394,6 +394,7 @@ public:
std::vector<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); }
std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
std::vector<EventDefinition const*> const& interfaceEvents() const;
bool isInterface() const { return m_contractKind == ContractKind::Interface; }
bool isLibrary() const { return m_contractKind == ContractKind::Library; }
/// @returns a map of canonical function signatures to FunctionDefinitions
@ -408,6 +409,10 @@ public:
FunctionDefinition const* constructor() const;
/// @returns true iff the constructor of this contract is public (or non-existing).
bool constructorIsPublic() const;
/// @returns true iff the contract can be deployed, i.e. is not abstract and has a
/// public constructor.
/// Should only be called after the type checker has run.
bool canBeDeployed() const;
/// Returns the fallback function or nullptr if no fallback function was specified.
FunctionDefinition const* fallbackFunction() const;

View File

@ -2525,7 +2525,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
strings parameterNames;
StateMutability stateMutability = StateMutability::NonPayable;
solAssert(_contract.contractKind() != ContractDefinition::ContractKind::Interface, "");
solAssert(!_contract.isInterface(), "");
if (constructor)
{
@ -2626,6 +2626,7 @@ string FunctionType::richIdentifier() const
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
case Kind::ABIDecode: id += "abidecode"; break;
case Kind::MetaType: id += "metatype"; break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
@ -3037,7 +3038,8 @@ bool FunctionType::isPure() const
m_kind == Kind::ABIEncodePacked ||
m_kind == Kind::ABIEncodeWithSelector ||
m_kind == Kind::ABIEncodeWithSignature ||
m_kind == Kind::ABIDecode;
m_kind == Kind::ABIDecode ||
m_kind == Kind::MetaType;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@ -3305,6 +3307,14 @@ string ModuleType::toString(bool) const
return string("module \"") + m_sourceUnit.annotation().path + string("\"");
}
shared_ptr<MagicType> MagicType::metaType(TypePointer _type)
{
solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now.");
auto t = make_shared<MagicType>(Kind::MetaType);
t->m_typeArgument = std::move(_type);
return t;
}
string MagicType::richIdentifier() const
{
switch (m_kind)
@ -3317,6 +3327,9 @@ string MagicType::richIdentifier() const
return "t_magic_transaction";
case Kind::ABI:
return "t_magic_abi";
case Kind::MetaType:
solAssert(m_typeArgument, "");
return "t_magic_meta_type_" + m_typeArgument->richIdentifier();
}
return "";
}
@ -3403,12 +3416,27 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
StateMutability::Pure
)}
});
default:
solAssert(false, "Unknown kind of magic.");
case Kind::MetaType:
{
solAssert(
m_typeArgument && m_typeArgument->category() == Type::Category::Contract,
"Only contracts supported for now"
);
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
if (contract.canBeDeployed())
return MemberList::MemberMap({
{"creationCode", std::make_shared<ArrayType>(DataLocation::Memory)},
{"runtimeCode", std::make_shared<ArrayType>(DataLocation::Memory)}
});
else
return {};
}
}
solAssert(false, "Unknown kind of magic.");
return {};
}
string MagicType::toString(bool) const
string MagicType::toString(bool _short) const
{
switch (m_kind)
{
@ -3420,7 +3448,17 @@ string MagicType::toString(bool) const
return "tx";
case Kind::ABI:
return "abi";
default:
solAssert(false, "Unknown kind of magic.");
case Kind::MetaType:
solAssert(m_typeArgument, "");
return "type(" + m_typeArgument->toString(_short) + ")";
}
solAssert(false, "Unknown kind of magic.");
return {};
}
TypePointer MagicType::typeArgument() const
{
solAssert(m_kind == Kind::MetaType, "");
solAssert(m_typeArgument, "");
return m_typeArgument;
}

View File

@ -989,6 +989,7 @@ public:
ABIEncodeWithSignature,
ABIDecode,
GasLeft, ///< gasleft()
MetaType ///< type(...)
};
Category category() const override { return Category::Function; }
@ -1299,16 +1300,23 @@ private:
};
/**
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference
* (it always references a global singleton by name).
* Special type for magic variables (block, msg, tx, type(...)), similar to a struct but without any reference.
*/
class MagicType: public Type
{
public:
enum class Kind { Block, Message, Transaction, ABI };
enum class Kind {
Block, ///< "block"
Message, ///< "msg"
Transaction, ///< "tx"
ABI, ///< "abi"
MetaType ///< "type(...)"
};
Category category() const override { return Category::Magic; }
explicit MagicType(Kind _kind): m_kind(_kind) {}
/// Factory function for meta type
static std::shared_ptr<MagicType> metaType(TypePointer _type);
TypeResult binaryOperatorResult(Token, TypePointer const&) const override
{
@ -1327,8 +1335,13 @@ public:
Kind kind() const { return m_kind; }
TypePointer typeArgument() const;
private:
Kind m_kind;
/// Contract type used for contract metadata magic.
TypePointer m_typeArgument;
};
/**

View File

@ -39,14 +39,19 @@ string ABIFunctions::tupleEncoder(
bool _encodeAsLibraryTypes
)
{
EncodingOptions options;
options.encodeAsLibraryTypes = _encodeAsLibraryTypes;
options.encodeFunctionFromStack = true;
options.padded = true;
options.dynamicInplace = false;
string functionName = string("abi_encode_tuple_");
for (auto const& t: _givenTypes)
functionName += t->identifier() + "_";
functionName += "_to_";
for (auto const& t: _targetTypes)
functionName += t->identifier() + "_";
if (_encodeAsLibraryTypes)
functionName += "_library";
functionName += options.toFunctionNameSuffix();
return createExternallyUsedFunction(functionName, [&]() {
solAssert(!_givenTypes.empty(), "");
@ -90,7 +95,7 @@ string ABIFunctions::tupleEncoder(
);
elementTempl("values", valueNames);
elementTempl("pos", to_string(headPos));
elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, true));
elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], options));
encodeElements += elementTempl.render();
headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize();
}
@ -184,6 +189,20 @@ pair<string, set<string>> ABIFunctions::requestedFunctions()
return make_pair(result, std::move(m_externallyUsedFunctions));
}
string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const
{
string suffix;
if (!padded)
suffix += "_nonPadded";
if (dynamicInplace)
suffix += "_inplace";
if (encodeFunctionFromStack)
suffix += "_fromStack";
if (encodeAsLibraryTypes)
suffix += "_library";
return suffix;
}
string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
{
string functionName = string("cleanup_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier();
@ -490,32 +509,31 @@ string ABIFunctions::splitExternalFunctionIdFunction()
string ABIFunctions::abiEncodingFunction(
Type const& _from,
Type const& _to,
bool _encodeAsLibraryTypes,
bool _fromStack
EncodingOptions const& _options
)
{
TypePointer toInterface = _to.fullEncodingType(_encodeAsLibraryTypes, true, false);
TypePointer toInterface = _to.fullEncodingType(_options.encodeAsLibraryTypes, true, false);
solUnimplementedAssert(toInterface, "Encoding type \"" + _to.toString() + "\" not yet implemented.");
Type const& to = *toInterface;
if (_from.category() == Type::Category::StringLiteral)
return abiEncodingFunctionStringLiteral(_from, to, _encodeAsLibraryTypes);
return abiEncodingFunctionStringLiteral(_from, to, _options);
else if (auto toArray = dynamic_cast<ArrayType const*>(&to))
{
solAssert(_from.category() == Type::Category::Array, "");
solAssert(to.dataStoredIn(DataLocation::Memory), "");
ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_from);
if (fromArray.location() == DataLocation::CallData)
return abiEncodingFunctionCalldataArray(fromArray, *toArray, _encodeAsLibraryTypes);
return abiEncodingFunctionCalldataArray(fromArray, *toArray, _options);
else if (!fromArray.isByteArray() && (
fromArray.location() == DataLocation::Memory ||
fromArray.baseType()->storageBytes() > 16
))
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _encodeAsLibraryTypes);
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
else if (fromArray.location() == DataLocation::Memory)
return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _encodeAsLibraryTypes);
return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _options);
else if (fromArray.location() == DataLocation::Storage)
return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _encodeAsLibraryTypes);
return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _options);
else
solAssert(false, "");
}
@ -523,14 +541,13 @@ string ABIFunctions::abiEncodingFunction(
{
StructType const* fromStruct = dynamic_cast<StructType const*>(&_from);
solAssert(fromStruct, "");
return abiEncodingFunctionStruct(*fromStruct, *toStruct, _encodeAsLibraryTypes);
return abiEncodingFunctionStruct(*fromStruct, *toStruct, _options);
}
else if (_from.category() == Type::Category::Function)
return abiEncodingFunctionFunctionType(
dynamic_cast<FunctionType const&>(_from),
to,
_encodeAsLibraryTypes,
_fromStack
_options
);
solAssert(_from.sizeOnStack() == 1, "");
@ -541,7 +558,7 @@ string ABIFunctions::abiEncodingFunction(
_from.identifier() +
"_to_" +
to.identifier() +
(_encodeAsLibraryTypes ? "_library" : "");
_options.toFunctionNameSuffix();
return createFunction(functionName, [&]() {
solAssert(!to.isDynamicallyEncoded(), "");
@ -556,7 +573,7 @@ string ABIFunctions::abiEncodingFunction(
{
// special case: convert storage reference type to value type - this is only
// possible for library calls where we just forward the storage reference
solAssert(_encodeAsLibraryTypes, "");
solAssert(_options.encodeAsLibraryTypes, "");
solAssert(to == IntegerType::uint256(), "");
templ("cleanupConvert", "value");
}
@ -574,7 +591,7 @@ string ABIFunctions::abiEncodingFunction(
string ABIFunctions::abiEncodingFunctionCalldataArray(
Type const& _from,
Type const& _to,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
)
{
solAssert(_to.isDynamicallySized(), "");
@ -596,7 +613,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray(
_from.identifier() +
"_to_" +
_to.identifier() +
(_encodeAsLibraryTypes ? "_library" : "");
_options.toFunctionNameSuffix();
return createFunction(functionName, [&]() {
solUnimplementedAssert(fromArrayType.isByteArray(), "Only byte arrays can be encoded from calldata currently.");
// TODO if this is not a byte array, we might just copy byte-by-byte anyway,
@ -622,7 +639,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray(
string ABIFunctions::abiEncodingFunctionSimpleArray(
ArrayType const& _from,
ArrayType const& _to,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
)
{
string functionName =
@ -630,7 +647,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
_from.identifier() +
"_to_" +
_to.identifier() +
(_encodeAsLibraryTypes ? "_library" : "");
_options.toFunctionNameSuffix();
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
solAssert(_from.length() == _to.length(), "");
@ -691,11 +708,13 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
templ("storeLength", "");
templ("dataAreaFun", arrayDataAreaFunction(_from));
templ("elementEncodedSize", toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize()));
EncodingOptions subOptions(_options);
subOptions.encodeFunctionFromStack = false;
templ("encodeToMemoryFun", abiEncodingFunction(
*_from.baseType(),
*_to.baseType(),
_encodeAsLibraryTypes,
false
subOptions
));
templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" );
templ("nextArrayElement", nextArrayElementFunction(_from));
@ -706,7 +725,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
string ABIFunctions::abiEncodingFunctionMemoryByteArray(
ArrayType const& _from,
ArrayType const& _to,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
)
{
string functionName =
@ -714,7 +733,7 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray(
_from.identifier() +
"_to_" +
_to.identifier() +
(_encodeAsLibraryTypes ? "_library" : "");
_options.toFunctionNameSuffix();
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
solAssert(_from.length() == _to.length(), "");
@ -742,7 +761,7 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray(
string ABIFunctions::abiEncodingFunctionCompactStorageArray(
ArrayType const& _from,
ArrayType const& _to,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
)
{
string functionName =
@ -750,7 +769,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
_from.identifier() +
"_to_" +
_to.identifier() +
(_encodeAsLibraryTypes ? "_library" : "");
_options.toFunctionNameSuffix();
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
solAssert(_from.length() == _to.length(), "");
@ -840,11 +859,13 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
templ("itemsPerSlot", to_string(itemsPerSlot));
string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize());
templ("elementEncodedSize", elementEncodedSize);
EncodingOptions subOptions(_options);
subOptions.encodeFunctionFromStack = false;
string encodeToMemoryFun = abiEncodingFunction(
*_from.baseType(),
*_to.baseType(),
_encodeAsLibraryTypes,
false
subOptions
);
templ("encodeToMemoryFun", encodeToMemoryFun);
std::vector<std::map<std::string, std::string>> items(itemsPerSlot);
@ -859,7 +880,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
string ABIFunctions::abiEncodingFunctionStruct(
StructType const& _from,
StructType const& _to,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
)
{
string functionName =
@ -867,7 +888,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
_from.identifier() +
"_to_" +
_to.identifier() +
(_encodeAsLibraryTypes ? "_library" : "");
_options.toFunctionNameSuffix();
solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "Encoding struct from calldata is not yet supported.");
solAssert(&_from.structDefinition() == &_to.structDefinition(), "");
@ -904,7 +925,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
solAssert(member.type, "");
if (!member.type->canLiveOutsideStorage())
continue;
TypePointer memberTypeTo = member.type->fullEncodingType(_encodeAsLibraryTypes, true, false);
TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false);
solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented.");
auto memberTypeFrom = _from.memberType(member.name);
solAssert(memberTypeFrom, "");
@ -958,7 +979,10 @@ string ABIFunctions::abiEncodingFunctionStruct(
}
memberTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset));
encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize();
memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, _encodeAsLibraryTypes, false));
EncodingOptions subOptions(_options);
subOptions.encodeFunctionFromStack = false;
memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions));
members.push_back({});
members.back()["encode"] = memberTempl.render();
@ -973,7 +997,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
string ABIFunctions::abiEncodingFunctionStringLiteral(
Type const& _from,
Type const& _to,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
)
{
solAssert(_from.category() == Type::Category::StringLiteral, "");
@ -983,7 +1007,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral(
_from.identifier() +
"_to_" +
_to.identifier() +
(_encodeAsLibraryTypes ? "_library" : "");
_options.toFunctionNameSuffix();
return createFunction(functionName, [&]() {
auto const& strType = dynamic_cast<StringLiteralType const&>(_from);
string const& value = strType.value();
@ -1034,8 +1058,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral(
string ABIFunctions::abiEncodingFunctionFunctionType(
FunctionType const& _from,
Type const& _to,
bool _encodeAsLibraryTypes,
bool _fromStack
EncodingOptions const& _options
)
{
solAssert(_from.kind() == FunctionType::Kind::External, "");
@ -1046,10 +1069,9 @@ string ABIFunctions::abiEncodingFunctionFunctionType(
_from.identifier() +
"_to_" +
_to.identifier() +
(_fromStack ? "_fromStack" : "") +
(_encodeAsLibraryTypes ? "_library" : "");
_options.toFunctionNameSuffix();
if (_fromStack)
if (_options.encodeFunctionFromStack)
return createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(addr, function_id, pos) {
@ -1716,3 +1738,4 @@ size_t ABIFunctions::headSize(TypePointers const& _targetTypes)
return headSize;
}

View File

@ -87,6 +87,23 @@ public:
std::pair<std::string, std::set<std::string>> requestedFunctions();
private:
struct EncodingOptions
{
/// Pad/signextend value types and bytes/string to multiples of 32 bytes.
bool padded = true;
/// Store arrays and structs in place without "data pointer" and do not store the length.
bool dynamicInplace = false;
/// Only for external function types: The value is a pair of address / function id instead
/// of a memory pointer to the compression representation.
bool encodeFunctionFromStack = false;
/// Encode storage pointers as storage pointers (we are targeting a library call).
bool encodeAsLibraryTypes = false;
/// @returns a string to uniquely identify the encoding options for the encoding
/// function name. Skips everything that has its default value.
std::string toFunctionNameSuffix() const;
};
/// @returns the name of the cleanup function for the given type and
/// adds its implementation to the requested functions.
/// @param _revertOnFailure if true, causes revert on invalid data,
@ -115,40 +132,39 @@ private:
std::string abiEncodingFunction(
Type const& _givenType,
Type const& _targetType,
bool _encodeAsLibraryTypes,
bool _fromStack
EncodingOptions const& _options
);
/// Part of @a abiEncodingFunction for array target type and given calldata array.
std::string abiEncodingFunctionCalldataArray(
Type const& _givenType,
Type const& _targetType,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
);
/// Part of @a abiEncodingFunction for array target type and given memory array or
/// a given storage array with one item per slot.
std::string abiEncodingFunctionSimpleArray(
ArrayType const& _givenType,
ArrayType const& _targetType,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
);
std::string abiEncodingFunctionMemoryByteArray(
ArrayType const& _givenType,
ArrayType const& _targetType,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
);
/// Part of @a abiEncodingFunction for array target type and given storage array
/// where multiple items are packed into the same storage slot.
std::string abiEncodingFunctionCompactStorageArray(
ArrayType const& _givenType,
ArrayType const& _targetType,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
);
/// Part of @a abiEncodingFunction for struct types.
std::string abiEncodingFunctionStruct(
StructType const& _givenType,
StructType const& _targetType,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
);
// @returns the name of the ABI encoding function with the given type
@ -157,14 +173,13 @@ private:
std::string abiEncodingFunctionStringLiteral(
Type const& _givenType,
Type const& _targetType,
bool _encodeAsLibraryTypes
EncodingOptions const& _options
);
std::string abiEncodingFunctionFunctionType(
FunctionType const& _from,
Type const& _to,
bool _encodeAsLibraryTypes,
bool _fromStack
EncodingOptions const& _options
);
/// @returns the name of the ABI decoding function for the given type

View File

@ -31,22 +31,28 @@ using namespace dev::solidity;
void Compiler::compileContract(
ContractDefinition const& _contract,
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts,
std::map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers,
bytes const& _metadata
)
{
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns);
runtimeCompiler.compileContract(_contract, _contracts);
runtimeCompiler.compileContract(_contract, _otherCompilers);
m_runtimeContext.appendAuxiliaryData(_metadata);
// This might modify m_runtimeContext because it can access runtime functions at
// creation time.
ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1);
m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
m_context.optimise(m_optimize, m_optimizeRuns);
}
std::shared_ptr<eth::Assembly> Compiler::runtimeAssemblyPtr() const
{
solAssert(m_context.runtimeContext(), "");
return m_context.runtimeContext()->assemblyPtr();
}
eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const
{
return m_runtimeContext.functionEntryLabelIfExists(_function);

View File

@ -45,11 +45,15 @@ public:
/// @arg _metadata contains the to be injected metadata CBOR
void compileContract(
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts,
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers,
bytes const& _metadata
);
/// @returns Entire assembly.
eth::Assembly const& assembly() const { return m_context.assembly(); }
/// @returns Entire assembly as a shared pointer to non-const.
std::shared_ptr<eth::Assembly> assemblyPtr() const { return m_context.assemblyPtr(); }
/// @returns Runtime assembly.
std::shared_ptr<eth::Assembly> runtimeAssemblyPtr() const;
/// @returns The entire assembled object (with constructor).
eth::LinkerObject assembledObject() const { return m_context.assembledObject(); }
/// @returns Only the runtime object (without constructor).

View File

@ -167,11 +167,18 @@ unsigned CompilerContext::numberOfLocalVariables() const
return m_localVariables.size();
}
eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const
shared_ptr<eth::Assembly> CompilerContext::compiledContract(ContractDefinition const& _contract) const
{
auto ret = m_compiledContracts.find(&_contract);
solAssert(ret != m_compiledContracts.end(), "Compiled contract not found.");
return *ret->second;
auto ret = m_otherCompilers.find(&_contract);
solAssert(ret != m_otherCompilers.end(), "Compiled contract not found.");
return ret->second->assemblyPtr();
}
shared_ptr<eth::Assembly> CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const
{
auto ret = m_otherCompilers.find(&_contract);
solAssert(ret != m_otherCompilers.end(), "Compiled contract not found.");
return ret->second->runtimeAssemblyPtr();
}
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const

View File

@ -41,6 +41,7 @@
namespace dev {
namespace solidity {
class Compiler;
/**
* Context to be shared by all units that compile the same contract.
@ -74,8 +75,9 @@ public:
/// Returns the number of currently allocated local variables.
unsigned numberOfLocalVariables() const;
void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
void setOtherCompilers(std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers) { m_otherCompilers = _otherCompilers; }
std::shared_ptr<eth::Assembly> compiledContract(ContractDefinition const& _contract) const;
std::shared_ptr<eth::Assembly> compiledContractRuntime(ContractDefinition const& _contract) const;
void setStackOffset(int _offset) { m_asm->setDeposit(_offset); }
void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); }
@ -222,15 +224,15 @@ public:
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); }
/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
CompilerContext* runtimeContext() { return m_runtimeContext; }
CompilerContext* runtimeContext() const { return m_runtimeContext; }
/// @returns the identifier of the runtime subroutine.
size_t runtimeSub() const { return m_runtimeSub; }
/// @returns a const reference to the underlying assembly.
eth::Assembly const& assembly() const { return *m_asm; }
/// @returns non-const reference to the underlying assembly. Should be avoided in favour of
/// wrappers in this class.
eth::Assembly& nonConstAssembly() { return *m_asm; }
/// @returns a shared pointer to the assembly.
/// Should be avoided except when adding sub-assemblies.
std::shared_ptr<eth::Assembly> assemblyPtr() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings
std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const
@ -307,7 +309,7 @@ private:
/// Activated experimental features.
std::set<ExperimentalFeature> m_experimentalFeatures;
/// Other already compiled contracts to be used in contract creation calls.
std::map<ContractDefinition const*, eth::Assembly const*> m_compiledContracts;
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> m_otherCompilers;
/// Storage offsets of state variables
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
/// Offsets of local variables on the stack (relative to stack base).

View File

@ -1199,6 +1199,29 @@ void CompilerUtils::computeHashStatic()
m_context << u256(32) << u256(0) << Instruction::KECCAK256;
}
void CompilerUtils::copyContractCodeToMemory(ContractDefinition const& contract, bool _creation)
{
string which = _creation ? "Creation" : "Runtime";
m_context.callLowLevelFunction(
"$copyContract" + which + "CodeToMemory_" + contract.type()->identifier(),
1,
1,
[&contract, _creation](CompilerContext& _context)
{
// copy the contract's code into memory
shared_ptr<eth::Assembly> assembly =
_creation ?
_context.compiledContract(contract) :
_context.compiledContractRuntime(contract);
// pushes size
auto subroutine = _context.addSubroutine(assembly);
_context << Instruction::DUP1 << subroutine;
_context << Instruction::DUP4 << Instruction::CODECOPY;
_context << Instruction::ADD;
}
);
}
void CompilerUtils::storeStringData(bytesConstRef _data)
{
//@todo provide both alternatives to the optimiser

View File

@ -263,6 +263,13 @@ public:
/// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type.
void computeHashStatic();
/// Apppends code that copies the code of the given contract to memory.
/// Stack pre: Memory position
/// Stack post: Updated memory position
/// @param creation if true, copies creation code, if false copies runtime code.
/// @note the contract has to be compiled already, so beware of cyclic dependencies!
void copyContractCodeToMemory(ContractDefinition const& contract, bool _creationCode);
/// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier.
static const unsigned dataStartOffset;

View File

@ -60,7 +60,7 @@ private:
void ContractCompiler::compileContract(
ContractDefinition const& _contract,
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers
)
{
CompilerContext::LocationSetter locationSetter(m_context, _contract);
@ -70,7 +70,7 @@ void ContractCompiler::compileContract(
// This has to be the first code in the contract.
appendDelegatecallCheck();
initializeContext(_contract, _contracts);
initializeContext(_contract, _otherCompilers);
// This generates the dispatch function for externally visible functions
// and adds the function to the compilation queue. Additionally internal functions,
// which are referenced directly or indirectly will be added.
@ -81,7 +81,7 @@ void ContractCompiler::compileContract(
size_t ContractCompiler::compileConstructor(
ContractDefinition const& _contract,
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
std::map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers
)
{
CompilerContext::LocationSetter locationSetter(m_context, _contract);
@ -89,18 +89,18 @@ size_t ContractCompiler::compileConstructor(
return deployLibrary(_contract);
else
{
initializeContext(_contract, _contracts);
initializeContext(_contract, _otherCompilers);
return packIntoContractCreator(_contract);
}
}
void ContractCompiler::initializeContext(
ContractDefinition const& _contract,
map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers
)
{
m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures);
m_context.setCompiledContracts(_compiledContracts);
m_context.setOtherCompilers(_otherCompilers);
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
CompilerUtils(m_context).initialiseFreeMemoryPointer();
registerStateVariables(_contract);
@ -716,7 +716,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
CodeGenerator::assemble(
_inlineAssembly.operations(),
*_inlineAssembly.annotation().analysisInfo,
m_context.nonConstAssembly(),
*m_context.assemblyPtr(),
identifierAccess
);
m_context.setStackOffset(startStackHeight);

View File

@ -49,13 +49,13 @@ public:
void compileContract(
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers
);
/// Compiles the constructor part of the contract.
/// @returns the identifier of the runtime sub-assembly.
size_t compileConstructor(
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers
);
private:
@ -63,7 +63,7 @@ private:
/// information about the contract like the AST annotations.
void initializeContext(
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers
);
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context. Adds the constructor code.

View File

@ -594,22 +594,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
ContractDefinition const* contract =
&dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
m_context.callLowLevelFunction(
"$copyContractCreationCodeToMemory_" + contract->type()->identifier(),
0,
1,
[contract](CompilerContext& _context)
{
// copy the contract's code into memory
eth::Assembly const& assembly = _context.compiledContract(*contract);
CompilerUtils(_context).fetchFreeMemoryPointer();
// pushes size
auto subroutine = _context.addSubroutine(make_shared<eth::Assembly>(assembly));
_context << Instruction::DUP1 << subroutine;
_context << Instruction::DUP4 << Instruction::CODECOPY;
_context << Instruction::ADD;
}
);
utils().fetchFreeMemoryPointer();
utils().copyContractCodeToMemory(*contract, true);
utils().abiEncode(argumentTypes, function.parameterTypes());
// now on stack: memory_end_ptr
// need: size, offset, endowment
@ -1107,6 +1093,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::GasLeft:
m_context << Instruction::GAS;
break;
case FunctionType::Kind::MetaType:
// No code to generate.
break;
}
}
return false;
@ -1348,6 +1337,23 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
solAssert(false, "Gas has been removed.");
else if (member == "blockhash")
solAssert(false, "Blockhash has been removed.");
else if (member == "creationCode" || member == "runtimeCode")
{
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
utils().fetchFreeMemoryPointer();
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
utils().copyContractCodeToMemory(contract, member == "creationCode");
// Stack: start end
m_context.appendInlineAssembly(
Whiskers(R"({
mstore(start, sub(end, add(start, 0x20)))
mstore(<free>, end)
})")("free", to_string(CompilerUtils::freeMemoryPointer)).render(),
{"start", "end"}
);
m_context << Instruction::POP;
}
else
solAssert(false, "Unknown magic member.");
break;

View File

@ -386,7 +386,7 @@ void SMTChecker::endVisit(BinaryOperation const& _op)
void SMTChecker::endVisit(FunctionCall const& _funCall)
{
solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, "");
if (_funCall.annotation().kind != FunctionCallKind::FunctionCall)
if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall)
{
m_errorReporter.warning(
_funCall.location(),
@ -395,6 +395,12 @@ void SMTChecker::endVisit(FunctionCall const& _funCall)
return;
}
if (_funCall.annotation().kind == FunctionCallKind::TypeConversion)
{
visitTypeConversion(_funCall);
return;
}
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
std::vector<ASTPointer<Expression const>> const args = _funCall.arguments();
@ -412,6 +418,10 @@ void SMTChecker::endVisit(FunctionCall const& _funCall)
case FunctionType::Kind::Internal:
inlineFunctionCall(_funCall);
break;
case FunctionType::Kind::External:
resetStateVariables();
resetStorageReferences();
break;
case FunctionType::Kind::KECCAK256:
case FunctionType::Kind::ECRecover:
case FunctionType::Kind::SHA256:
@ -571,6 +581,43 @@ void SMTChecker::endVisit(Identifier const& _identifier)
}
}
void SMTChecker::visitTypeConversion(FunctionCall const& _funCall)
{
solAssert(_funCall.annotation().kind == FunctionCallKind::TypeConversion, "");
solAssert(_funCall.arguments().size() == 1, "");
auto argument = _funCall.arguments().at(0);
unsigned argSize = argument->annotation().type->storageBytes();
unsigned castSize = _funCall.annotation().type->storageBytes();
if (argSize == castSize)
defineExpr(_funCall, expr(*argument));
else
{
createExpr(_funCall);
setUnknownValue(*m_expressions.at(&_funCall));
auto const& funCallCategory = _funCall.annotation().type->category();
// TODO: truncating and bytesX needs a different approach because of right padding.
if (funCallCategory == Type::Category::Integer || funCallCategory == Type::Category::Address)
{
if (argSize < castSize)
defineExpr(_funCall, expr(*argument));
else
{
auto const& intType = dynamic_cast<IntegerType const&>(*m_expressions.at(&_funCall)->type());
defineExpr(_funCall, smt::Expression::ite(
expr(*argument) >= minValue(intType) && expr(*argument) <= maxValue(intType),
expr(*argument),
expr(_funCall)
));
}
}
m_errorReporter.warning(
_funCall.location(),
"Type conversion is not yet fully supported and might yield false positives."
);
}
}
void SMTChecker::visitFunctionIdentifier(Identifier const& _identifier)
{
auto const& fType = dynamic_cast<FunctionType const&>(*_identifier.annotation().type);
@ -1151,25 +1198,35 @@ void SMTChecker::removeLocalVariables()
}
}
void SMTChecker::resetVariable(VariableDeclaration const& _variable)
{
newValue(_variable);
setUnknownValue(_variable);
}
void SMTChecker::resetStateVariables()
{
for (auto const& variable: m_variables)
{
if (variable.first->isStateVariable())
{
newValue(*variable.first);
setUnknownValue(*variable.first);
}
}
resetVariables([&](VariableDeclaration const& _variable) { return _variable.isStateVariable(); });
}
void SMTChecker::resetStorageReferences()
{
resetVariables([&](VariableDeclaration const& _variable) { return _variable.hasReferenceOrMappingType(); });
}
void SMTChecker::resetVariables(vector<VariableDeclaration const*> _variables)
{
for (auto const* decl: _variables)
resetVariable(*decl);
}
void SMTChecker::resetVariables(function<bool(VariableDeclaration const&)> const& _filter)
{
for_each(begin(m_variables), end(m_variables), [&](auto _variable)
{
newValue(*decl);
setUnknownValue(*decl);
}
if (_filter(*_variable.first))
this->resetVariable(*_variable.first);
});
}
void SMTChecker::mergeVariables(vector<VariableDeclaration const*> const& _variables, smt::Expression const& _condition, VariableIndices const& _indicesEndTrue, VariableIndices const& _indicesEndFalse)

View File

@ -86,6 +86,7 @@ private:
void visitAssert(FunctionCall const& _funCall);
void visitRequire(FunctionCall const& _funCall);
void visitGasLeft(FunctionCall const& _funCall);
void visitTypeConversion(FunctionCall const& _funCall);
/// Visits the FunctionDefinition of the called function
/// if available and inlines the return value.
void inlineFunctionCall(FunctionCall const& _funCall);
@ -146,8 +147,11 @@ private:
void initializeLocalVariables(FunctionDefinition const& _function);
void initializeFunctionCallParameters(FunctionDefinition const& _function, std::vector<smt::Expression> const& _callArgs);
void resetVariable(VariableDeclaration const& _variable);
void resetStateVariables();
void resetStorageReferences();
void resetVariables(std::vector<VariableDeclaration const*> _variables);
void resetVariables(std::function<bool(VariableDeclaration const&)> const& _filter);
/// Given two different branches and the touched variables,
/// merge the touched variables into after-branch ite variables
/// using the branch condition as guard.

View File

@ -134,7 +134,7 @@ void AssemblyStack::optimize(yul::Object& _object)
for (auto& subNode: _object.subObjects)
if (auto subObject = dynamic_cast<yul::Object*>(subNode.get()))
optimize(*subObject);
yul::OptimiserSuite::run(*_object.code, *_object.analysisInfo);
yul::OptimiserSuite::run(*languageToDialect(m_language), *_object.code, *_object.analysisInfo);
}
MachineAssemblyObject AssemblyStack::assemble(Machine _machine, bool _optimize) const

View File

@ -343,12 +343,12 @@ bool CompilerStack::compile()
return false;
// Only compile contracts individually which have been requested.
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
if (isRequestedContract(*contract))
compileContract(*contract, compiledContracts);
compileContract(*contract, otherCompilers);
m_stackState = CompilationSuccessful;
this->link();
return true;
@ -795,19 +795,15 @@ bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& featu
void CompilerStack::compileContract(
ContractDefinition const& _contract,
map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
map<ContractDefinition const*, shared_ptr<Compiler const>>& _otherCompilers
)
{
solAssert(m_stackState >= AnalysisSuccessful, "");
if (
_compiledContracts.count(&_contract) ||
!_contract.annotation().unimplementedFunctions.empty() ||
!_contract.constructorIsPublic()
)
if (_otherCompilers.count(&_contract) || !_contract.canBeDeployed())
return;
for (auto const* dependency: _contract.annotation().contractDependencies)
compileContract(*dependency, _compiledContracts);
compileContract(*dependency, _otherCompilers);
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
@ -825,7 +821,7 @@ void CompilerStack::compileContract(
try
{
// Run optimiser and compile the contract.
compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata);
}
catch(eth::OptimizerException const&)
{
@ -852,7 +848,7 @@ void CompilerStack::compileContract(
solAssert(false, "Assembly exception for deployed bytecode");
}
_compiledContracts[compiledContract.contract] = &compiler->assembly();
_otherCompilers[compiledContract.contract] = compiler;
}
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const

View File

@ -293,10 +293,12 @@ private:
/// @returns true if the contract is requested to be compiled.
bool isRequestedContract(ContractDefinition const& _contract) const;
/// Compile a single contract and put the result in @a _compiledContracts.
/// Compile a single contract.
/// @param _otherCompilers provides access to compilers of other contracts, to get
/// their bytecode if needed. Only filled after they have been compiled.
void compileContract(
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>>& _otherCompilers
);
/// Links all the known library addresses in the available objects. Any unknown

View File

@ -1551,6 +1551,12 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
break;
case Token::Type:
// Inside expressions "type" is the name of a special, globally-available function.
nodeFactory.markEndPosition();
m_scanner->next();
expression = nodeFactory.createNode<Identifier>(make_shared<ASTString>("type"));
break;
case Token::LParen:
case Token::LBrack:
{

View File

@ -24,6 +24,7 @@
#include <libyul/AsmScopeFiller.h>
#include <libyul/AsmScope.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/Utilities.h>
#include <liblangutil/ErrorReporter.h>
@ -299,11 +300,14 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
bool success = true;
size_t parameters = 0;
size_t returns = 0;
bool needsLiteralArguments = false;
if (BuiltinFunction const* f = m_dialect->builtin(_funCall.functionName.name))
{
// TODO: compare types, too
parameters = f->parameters.size();
returns = f->returns.size();
if (f->literalArguments)
needsLiteralArguments = true;
}
else if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor(
[&](Scope::Variable const&)
@ -347,8 +351,15 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
}
for (auto const& arg: _funCall.arguments | boost::adaptors::reversed)
{
if (!expectExpression(arg))
success = false;
else if (needsLiteralArguments && arg.type() != typeid(Literal))
m_errorReporter.typeError(
_funCall.functionName.location,
"Function expects direct literals as arguments."
);
}
// Use argument size instead of parameter count to avoid misleading errors.
m_stackHeight += int(returns) - int(_funCall.arguments.size());
m_info.stackHeightInfo[&_funCall] = m_stackHeight;
@ -380,7 +391,29 @@ bool AsmAnalyzer::operator()(Switch const& _switch)
if (!expectExpression(*_switch.expression))
success = false;
set<tuple<LiteralKind, YulString>> cases;
if (m_dialect->flavour == AsmFlavour::Yul)
{
YulString caseType;
bool mismatchingTypes = false;
for (auto const& _case: _switch.cases)
if (_case.value)
{
if (caseType.empty())
caseType = _case.value->type;
else if (caseType != _case.value->type)
{
mismatchingTypes = true;
break;
}
}
if (mismatchingTypes)
m_errorReporter.typeError(
_switch.location,
"Switch cases have non-matching types."
);
}
set<Literal const*, Less<Literal*>> cases;
for (auto const& _case: _switch.cases)
{
if (_case.value)
@ -394,12 +427,11 @@ bool AsmAnalyzer::operator()(Switch const& _switch)
m_stackHeight--;
/// Note: the parser ensures there is only one default case
auto val = make_tuple(_case.value->kind, _case.value->value);
if (!cases.insert(val).second)
if (!cases.insert(_case.value.get()).second)
{
m_errorReporter.declarationError(
_case.location,
"Duplicate case defined"
"Duplicate case defined."
);
success = false;
}

View File

@ -59,25 +59,25 @@ struct StackAssignment { langutil::SourceLocation location; Identifier variableN
/// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy
/// a single stack slot and expects a single expression on the right hand returning
/// the same amount of items as the number of variables.
struct Assignment { langutil::SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Expression> value; };
struct Assignment { langutil::SourceLocation location; std::vector<Identifier> variableNames; std::unique_ptr<Expression> value; };
/// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))"
struct FunctionalInstruction { langutil::SourceLocation location; dev::solidity::Instruction instruction; std::vector<Expression> arguments; };
struct FunctionCall { langutil::SourceLocation location; Identifier functionName; std::vector<Expression> arguments; };
/// Statement that contains only a single expression
struct ExpressionStatement { langutil::SourceLocation location; Expression expression; };
/// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted
struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::shared_ptr<Expression> value; };
struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::unique_ptr<Expression> value; };
/// Block that creates a scope (frees declared stack variables)
struct Block { langutil::SourceLocation location; std::vector<Statement> statements; };
/// Function definition ("function f(a, b) -> (d, e) { ... }")
struct FunctionDefinition { langutil::SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; };
/// Conditional execution without "else" part.
struct If { langutil::SourceLocation location; std::shared_ptr<Expression> condition; Block body; };
struct If { langutil::SourceLocation location; std::unique_ptr<Expression> condition; Block body; };
/// Switch case or default case
struct Case { langutil::SourceLocation location; std::shared_ptr<Literal> value; Block body; };
struct Case { langutil::SourceLocation location; std::unique_ptr<Literal> value; Block body; };
/// Switch statement
struct Switch { langutil::SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; };
struct ForLoop { langutil::SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; };
struct Switch { langutil::SourceLocation location; std::unique_ptr<Expression> expression; std::vector<Case> cases; };
struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<Expression> condition; Block post; Block body; };
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
{

View File

@ -81,15 +81,15 @@ Statement Parser::parseStatement()
{
If _if = createWithLocation<If>();
m_scanner->next();
_if.condition = make_shared<Expression>(parseExpression());
_if.condition = make_unique<Expression>(parseExpression());
_if.body = parseBlock();
return _if;
return Statement{move(_if)};
}
case Token::Switch:
{
Switch _switch = createWithLocation<Switch>();
m_scanner->next();
_switch.expression = make_shared<Expression>(parseExpression());
_switch.expression = make_unique<Expression>(parseExpression());
while (m_scanner->currentToken() == Token::Case)
_switch.cases.emplace_back(parseCase());
if (m_scanner->currentToken() == Token::Default)
@ -101,7 +101,7 @@ Statement Parser::parseStatement()
if (_switch.cases.empty())
fatalParserError("Switch statement without any cases.");
_switch.location.end = _switch.cases.back().body.location.end;
return _switch;
return Statement{move(_switch)};
}
case Token::For:
return parseForLoop();
@ -120,7 +120,7 @@ Statement Parser::parseStatement()
fatalParserError("Identifier expected, got instruction name.");
assignment.location.end = endPosition();
expectToken(Token::Identifier);
return assignment;
return Statement{move(assignment)};
}
default:
break;
@ -163,7 +163,7 @@ Statement Parser::parseStatement()
assignment.value.reset(new Expression(parseExpression()));
assignment.location.end = locationOf(*assignment.value).end;
return assignment;
return Statement{std::move(assignment)};
}
case Token::Colon:
{
@ -184,7 +184,7 @@ Statement Parser::parseStatement()
assignment.variableNames.emplace_back(identifier);
assignment.value.reset(new Expression(parseExpression()));
assignment.location.end = locationOf(*assignment.value).end;
return assignment;
return Statement{std::move(assignment)};
}
else
{
@ -230,7 +230,7 @@ Case Parser::parseCase()
ElementaryOperation literal = parseElementaryOperation();
if (literal.type() != typeid(Literal))
fatalParserError("Literal expected.");
_case.value = make_shared<Literal>(boost::get<Literal>(std::move(literal)));
_case.value = make_unique<Literal>(boost::get<Literal>(std::move(literal)));
}
else
fatalParserError("Case or default case expected.");
@ -245,7 +245,7 @@ ForLoop Parser::parseForLoop()
ForLoop forLoop = createWithLocation<ForLoop>();
expectToken(Token::For);
forLoop.pre = parseBlock();
forLoop.condition = make_shared<Expression>(parseExpression());
forLoop.condition = make_unique<Expression>(parseExpression());
forLoop.post = parseBlock();
forLoop.body = parseBlock();
forLoop.location.end = forLoop.body.location.end;
@ -443,7 +443,7 @@ VariableDeclaration Parser::parseVariableDeclaration()
{
expectToken(Token::Colon);
expectToken(Token::Assign);
varDecl.value.reset(new Expression(parseExpression()));
varDecl.value = make_unique<Expression>(parseExpression());
varDecl.location.end = locationOf(*varDecl.value).end;
}
else

View File

@ -20,6 +20,8 @@ add_library(yul
Object.h
ObjectParser.cpp
ObjectParser.h
Utilities.cpp
Utilities.h
YulString.h
backends/evm/AbstractAssembly.h
backends/evm/EVMAssembly.cpp
@ -42,6 +44,10 @@ add_library(yul
optimiser/DataFlowAnalyzer.h
optimiser/Disambiguator.cpp
optimiser/Disambiguator.h
optimiser/EquivalentFunctionDetector.cpp
optimiser/EquivalentFunctionDetector.h
optimiser/EquivalentFunctionCombiner.cpp
optimiser/EquivalentFunctionCombiner.h
optimiser/ExpressionInliner.cpp
optimiser/ExpressionInliner.h
optimiser/ExpressionJoiner.cpp
@ -68,10 +74,14 @@ add_library(yul
optimiser/NameCollector.h
optimiser/NameDispenser.cpp
optimiser/NameDispenser.h
optimiser/OptimizerUtilities.cpp
optimiser/OptimizerUtilities.h
optimiser/RedundantAssignEliminator.cpp
optimiser/RedundantAssignEliminator.h
optimiser/Rematerialiser.cpp
optimiser/Rematerialiser.h
optimiser/SSAReverser.cpp
optimiser/SSAReverser.h
optimiser/SSATransform.cpp
optimiser/SSATransform.h
optimiser/SSAValueTracker.cpp
@ -90,8 +100,6 @@ add_library(yul
optimiser/SyntacticalEquality.h
optimiser/UnusedPruner.cpp
optimiser/UnusedPruner.h
optimiser/Utilities.cpp
optimiser/Utilities.h
optimiser/VarDeclInitializer.cpp
optimiser/VarDeclInitializer.h
)

Some files were not shown because too many files have changed in this diff Show More