mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5836 from ethereum/develop
Merge develop into release for 0.5.3.
This commit is contained in:
commit
10d17f2458
@ -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:
|
||||
|
14
.travis.yml
14
.travis.yml
@ -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:
|
||||
|
@ -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)
|
||||
|
25
Changelog.md
25
Changelog.md
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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 ()
|
||||
|
@ -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)
|
||||
|
8
cmake/EthToolchains.cmake
Normal file
8
cmake/EthToolchains.cmake
Normal 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()
|
@ -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.
|
||||
|
4
cmake/toolchains/default.cmake
Normal file
4
cmake/toolchains/default.cmake
Normal file
@ -0,0 +1,4 @@
|
||||
# Require C++14.
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
2
cmake/toolchains/emscripten.cmake
Normal file
2
cmake/toolchains/emscripten.cmake
Normal file
@ -0,0 +1,2 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/default.cmake")
|
||||
include("$ENV{EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake")
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -620,5 +620,9 @@
|
||||
"0.5.2": {
|
||||
"bugs": [],
|
||||
"released": "2018-12-19"
|
||||
},
|
||||
"0.5.3": {
|
||||
"bugs": [],
|
||||
"released": "2019-01-22"
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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.
|
||||
|
1765
docs/contracts.rst
1765
docs/contracts.rst
File diff suppressed because it is too large
Load Diff
43
docs/contracts/abstract-contracts.rst
Normal file
43
docs/contracts/abstract-contracts.rst
Normal 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".
|
35
docs/contracts/constant-state-variables.rst
Normal file
35
docs/contracts/constant-state-variables.rst
Normal 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");
|
||||
}
|
117
docs/contracts/creating-contracts.rst
Normal file
117
docs/contracts/creating-contracts.rst
Normal 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
161
docs/contracts/events.rst
Normal 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>`_
|
111
docs/contracts/function-modifiers.rst
Normal file
111
docs/contracts/function-modifiers.rst
Normal 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).
|
398
docs/contracts/functions.rst
Normal file
398
docs/contracts/functions.rst
Normal 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``.
|
299
docs/contracts/inheritance.rst
Normal file
299
docs/contracts/inheritance.rst
Normal 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.
|
36
docs/contracts/interfaces.rst
Normal file
36
docs/contracts/interfaces.rst
Normal 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``.
|
230
docs/contracts/libraries.rst
Normal file
230
docs/contracts/libraries.rst
Normal 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.
|
119
docs/contracts/using-for.rst
Normal file
119
docs/contracts/using-for.rst
Normal 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.
|
198
docs/contracts/visibility-and-getters.rst
Normal file
198
docs/contracts/visibility-and-getters.rst
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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 {
|
||||
|
339
docs/examples/blind-auction.rst
Normal file
339
docs/examples/blind-auction.rst
Normal 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);
|
||||
}
|
||||
}
|
429
docs/examples/micropayment.rst
Normal file
429
docs/examples/micropayment.rst
Normal 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();
|
||||
}
|
107
docs/examples/safe-remote.rst
Normal file
107
docs/examples/safe-remote.rst
Normal 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
191
docs/examples/voting.rst
Normal 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?
|
@ -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, there’s 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?
|
||||
===============
|
||||
|
||||
|
@ -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
|
||||
----------------------
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
1354
docs/types.rst
1354
docs/types.rst
File diff suppressed because it is too large
Load Diff
127
docs/types/conversion.rst
Normal file
127
docs/types/conversion.rst
Normal 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``.
|
58
docs/types/mapping-types.rst
Normal file
58
docs/types/mapping-types.rst
Normal 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
47
docs/types/operators.rst
Normal 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);
|
||||
}
|
||||
}
|
409
docs/types/reference-types.rst
Normal file
409
docs/types/reference-types.rst
Normal 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
716
docs/types/value-types.rst
Normal 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.
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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{};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
set(sources
|
||||
Algorithms.h
|
||||
Assertions.h
|
||||
boost_multiprecision_number_compare_bug_workaround.hpp
|
||||
Common.h
|
||||
CommonData.cpp
|
||||
CommonData.h
|
||||
|
@ -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>
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) \
|
||||
|
@ -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)
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
)),
|
||||
})
|
||||
{
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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.
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user