mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #11620 from ethereum/breakingMerge
Merge develop into breaking
This commit is contained in:
commit
3e086badb4
@ -9,20 +9,20 @@ version: 2.1
|
||||
parameters:
|
||||
ubuntu-2004-docker-image:
|
||||
type: string
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-5
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:2d306b8da3485c2584a8868d656dc36c1ae50f003ff085ad2e904e312534b9b7"
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-6
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:da44d7f78e093f7f0415abf07f7c1fd1c2ed4fa65fefea428821a05186c42ec9"
|
||||
ubuntu-2004-clang-docker-image:
|
||||
type: string
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-5
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:4fbc7a99dd0b204fef587856d89640e4b2060d459ba15c32b89733b2a6054d7f"
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-6
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c78dd9c48d393b57afe053aeb2d0d358a9f31ac85039a181724c2f8408d0bcf8"
|
||||
ubuntu-1604-clang-ossfuzz-docker-image:
|
||||
type: string
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-8
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:42f47b7ddafbf57b4e48357022cf34dc38ae477b05ddc2210e7ed68d821c2019"
|
||||
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-9
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:5078e1d74ab6f4329e9218c2d8c0ebe2d42817a3d4c3c62ce887100cbe9bc739"
|
||||
emscripten-docker-image:
|
||||
type: string
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-4
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:434719d8104cab47712dd1f56f255994d04eb65b802c0d382790071c1a0c074b"
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-5
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d28afb9624c2352ea40f157d1a321ffac77f54a21e33a8e8744f9126b780ded4"
|
||||
|
||||
orbs:
|
||||
win: circleci/windows@2.2.0
|
||||
@ -354,8 +354,8 @@ jobs:
|
||||
command: apt -q update && apt install -y python3-pip
|
||||
- run:
|
||||
name: Install pylint
|
||||
command: python3 -m pip install pylint z3-solver pygments-lexer-solidity
|
||||
# also z3-solver to make sure pylint knows about this module, pygments-lexer-solidity for docs
|
||||
command: python3 -m pip install pylint z3-solver pygments-lexer-solidity parsec tabulate
|
||||
# also z3-solver, parsec and tabulate to make sure pylint knows about this module, pygments-lexer-solidity for docs
|
||||
- run:
|
||||
name: Linting Python Scripts
|
||||
command: ./scripts/pylint_all.py
|
||||
@ -571,7 +571,9 @@ jobs:
|
||||
|
||||
b_archlinux:
|
||||
docker:
|
||||
- image: archlinux/base
|
||||
# FIXME: Newer releases won't work until CircleCI updates its host machines.
|
||||
# See https://github.com/ethereum/solidity/pull/11332
|
||||
- image: archlinux:base-20210131.0.14634
|
||||
environment:
|
||||
TERM: xterm
|
||||
MAKEFLAGS: -j 3
|
||||
@ -710,11 +712,19 @@ jobs:
|
||||
|
||||
t_archlinux_soltest: &t_archlinux_soltest
|
||||
docker:
|
||||
- image: archlinux/base
|
||||
# FIXME: Newer releases won't work until CircleCI updates its host machines.
|
||||
# See https://github.com/ethereum/solidity/pull/11332
|
||||
- image: archlinux:base-20210131.0.14634
|
||||
environment:
|
||||
EVM: constantinople
|
||||
OPTIMIZE: 0
|
||||
TERM: xterm
|
||||
# For Archlinux we do not have prebuilt docker images and we would need to build evmone from source,
|
||||
# thus we forgo semantics tests to speed things up.
|
||||
# FIXME: Z3 4.8.11 prerelease is now in main Arch Linux repos but it makes some of our SMT
|
||||
# tests hang. Disabling SMT tests until we get a proper release.
|
||||
# See https://github.com/Z3Prover/z3/issues/5330 for more details.
|
||||
SOLTEST_FLAGS: --no-semantic-tests --no-smt
|
||||
steps:
|
||||
- run:
|
||||
name: Install runtime dependencies
|
||||
|
@ -37,6 +37,8 @@ set -eu
|
||||
|
||||
if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not restored), rebuild dependencies
|
||||
then
|
||||
git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow
|
||||
git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow
|
||||
brew update
|
||||
brew unlink python
|
||||
brew install boost
|
||||
@ -55,12 +57,12 @@ then
|
||||
rm -rf z3-4.8.10-x64-osx-10.15.7
|
||||
|
||||
# evmone
|
||||
wget https://github.com/ethereum/evmone/releases/download/v0.4.0/evmone-0.4.0-darwin-x86_64.tar.gz
|
||||
tar xzpf evmone-0.4.0-darwin-x86_64.tar.gz -C /usr/local
|
||||
rm -f evmone-0.4.0-darwin-x86_64.tar.gz
|
||||
wget https://github.com/ethereum/evmone/releases/download/v0.7.0/evmone-0.7.0-darwin-x86_64.tar.gz
|
||||
tar xzpf evmone-0.7.0-darwin-x86_64.tar.gz -C /usr/local
|
||||
rm -f evmone-0.7.0-darwin-x86_64.tar.gz
|
||||
|
||||
# hera
|
||||
wget https://github.com/ewasm/hera/releases/download/v0.3.2/hera-0.3.2-darwin-x86_64.tar.gz
|
||||
tar xzpf hera-0.3.2-darwin-x86_64.tar.gz -C /usr/local
|
||||
rm -f hera-0.3.2-darwin-x86_64.tar.gz
|
||||
wget https://github.com/ewasm/hera/releases/download/v0.3.2-evmc8/hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz
|
||||
tar xzpf hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz -C /usr/local
|
||||
rm -f hera-0.3.2+commit.dc886eb7-darwin-x86_64.tar.gz
|
||||
fi
|
||||
|
@ -28,7 +28,9 @@ set -e
|
||||
|
||||
REPODIR="$(realpath "$(dirname "$0")"/..)"
|
||||
|
||||
EVM_VALUES=(homestead byzantium constantinople petersburg istanbul)
|
||||
EVM_VALUES=(homestead byzantium constantinople petersburg istanbul berlin)
|
||||
DEFAULT_EVM=berlin
|
||||
[[ " ${EVM_VALUES[*]} " =~ $DEFAULT_EVM ]]
|
||||
OPTIMIZE_VALUES=(0 1)
|
||||
STEPS=$(( 1 + ${#EVM_VALUES[@]} * ${#OPTIMIZE_VALUES[@]} ))
|
||||
|
||||
@ -45,7 +47,7 @@ STEP=1
|
||||
|
||||
|
||||
# Run for ABI encoder v1, without SMTChecker tests.
|
||||
[[ " $RUN_STEPS " == *" $STEP "* ]] && EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V1=1 BOOST_TEST_ARGS="-t !smtCheckerTests" "${REPODIR}/.circleci/soltest.sh"
|
||||
[[ " $RUN_STEPS " == *" $STEP "* ]] && EVM="${DEFAULT_EVM}" OPTIMIZE=1 ABI_ENCODER_V1=1 BOOST_TEST_ARGS="-t !smtCheckerTests" "${REPODIR}/.circleci/soltest.sh"
|
||||
STEP=$((STEP + 1))
|
||||
|
||||
for OPTIMIZE in "${OPTIMIZE_VALUES[@]}"
|
||||
@ -56,7 +58,7 @@ do
|
||||
EWASM_ARGS=""
|
||||
[ "${EVM}" = "byzantium" ] && [ "${OPTIMIZE}" = "0" ] && EWASM_ARGS="--ewasm"
|
||||
ENFORCE_GAS_ARGS=""
|
||||
[ "${EVM}" = "istanbul" ] && ENFORCE_GAS_ARGS="--enforce-gas-cost"
|
||||
[ "${EVM}" = "${DEFAULT_EVM}" ] && ENFORCE_GAS_ARGS="--enforce-gas-cost"
|
||||
# Run SMTChecker tests only when OPTIMIZE == 0
|
||||
DISABLE_SMTCHECKER=""
|
||||
[ "${OPTIMIZE}" != "0" ] && DISABLE_SMTCHECKER="-t !smtCheckerTests"
|
||||
|
22
.github/ISSUE_TEMPLATE/documentation_issue.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/documentation_issue.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Documentation Issue
|
||||
about: Solidity documentation.
|
||||
---
|
||||
|
||||
## Page
|
||||
|
||||
<!--
|
||||
Please link directly to the page which you think has a problem
|
||||
-->
|
||||
|
||||
## Abstract
|
||||
|
||||
<!--
|
||||
Please describe in detail what is wrong.
|
||||
-->
|
||||
|
||||
## Pull request
|
||||
|
||||
<!--
|
||||
Please link to your pull request which resolves this issue
|
||||
-->
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -34,6 +34,7 @@ prerelease.txt
|
||||
/build*
|
||||
emscripten_build/
|
||||
docs/_build
|
||||
docs/_static/robots.txt
|
||||
__pycache__
|
||||
docs/utils/*.pyc
|
||||
/deps/downloads/
|
||||
|
@ -21,7 +21,7 @@ include(EthPolicy)
|
||||
eth_policy()
|
||||
|
||||
# project name and version should be set after cmake_policy CMP0048
|
||||
set(PROJECT_VERSION "0.8.5")
|
||||
set(PROJECT_VERSION "0.8.7")
|
||||
# OSX target needed in order to support std::visit
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
|
||||
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
|
||||
|
73
Changelog.md
73
Changelog.md
@ -3,7 +3,78 @@
|
||||
Breaking changes:
|
||||
* `error` is now a keyword that can only be used for defining errors.
|
||||
|
||||
### 0.8.5 (unreleased)
|
||||
|
||||
### 0.8.7 (unreleased)
|
||||
|
||||
Language Features:
|
||||
|
||||
|
||||
Compiler Features:
|
||||
* AssemblyStack: Also run opcode-based optimizer when compiling Yul code.
|
||||
* Yul EVM Code Transform: Do not reuse stack slots that immediately become unreachable.
|
||||
* Yul EVM Code Transform: Also pop unused argument slots for functions without return variables (under the same restrictions as for functions with return variables).
|
||||
* Yul Optimizer: Move function arguments and return variables to memory with the experimental Stack Limit Evader (which is not enabled by default).
|
||||
|
||||
|
||||
Bugfixes:
|
||||
* Code Generator: Fix crash when passing an empty string literal to ``bytes.concat()``.
|
||||
* Code Generator: Fix internal compiler error when calling functions bound to calldata structs and arrays.
|
||||
* Code Generator: Fix internal compiler error when passing a 32-byte hex literal or a zero literal to ``bytes.concat()`` by disallowing such literals.
|
||||
* Type Checker: Fix internal error and prevent static calls to unimplemented modifiers.
|
||||
* Yul Code Generator: Fix internal compiler error when using a long literal with bitwise negation.
|
||||
|
||||
|
||||
### 0.8.6 (2021-06-22)
|
||||
|
||||
Language Features:
|
||||
* Yul: Special meaning of ``".metadata"`` data object in Yul object.
|
||||
|
||||
|
||||
Bugfixes:
|
||||
* Control Flow Graph: Fix incorrectly reported unreachable code.
|
||||
* Solc-Js: When running ``solcjs`` without the ``--optimize`` flag, use ``settings.optimizer.enabled=false`` in Standard JSON instead of omitting the key.
|
||||
* Standard JSON: Omitting ``settings.optimizer.enabled`` was not equivalent to setting it to ``false``. It meant disabling also the peephole optimizer and jumpdest remover which by default still run with ``enabled=false``.
|
||||
|
||||
|
||||
### 0.8.5 (2021-06-10)
|
||||
|
||||
Language Features:
|
||||
* Allowing conversion from ``bytes`` and ``bytes`` slices to ``bytes1``/.../``bytes32``.
|
||||
* Yul: Add ``verbatim`` builtin function to inject arbitrary bytecode.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
* Code Generator: Insert helper functions for panic codes instead of inlining unconditionally. This can reduce costs if many panics (checks) are inserted, but can increase costs where few panics are used.
|
||||
* EVM: Set the default EVM version to "Berlin".
|
||||
* SMTChecker: Function definitions can be annotated with the custom Natspec tag ``custom:smtchecker abstract-function-nondet`` to be abstracted by a nondeterministic value when called.
|
||||
* Standard JSON / combined JSON: New artifact "functionDebugData" that contains bytecode offsets of entry points of functions and potentially more information in the future.
|
||||
* Yul Optimizer: Evaluate ``keccak256(a, c)``, when the value at memory location ``a`` is known at compile time and ``c`` is a constant ``<= 32``.
|
||||
|
||||
|
||||
Bugfixes:
|
||||
* AST: Do not output value of Yul literal if it is not a valid UTF-8 string.
|
||||
* Code Generator: Fix internal error when function arrays are assigned to storage variables and the function types can be implicitly converted but are not identical.
|
||||
* Code Generator: Fix internal error when super would have to skip an unimplemented function in the virtual resolution order.
|
||||
* Control Flow Graph: Assume unimplemented modifiers use a placeholder.
|
||||
* Control Flow Graph: Take internal calls to functions that always revert into account for reporting unused or unassigned variables.
|
||||
* Function Call Graph: Fix internal error connected with circular constant references.
|
||||
* Name Resolver: Do not issue shadowing warning if the shadowing name is not directly accessible.
|
||||
* Natspec: Allow multiple ``@return`` tags on public state variable documentation.
|
||||
* SMTChecker: Fix internal error on conversion from ``bytes`` to ``fixed bytes``.
|
||||
* SMTChecker: Fix internal error on external calls from the constructor.
|
||||
* SMTChecker: Fix internal error on struct constructor with fixed bytes member initialized with string literal.
|
||||
* Source Locations: Properly set source location of scoped blocks.
|
||||
* Standard JSON: Properly allow the ``inliner`` setting under ``settings.optimizer.details``.
|
||||
* Type Checker: Fix internal compiler error related to having mapping types in constructor parameter for abstract contracts.
|
||||
* Type Checker: Fix internal compiler error when attempting to use an invalid external function type on pre-byzantium EVMs.
|
||||
* Type Checker: Fix internal compiler error when overriding receive ether function with one having different parameters during inheritance.
|
||||
* Type Checker: Make errors about (nested) mapping type in event or error parameter into fatal type errors.
|
||||
* Type Checker: Fix internal compiler error when overriding an implemented modifier with an unimplemented one.
|
||||
|
||||
|
||||
AST Changes:
|
||||
* Add member `hexValue` for Yul string and hex literals.
|
||||
|
||||
|
||||
|
||||
### 0.8.4 (2021-04-21)
|
||||
|
@ -4,6 +4,7 @@
|
||||
[](https://gitter.im/ethereum/solidity)
|
||||
[](https://forum.soliditylang.org/)
|
||||
[](https://twitter.com/solidity_lang)
|
||||
[](https://fosstodon.org/@solidity)
|
||||
|
||||
You can talk to us on Gitter and Matrix, tweet at us on Twitter or create a new topic in the Solidity forum. Questions, feedback, and suggestions are welcome!
|
||||
|
||||
|
@ -61,7 +61,6 @@
|
||||
### Documentation
|
||||
- [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` at the bottom of the page and click `BUILD`).
|
||||
- [ ] In the admin panel, select `Versions` in the menu and set the default version to the released one.
|
||||
- [ ] If it is a non-breaking release, block indexing of previous release version in the ``robots.txt`` file.
|
||||
|
||||
### Release solc-js
|
||||
- [ ] Wait until solc-bin was properly deployed. You can test this via remix - a test run through remix is advisable anyway.
|
||||
@ -72,5 +71,7 @@
|
||||
### Post-release
|
||||
- [ ] Publish the blog post.
|
||||
- [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry.
|
||||
- [ ] Announce on Twitter and Reddit.
|
||||
- [ ] Announce on Twitter, including links to the release and the blog post.
|
||||
- [ ] Share announcement on Reddit and Solidity forum.
|
||||
- [ ] Update the release information section on [soliditylang.org](https://github.com/ethereum/solidity-portal).
|
||||
- [ ] Lean back, wait for bug reports and repeat from step 1 :)
|
||||
|
@ -290,7 +290,7 @@ It is still possible to interface with contracts written for Solidity versions p
|
||||
v0.5.0 (or the other way around) by defining interfaces for them.
|
||||
Consider you have the following pre-0.5.0 contract already deployed:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.4.25;
|
||||
@ -308,7 +308,7 @@ Consider you have the following pre-0.5.0 contract already deployed:
|
||||
|
||||
This will no longer compile with Solidity v0.5.0. However, you can define a compatible interface for it:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -326,7 +326,7 @@ the function will work with ``staticcall``.
|
||||
|
||||
Given the interface defined above, you can now easily use the already deployed pre-0.5.0 contract:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -347,7 +347,7 @@ Similarly, pre-0.5.0 libraries can be used by defining the functions of the libr
|
||||
supplying the address of the pre-0.5.0 library during linking (see :ref:`commandline-compiler` for how to use the
|
||||
commandline compiler for linking):
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// This will not compile after 0.6.0
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
@ -372,7 +372,7 @@ v0.5.0 with some of the changes listed in this section.
|
||||
|
||||
Old version:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.4.25;
|
||||
@ -435,7 +435,7 @@ Old version:
|
||||
|
||||
New version:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.5.0;
|
||||
|
95
docs/_static/robots.txt
vendored
95
docs/_static/robots.txt
vendored
@ -1,95 +0,0 @@
|
||||
Sitemap: http://docs.soliditylang.org/sitemap.xml
|
||||
|
||||
# Prevent documentation for the development branches from showing up in search results.
|
||||
Disallow: /en/develop/
|
||||
Disallow: /en/breaking/
|
||||
|
||||
# Prevent documentation for the older Solidity versions from showing up in search results.
|
||||
|
||||
Disallow: /en/v0.1.2/
|
||||
Disallow: /en/v0.1.3/
|
||||
Disallow: /en/v0.1.4/
|
||||
Disallow: /en/v0.1.5/
|
||||
Disallow: /en/v0.1.6/
|
||||
Disallow: /en/v0.1.7/
|
||||
Disallow: /en/v0.2.0/
|
||||
Disallow: /en/v0.2.1/
|
||||
Disallow: /en/v0.2.2/
|
||||
Disallow: /en/v0.3.0/
|
||||
Disallow: /en/v0.3.1/
|
||||
Disallow: /en/v0.3.2/
|
||||
Disallow: /en/v0.3.3/
|
||||
Disallow: /en/v0.3.4/
|
||||
Disallow: /en/v0.3.5/
|
||||
Disallow: /en/v0.3.6/
|
||||
Disallow: /en/v0.4.0/
|
||||
Disallow: /en/v0.4.1/
|
||||
Disallow: /en/v0.4.2/
|
||||
Disallow: /en/v0.4.3/
|
||||
Disallow: /en/v0.4.4/
|
||||
Disallow: /en/v0.4.5/
|
||||
Disallow: /en/v0.4.6/
|
||||
Disallow: /en/v0.4.7/
|
||||
Disallow: /en/v0.4.8/
|
||||
Disallow: /en/v0.4.9/
|
||||
Disallow: /en/v0.4.10/
|
||||
Disallow: /en/v0.4.11/
|
||||
Disallow: /en/v0.4.12/
|
||||
Disallow: /en/v0.4.13/
|
||||
Disallow: /en/v0.4.14/
|
||||
Disallow: /en/v0.4.15/
|
||||
Disallow: /en/v0.4.16/
|
||||
Disallow: /en/v0.4.17/
|
||||
Disallow: /en/v0.4.18/
|
||||
Disallow: /en/v0.4.19/
|
||||
Disallow: /en/v0.4.20/
|
||||
Disallow: /en/v0.4.21/
|
||||
Disallow: /en/v0.4.22/
|
||||
Disallow: /en/v0.4.23/
|
||||
Disallow: /en/v0.4.24/
|
||||
Disallow: /en/v0.4.25/
|
||||
Disallow: /en/v0.4.26/
|
||||
Disallow: /en/v0.5.0/
|
||||
Disallow: /en/v0.5.1/
|
||||
Disallow: /en/v0.5.2/
|
||||
Disallow: /en/v0.5.3/
|
||||
Disallow: /en/v0.5.4/
|
||||
Disallow: /en/v0.5.5/
|
||||
Disallow: /en/v0.5.6/
|
||||
Disallow: /en/v0.5.7/
|
||||
Disallow: /en/v0.5.8/
|
||||
Disallow: /en/v0.5.9/
|
||||
Disallow: /en/v0.5.10/
|
||||
Disallow: /en/v0.5.11/
|
||||
Disallow: /en/v0.5.12/
|
||||
Disallow: /en/v0.5.13/
|
||||
Disallow: /en/v0.5.14/
|
||||
Disallow: /en/v0.5.15/
|
||||
Disallow: /en/v0.5.16/
|
||||
Disallow: /en/v0.5.17/
|
||||
Disallow: /en/v0.6.0/
|
||||
Disallow: /en/v0.6.1/
|
||||
Disallow: /en/v0.6.2/
|
||||
Disallow: /en/v0.6.3/
|
||||
Disallow: /en/v0.6.4/
|
||||
Disallow: /en/v0.6.5/
|
||||
Disallow: /en/v0.6.6/
|
||||
Disallow: /en/v0.6.7/
|
||||
Disallow: /en/v0.6.8/
|
||||
Disallow: /en/v0.6.9/
|
||||
Disallow: /en/v0.6.10/
|
||||
Disallow: /en/v0.6.11/
|
||||
Disallow: /en/v0.6.12/
|
||||
Disallow: /en/v0.7.0/
|
||||
Disallow: /en/v0.7.1/
|
||||
Disallow: /en/v0.7.2/
|
||||
Disallow: /en/v0.7.3/
|
||||
Disallow: /en/v0.7.4/
|
||||
Disallow: /en/v0.7.5/
|
||||
# Allow the last patch release of the 0.7.x series
|
||||
#Disallow: /en/v0.7.6/
|
||||
Disallow: /en/v0.8.0/
|
||||
Disallow: /en/v0.8.1/
|
||||
Disallow: /en/v0.8.2/
|
||||
# Allow the latest release
|
||||
#Disallow: /en/v0.8.3/
|
2
docs/_templates/layout.html
vendored
2
docs/_templates/layout.html
vendored
@ -2,5 +2,5 @@
|
||||
|
||||
{% block menu %}
|
||||
{{ super() }}
|
||||
<a href="genindex.html">Keyword Index</a>
|
||||
<a href="{{ pathto('genindex') }}">Keyword Index</a>
|
||||
{% endblock %}
|
||||
|
@ -235,7 +235,7 @@ Examples
|
||||
|
||||
Given the contract:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -465,17 +465,20 @@ address, a series of up to four topics and some arbitrary length binary data. Ev
|
||||
ABI in order to interpret this (together with an interface spec) as a properly typed structure.
|
||||
|
||||
Given an event name and series of event parameters, we split them into two sub-series: those which are indexed and
|
||||
those which are not. Those which are indexed, which may number up to 3, are used alongside the Keccak hash of the
|
||||
event signature to form the topics of the log entry. Those which are not indexed form the byte array of the event.
|
||||
those which are not.
|
||||
Those which are indexed, which may number up to 3 (for non-anonymous events) or 4 (for anonymous ones), are used
|
||||
alongside the Keccak hash of the event signature to form the topics of the log entry.
|
||||
Those which are not indexed form the byte array of the event.
|
||||
|
||||
In effect, a log entry using this ABI is described as:
|
||||
|
||||
- ``address``: the address of the contract (intrinsically provided by Ethereum);
|
||||
- ``topics[0]``: ``keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")")`` (``canonical_type_of``
|
||||
is a function that simply returns the canonical type of a given argument, e.g. for ``uint indexed foo``, it would
|
||||
return ``uint256``). If the event is declared as ``anonymous`` the ``topics[0]`` is not generated;
|
||||
- ``topics[n]``: ``abi_encode(EVENT_INDEXED_ARGS[n - 1])`` (``EVENT_INDEXED_ARGS`` is the series of ``EVENT_ARGS``
|
||||
that are indexed);
|
||||
return ``uint256``). This value is only present in ``topics[0]`` if the event is not declared as ``anonymous``;
|
||||
- ``topics[n]``: ``abi_encode(EVENT_INDEXED_ARGS[n - 1])`` if the event is not declared as ``anonymous``
|
||||
or ``abi_encode(EVENT_INDEXED_ARGS[n])`` if it is (``EVENT_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that
|
||||
are indexed);
|
||||
- ``data``: ABI encoding of ``EVENT_NON_INDEXED_ARGS`` (``EVENT_NON_INDEXED_ARGS`` is the series of ``EVENT_ARGS``
|
||||
that are not indexed, ``abi_encode`` is the ABI encoding function used for returning a series of typed values
|
||||
from a function, as described above).
|
||||
@ -507,7 +510,7 @@ call.
|
||||
As an example, let us consider the following contract whose ``transfer`` function always
|
||||
reverts with a custom error of "insufficient balance":
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -596,7 +599,7 @@ Errors look as follows:
|
||||
|
||||
For example,
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -650,7 +653,7 @@ which is of array type and has the same structure as the top-level object except
|
||||
|
||||
As an example, the code
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.4 <0.9.0;
|
||||
|
@ -39,7 +39,7 @@ load it into a ``bytes`` variable. This is not possible with "plain Solidity" an
|
||||
idea is that reusable assembly libraries can enhance the Solidity language
|
||||
without a compiler change.
|
||||
|
||||
.. code::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -65,7 +65,7 @@ without a compiler change.
|
||||
Inline assembly is also beneficial in cases where the optimizer fails to produce
|
||||
efficient code, for example:
|
||||
|
||||
.. code::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -141,7 +141,8 @@ Both expressions can also be assigned to.
|
||||
|
||||
Local Solidity variables are available for assignments, for example:
|
||||
|
||||
.. code::
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -216,7 +217,9 @@ starting from where this pointer points at and update it.
|
||||
There is no guarantee that the memory has not been used before and thus
|
||||
you cannot assume that its contents are zero bytes.
|
||||
There is no built-in mechanism to release or free allocated memory.
|
||||
Here is an assembly snippet you can use for allocating memory that follows the process outlined above::
|
||||
Here is an assembly snippet you can use for allocating memory that follows the process outlined above
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
function allocate(length) -> pos {
|
||||
pos := mload(0x40)
|
||||
|
@ -1,8 +1,10 @@
|
||||
[
|
||||
{
|
||||
"uid": "SOL-2021-2",
|
||||
"name": "ABIDecodeTwoDimensionalArrayMemory",
|
||||
"summary": "If used on memory byte arrays, result of the function ``abi.decode`` can depend on the contents of memory outside of the actual byte array that is decoded.",
|
||||
"description": "The ABI specification uses pointers to data areas for everything that is dynamically-sized. When decoding data from memory (instead of calldata), the ABI decoder did not properly validate some of these pointers. More specifically, it was possible to use large values for the pointers inside arrays such that computing the offset resulted in an undetected overflow. This could lead to these pointers targeting areas in memory outside of the actual area to be decoded. This way, it was possible for ``abi.decode`` to return different values for the same encoded byte array.",
|
||||
"link": "https://blog.soliditylang.org/2021/04/21/decoding-from-memory-bug/",
|
||||
"introduced": "0.4.16",
|
||||
"fixed": "0.8.4",
|
||||
"conditions": {
|
||||
@ -11,6 +13,7 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2021-1",
|
||||
"name": "KeccakCaching",
|
||||
"summary": "The bytecode optimizer incorrectly re-used previously evaluated Keccak-256 hashes. You are unlikely to be affected if you do not compute Keccak-256 hashes in inline assembly.",
|
||||
"description": "Solidity's bytecode optimizer has a step that can compute Keccak-256 hashes, if the contents of the memory are known during compilation time. This step also has a mechanism to determine that two Keccak-256 hashes are equal even if the values in memory are not known during compile time. This mechanism had a bug where Keccak-256 of the same memory content, but different sizes were considered equal. More specifically, ``keccak256(mpos1, length1)`` and ``keccak256(mpos2, length2)`` in some cases were considered equal if ``length1`` and ``length2``, when rounded up to nearest multiple of 32 were the same, and when the memory contents at ``mpos1`` and ``mpos2`` can be deduced to be equal. You maybe affected if you compute multiple Keccak-256 hashes of the same content, but with different lengths inside inline assembly. You are unaffected if your code uses ``keccak256`` with a length that is not a compile-time constant or if it is always a multiple of 32.",
|
||||
@ -22,20 +25,25 @@
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-11",
|
||||
"name": "EmptyByteArrayCopy",
|
||||
"summary": "Copying an empty byte array (or string) from memory or calldata to storage can result in data corruption if the target array's length is increased subsequently without storing new data.",
|
||||
"description": "The routine that copies byte arrays from memory or calldata to storage stores unrelated data from after the source array in the storage slot if the source array is empty. If the storage array's length is subsequently increased either by using ``.push()`` or by assigning to its ``.length`` attribute (only before 0.6.0), the newly created byte array elements will not be zero-initialized, but contain the unrelated data. You are not affected if you do not assign to ``.length`` and do not use ``.push()`` on byte arrays, or only use ``.push(<arg>)`` or manually initialize the new elements.",
|
||||
"link": "https://blog.soliditylang.org/2020/10/19/empty-byte-array-copy-bug/",
|
||||
"fixed": "0.7.4",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-10",
|
||||
"name": "DynamicArrayCleanup",
|
||||
"summary": "When assigning a dynamically-sized array with types of size at most 16 bytes in storage causing the assigned array to shrink, some parts of deleted slots were not zeroed out.",
|
||||
"description": "Consider a dynamically-sized array in storage whose base-type is small enough such that multiple values can be packed into a single slot, such as `uint128[]`. Let us define its length to be `l`. When this array gets assigned from another array with a smaller length, say `m`, the slots between elements `m` and `l` have to be cleaned by zeroing them out. However, this cleaning was not performed properly. Specifically, after the slot corresponding to `m`, only the first packed value was cleaned up. If this array gets resized to a length larger than `m`, the indices corresponding to the unclean parts of the slot contained the original value, instead of 0. The resizing here is performed by assigning to the array `length`, by a `push()` or via inline assembly. You are not affected if you are only using `.push(<arg>)` or if you assign a value (even zero) to the new elements after increasing the length of the array.",
|
||||
"link": "https://blog.soliditylang.org/2020/10/07/solidity-dynamic-array-cleanup-bug/",
|
||||
"fixed": "0.7.3",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-9",
|
||||
"name": "FreeFunctionRedefinition",
|
||||
"summary": "The compiler does not flag an error when two or more free functions with the same name and parameter types are defined in a source unit or when an imported free function alias shadows another free function with a different name but identical parameter types.",
|
||||
"description": "In contrast to functions defined inside contracts, free functions with identical names and parameter types did not create an error. Both definition of free functions with identical name and parameter types and an imported free function with an alias that shadows another function with a different name but identical parameter types were permitted due to which a call to either the multiply defined free function or the imported free function alias within a contract led to the execution of that free function which was defined first within the source unit. Subsequently defined identical free function definitions were silently ignored and their code generation was skipped.",
|
||||
@ -44,6 +52,7 @@
|
||||
"severity": "low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-8",
|
||||
"name": "UsingForCalldata",
|
||||
"summary": "Function calls to internal library functions with calldata parameters called via ``using for`` can result in invalid data being read.",
|
||||
"description": "Function calls to internal library functions using the ``using for`` mechanism copied all calldata parameters to memory first and passed them on like that, regardless of whether it was an internal or an external call. Due to that, the called function would receive a memory pointer that is interpreted as a calldata pointer. Since dynamically sized arrays are passed using two stack slots for calldata, but only one for memory, this can lead to stack corruption. An affected library call will consider the JUMPDEST to which it is supposed to return as part of its arguments and will instead jump out to whatever was on the stack before the call.",
|
||||
@ -52,6 +61,7 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-7",
|
||||
"name": "MissingEscapingInFormatting",
|
||||
"summary": "String literals containing double backslash characters passed directly to external or encoding function calls can lead to a different string being used when ABIEncoderV2 is enabled.",
|
||||
"description": "When ABIEncoderV2 is enabled, string literals passed directly to encoding functions or external function calls are stored as strings in the intemediate code. Characters outside the printable range are handled correctly, but backslashes are not escaped in this procedure. This leads to double backslashes being reduced to single backslashes and consequently re-interpreted as escapes potentially resulting in a different string being encoded.",
|
||||
@ -63,6 +73,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-6",
|
||||
"name": "ArraySliceDynamicallyEncodedBaseType",
|
||||
"summary": "Accessing array slices of arrays with dynamically encoded base types (e.g. multi-dimensional arrays) can result in invalid data being read.",
|
||||
"description": "For arrays with dynamically sized base types, index range accesses that use a start expression that is non-zero will result in invalid array slices. Any index access to such array slices will result in data being read from incorrect calldata offsets. Array slices are only supported for dynamic calldata types and all problematic type require ABIEncoderV2 to be enabled.",
|
||||
@ -74,6 +85,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-5",
|
||||
"name": "ImplicitConstructorCallvalueCheck",
|
||||
"summary": "The creation code of a contract that does not define a constructor but has a base that does define a constructor did not revert for calls with non-zero value.",
|
||||
"description": "Starting from Solidity 0.4.5 the creation code of contracts without explicit payable constructor is supposed to contain a callvalue check that results in contract creation reverting, if non-zero value is passed. However, this check was missing in case no explicit constructor was defined in a contract at all, but the contract has a base that does define a constructor. In these cases it is possible to send value in a contract creation transaction or using inline assembly without revert, even though the creation code is supposed to be non-payable.",
|
||||
@ -82,6 +94,7 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-4",
|
||||
"name": "TupleAssignmentMultiStackSlotComponents",
|
||||
"summary": "Tuple assignments with components that occupy several stack slots, i.e. nested tuples, pointers to external functions or references to dynamically sized calldata arrays, can result in invalid values.",
|
||||
"description": "Tuple assignments did not correctly account for tuple components that occupy multiple stack slots in case the number of stack slots differs between left-hand-side and right-hand-side. This can either happen in the presence of nested tuples or if the right-hand-side contains external function pointers or references to dynamic calldata arrays, while the left-hand-side contains an omission.",
|
||||
@ -90,15 +103,17 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-3",
|
||||
"name": "MemoryArrayCreationOverflow",
|
||||
"summary": "The creation of very large memory arrays can result in overlapping memory regions and thus memory corruption.",
|
||||
"description": "No runtime overflow checks were performed for the length of memory arrays during creation. In cases for which the memory size of an array in bytes, i.e. the array length times 32, is larger than 2^256-1, the memory allocation will overflow, potentially resulting in overlapping memory areas. The length of the array is still stored correctly, so copying or iterating over such an array will result in out-of-gas.",
|
||||
"link": "https://solidity.ethereum.org/2020/04/06/memory-creation-overflow-bug/",
|
||||
"link": "https://blog.soliditylang.org/2020/04/06/memory-creation-overflow-bug/",
|
||||
"introduced": "0.2.0",
|
||||
"fixed": "0.6.5",
|
||||
"severity": "low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-1",
|
||||
"name": "YulOptimizerRedundantAssignmentBreakContinue",
|
||||
"summary": "The Yul optimizer can remove essential assignments to variables declared inside for loops when Yul's continue or break statement is used. You are unlikely to be affected if you do not use inline assembly with for loops and continue and break statements.",
|
||||
"description": "The Yul optimizer has a stage that removes assignments to variables that are overwritten again or are not used in all following control-flow branches. This logic incorrectly removes such assignments to variables declared inside a for loop if they can be removed in a control-flow branch that ends with ``break`` or ``continue`` even though they cannot be removed in other control-flow branches. Variables declared outside of the respective for loop are not affected.",
|
||||
@ -110,6 +125,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-2",
|
||||
"name": "privateCanBeOverridden",
|
||||
"summary": "Private methods can be overridden by inheriting contracts.",
|
||||
"description": "While private methods of base contracts are not visible and cannot be called directly from the derived contract, it is still possible to declare a function of the same name and type and thus change the behaviour of the base contract's function.",
|
||||
@ -118,6 +134,7 @@
|
||||
"severity": "low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2020-1",
|
||||
"name": "YulOptimizerRedundantAssignmentBreakContinue0.5",
|
||||
"summary": "The Yul optimizer can remove essential assignments to variables declared inside for loops when Yul's continue or break statement is used. You are unlikely to be affected if you do not use inline assembly with for loops and continue and break statements.",
|
||||
"description": "The Yul optimizer has a stage that removes assignments to variables that are overwritten again or are not used in all following control-flow branches. This logic incorrectly removes such assignments to variables declared inside a for loop if they can be removed in a control-flow branch that ends with ``break`` or ``continue`` even though they cannot be removed in other control-flow branches. Variables declared outside of the respective for loop are not affected.",
|
||||
@ -129,6 +146,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-10",
|
||||
"name": "ABIEncoderV2LoopYulOptimizer",
|
||||
"summary": "If both the experimental ABIEncoderV2 and the experimental Yul optimizer are activated, one component of the Yul optimizer may reuse data in memory that has been changed in the meantime.",
|
||||
"description": "The Yul optimizer incorrectly replaces ``mload`` and ``sload`` calls with values that have been previously written to the load location (and potentially changed in the meantime) if all of the following conditions are met: (1) there is a matching ``mstore`` or ``sstore`` call before; (2) the contents of memory or storage is only changed in a function that is called (directly or indirectly) in between the first store and the load call; (3) called function contains a for loop where the same memory location is changed in the condition or the post or body block. When used in Solidity mode, this can only happen if the experimental ABIEncoderV2 is activated and the experimental Yul optimizer has been activated manually in addition to the regular optimizer in the compiler settings.",
|
||||
@ -142,6 +160,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-9",
|
||||
"name": "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
|
||||
"summary": "Reading from calldata structs that contain dynamically encoded, but statically-sized members can result in incorrect values.",
|
||||
"description": "When a calldata struct contains a dynamically encoded, but statically-sized member, the offsets for all subsequent struct members are calculated incorrectly. All reads from such members will result in invalid values. Only calldata structs are affected, i.e. this occurs in external functions with such structs as argument. Using affected structs in storage or memory or as arguments to public functions on the other hand works correctly.",
|
||||
@ -153,19 +172,21 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-8",
|
||||
"name": "SignedArrayStorageCopy",
|
||||
"summary": "Assigning an array of signed integers to a storage array of different type can lead to data corruption in that array.",
|
||||
"description": "In two's complement, negative integers have their higher order bits set. In order to fit into a shared storage slot, these have to be set to zero. When a conversion is done at the same time, the bits to set to zero were incorrectly determined from the source and not the target type. This means that such copy operations can lead to incorrect values being stored.",
|
||||
"link": "https://blog.ethereum.org/2019/06/25/solidity-storage-array-bugs/",
|
||||
"link": "https://blog.soliditylang.org/2019/06/25/solidity-storage-array-bugs/",
|
||||
"introduced": "0.4.7",
|
||||
"fixed": "0.5.10",
|
||||
"severity": "low/medium"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-7",
|
||||
"name": "ABIEncoderV2StorageArrayWithMultiSlotElement",
|
||||
"summary": "Storage arrays containing structs or other statically-sized arrays are not read properly when directly encoded in external function calls or in abi.encode*.",
|
||||
"description": "When storage arrays whose elements occupy more than a single storage slot are directly encoded in external function calls or using abi.encode*, their elements are read in an overlapping manner, i.e. the element pointer is not properly advanced between reads. This is not a problem when the storage data is first copied to a memory variable or if the storage array only contains value types or dynamically-sized arrays.",
|
||||
"link": "https://blog.ethereum.org/2019/06/25/solidity-storage-array-bugs/",
|
||||
"link": "https://blog.soliditylang.org/2019/06/25/solidity-storage-array-bugs/",
|
||||
"introduced": "0.4.16",
|
||||
"fixed": "0.5.10",
|
||||
"severity": "low",
|
||||
@ -174,6 +195,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-6",
|
||||
"name": "DynamicConstructorArgumentsClippedABIV2",
|
||||
"summary": "A contract's constructor that takes structs or arrays that contain dynamically-sized arrays reverts or decodes to invalid data.",
|
||||
"description": "During construction of a contract, constructor parameters are copied from the code section to memory for decoding. The amount of bytes to copy was calculated incorrectly in case all parameters are statically-sized but contain dynamically-sized arrays as struct members or inner arrays. Such types are only available if ABIEncoderV2 is activated.",
|
||||
@ -185,6 +207,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-5",
|
||||
"name": "UninitializedFunctionPointerInConstructor",
|
||||
"summary": "Calling uninitialized internal function pointers created in the constructor does not always revert and can cause unexpected behaviour.",
|
||||
"description": "Uninitialized internal function pointers point to a special piece of code that causes a revert when called. Jump target positions are different during construction and after deployment, but the code for setting this special jump target only considered the situation after deployment.",
|
||||
@ -193,6 +216,7 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-5",
|
||||
"name": "UninitializedFunctionPointerInConstructor_0.4.x",
|
||||
"summary": "Calling uninitialized internal function pointers created in the constructor does not always revert and can cause unexpected behaviour.",
|
||||
"description": "Uninitialized internal function pointers point to a special piece of code that causes a revert when called. Jump target positions are different during construction and after deployment, but the code for setting this special jump target only considered the situation after deployment.",
|
||||
@ -201,6 +225,7 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-4",
|
||||
"name": "IncorrectEventSignatureInLibraries",
|
||||
"summary": "Contract types used in events in libraries cause an incorrect event signature hash",
|
||||
"description": "Instead of using the type `address` in the hashed signature, the actual contract name was used, leading to a wrong hash in the logs.",
|
||||
@ -209,6 +234,7 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-4",
|
||||
"name": "IncorrectEventSignatureInLibraries_0.4.x",
|
||||
"summary": "Contract types used in events in libraries cause an incorrect event signature hash",
|
||||
"description": "Instead of using the type `address` in the hashed signature, the actual contract name was used, leading to a wrong hash in the logs.",
|
||||
@ -217,10 +243,11 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-3",
|
||||
"name": "ABIEncoderV2PackedStorage",
|
||||
"summary": "Storage structs and arrays with types shorter than 32 bytes can cause data corruption if encoded directly from storage using the experimental ABIEncoderV2.",
|
||||
"description": "Elements of structs and arrays that are shorter than 32 bytes are not properly decoded from storage when encoded directly (i.e. not via a memory type) using ABIEncoderV2. This can cause corruption in the values themselves but can also overwrite other parts of the encoded data.",
|
||||
"link": "https://blog.ethereum.org/2019/03/26/solidity-optimizer-and-abiencoderv2-bug/",
|
||||
"link": "https://blog.soliditylang.org/2019/03/26/solidity-optimizer-and-abiencoderv2-bug/",
|
||||
"introduced": "0.5.0",
|
||||
"fixed": "0.5.7",
|
||||
"severity": "low",
|
||||
@ -229,10 +256,11 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-3",
|
||||
"name": "ABIEncoderV2PackedStorage_0.4.x",
|
||||
"summary": "Storage structs and arrays with types shorter than 32 bytes can cause data corruption if encoded directly from storage using the experimental ABIEncoderV2.",
|
||||
"description": "Elements of structs and arrays that are shorter than 32 bytes are not properly decoded from storage when encoded directly (i.e. not via a memory type) using ABIEncoderV2. This can cause corruption in the values themselves but can also overwrite other parts of the encoded data.",
|
||||
"link": "https://blog.ethereum.org/2019/03/26/solidity-optimizer-and-abiencoderv2-bug/",
|
||||
"link": "https://blog.soliditylang.org/2019/03/26/solidity-optimizer-and-abiencoderv2-bug/",
|
||||
"introduced": "0.4.19",
|
||||
"fixed": "0.4.26",
|
||||
"severity": "low",
|
||||
@ -241,10 +269,11 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-2",
|
||||
"name": "IncorrectByteInstructionOptimization",
|
||||
"summary": "The optimizer incorrectly handles byte opcodes whose second argument is 31 or a constant expression that evaluates to 31. This can result in unexpected values.",
|
||||
"description": "The optimizer incorrectly handles byte opcodes that use the constant 31 as second argument. This can happen when performing index access on bytesNN types with a compile-time constant value (not index) of 31 or when using the byte opcode in inline assembly.",
|
||||
"link": "https://blog.ethereum.org/2019/03/26/solidity-optimizer-and-abiencoderv2-bug/",
|
||||
"link": "https://blog.soliditylang.org/2019/03/26/solidity-optimizer-and-abiencoderv2-bug/",
|
||||
"introduced": "0.5.5",
|
||||
"fixed": "0.5.7",
|
||||
"severity": "very low",
|
||||
@ -253,10 +282,11 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2019-1",
|
||||
"name": "DoubleShiftSizeOverflow",
|
||||
"summary": "Double bitwise shifts by large constants whose sum overflows 256 bits can result in unexpected values.",
|
||||
"description": "Nested logical shift operations whose total shift size is 2**256 or more are incorrectly optimized. This only applies to shifts by numbers of bits that are compile-time constant expressions.",
|
||||
"link": "https://blog.ethereum.org/2019/03/26/solidity-optimizer-and-abiencoderv2-bug/",
|
||||
"link": "https://blog.soliditylang.org/2019/03/26/solidity-optimizer-and-abiencoderv2-bug/",
|
||||
"introduced": "0.5.5",
|
||||
"fixed": "0.5.6",
|
||||
"severity": "low",
|
||||
@ -266,35 +296,39 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2018-4",
|
||||
"name": "ExpExponentCleanup",
|
||||
"summary": "Using the ** operator with an exponent of type shorter than 256 bits can result in unexpected values.",
|
||||
"description": "Higher order bits in the exponent are not properly cleaned before the EXP opcode is applied if the type of the exponent expression is smaller than 256 bits and not smaller than the type of the base. In that case, the result might be larger than expected if the exponent is assumed to lie within the value range of the type. Literal numbers as exponents are unaffected as are exponents or bases of type uint256.",
|
||||
"link": "https://blog.ethereum.org/2018/09/13/solidity-bugfix-release/",
|
||||
"link": "https://blog.soliditylang.org/2018/09/13/solidity-bugfix-release/",
|
||||
"fixed": "0.4.25",
|
||||
"severity": "medium/high",
|
||||
"check": {"regex-source": "[^/]\\*\\* *[^/0-9 ]"}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2018-3",
|
||||
"name": "EventStructWrongData",
|
||||
"summary": "Using structs in events logged wrong data.",
|
||||
"description": "If a struct is used in an event, the address of the struct is logged instead of the actual data.",
|
||||
"link": "https://blog.ethereum.org/2018/09/13/solidity-bugfix-release/",
|
||||
"link": "https://blog.soliditylang.org/2018/09/13/solidity-bugfix-release/",
|
||||
"introduced": "0.4.17",
|
||||
"fixed": "0.4.25",
|
||||
"severity": "very low",
|
||||
"check": {"ast-compact-json-path": "$..[?(@.nodeType === 'EventDefinition')]..[?(@.nodeType === 'UserDefinedTypeName' && @.typeDescriptions.typeString.startsWith('struct'))]"}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2018-2",
|
||||
"name": "NestedArrayFunctionCallDecoder",
|
||||
"summary": "Calling functions that return multi-dimensional fixed-size arrays can result in memory corruption.",
|
||||
"description": "If Solidity code calls a function that returns a multi-dimensional fixed-size array, array elements are incorrectly interpreted as memory pointers and thus can cause memory corruption if the return values are accessed. Calling functions with multi-dimensional fixed-size arrays is unaffected as is returning fixed-size arrays from function calls. The regular expression only checks if such functions are present, not if they are called, which is required for the contract to be affected.",
|
||||
"link": "https://blog.ethereum.org/2018/09/13/solidity-bugfix-release/",
|
||||
"link": "https://blog.soliditylang.org/2018/09/13/solidity-bugfix-release/",
|
||||
"introduced": "0.1.4",
|
||||
"fixed": "0.4.22",
|
||||
"severity": "medium",
|
||||
"check": {"regex-source": "returns[^;{]*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\]\\s*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\][^{;]*[;{]"}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2018-1",
|
||||
"name": "OneOfTwoConstructorsSkipped",
|
||||
"summary": "If a contract has both a new-style constructor (using the constructor keyword) and an old-style constructor (a function with the same name as the contract) at the same time, one of them will be ignored.",
|
||||
"description": "If a contract has both a new-style constructor (using the constructor keyword) and an old-style constructor (a function with the same name as the contract) at the same time, one of them will be ignored. There will be a compiler warning about the old-style constructor, so contracts only using new-style constructors are fine.",
|
||||
@ -303,6 +337,7 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2017-5",
|
||||
"name": "ZeroFunctionSelector",
|
||||
"summary": "It is possible to craft the name of a function such that it is executed instead of the fallback function in very specific circumstances.",
|
||||
"description": "If a function has a selector consisting only of zeros, is payable and part of a contract that does not have a fallback function and at most five external functions in total, this function is called instead of the fallback function if Ether is sent to the contract without data.",
|
||||
@ -310,6 +345,7 @@
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2017-4",
|
||||
"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 successful.",
|
||||
@ -318,6 +354,7 @@
|
||||
"severity": "low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2017-3",
|
||||
"name": "ECRecoverMalformedInput",
|
||||
"summary": "The ecrecover() builtin can return garbage for malformed input.",
|
||||
"description": "The ecrecover precompile does not properly signal failure for malformed input (especially in the 'v' argument) and thus the Solidity function can return data that was previously present in the return area in memory.",
|
||||
@ -325,6 +362,7 @@
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2017-2",
|
||||
"name": "SkipEmptyStringLiteral",
|
||||
"summary": "If \"\" is used in a function call, the following function arguments will not be correctly passed to the function.",
|
||||
"description": "If the empty string literal \"\" is used as an argument in a function call, it is skipped by the encoder. This has the effect that the encoding of all arguments following this is shifted left by 32 bytes and thus the function call data is corrupted.",
|
||||
@ -332,10 +370,11 @@
|
||||
"severity": "low"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2017-1",
|
||||
"name": "ConstantOptimizerSubtraction",
|
||||
"summary": "In some situations, the optimizer replaces certain numbers in the code with routines that compute different numbers.",
|
||||
"description": "The optimizer tries to represent any number in the bytecode by routines that compute them with less gas. For some special numbers, an incorrect routine is generated. This could allow an attacker to e.g. trick victims about a specific amount of ether, or function calls to call different functions (or none at all).",
|
||||
"link": "https://blog.ethereum.org/2017/05/03/solidity-optimizer-bug/",
|
||||
"link": "https://blog.soliditylang.org/2017/05/03/solidity-optimizer-bug/",
|
||||
"fixed": "0.4.11",
|
||||
"severity": "low",
|
||||
"conditions": {
|
||||
@ -343,6 +382,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-11",
|
||||
"name": "IdentityPrecompileReturnIgnored",
|
||||
"summary": "Failure of the identity precompile was ignored.",
|
||||
"description": "Calls to the identity contract, which is used for copying memory, ignored its return value. On the public chain, calls to the identity precompile can be made in a way that they never fail, but this might be different on private chains.",
|
||||
@ -350,6 +390,7 @@
|
||||
"fixed": "0.4.7"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-10",
|
||||
"name": "OptimizerStateKnowledgeNotResetForJumpdest",
|
||||
"summary": "The optimizer did not properly reset its internal state at jump destinations, which could lead to data corruption.",
|
||||
"description": "The optimizer performs symbolic execution at certain stages. At jump destinations, multiple code paths join and thus it has to compute a common state from the incoming edges. Computing this common state was simplified to just use the empty state, but this implementation was not done properly. This bug can cause data corruption.",
|
||||
@ -361,15 +402,17 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-9",
|
||||
"name": "HighOrderByteCleanStorage",
|
||||
"summary": "For short types, the high order bytes were not cleaned properly and could overwrite existing data.",
|
||||
"description": "Types shorter than 32 bytes are packed together into the same 32 byte storage slot, but storage writes always write 32 bytes. For some types, the higher order bytes were not cleaned properly, which made it sometimes possible to overwrite a variable in storage when writing to another one.",
|
||||
"link": "https://blog.ethereum.org/2016/11/01/security-alert-solidity-variables-can-overwritten-storage/",
|
||||
"link": "https://blog.soliditylang.org/2016/11/01/security-alert-solidity-variables-can-overwritten-storage/",
|
||||
"severity": "high",
|
||||
"introduced": "0.1.6",
|
||||
"fixed": "0.4.4"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-8",
|
||||
"name": "OptimizerStaleKnowledgeAboutSHA3",
|
||||
"summary": "The optimizer did not properly reset its knowledge about SHA3 operations resulting in some hashes (also used for storage variable positions) not being calculated correctly.",
|
||||
"description": "The optimizer performs symbolic execution in order to save re-evaluating expressions whose value is already known. This knowledge was not properly reset across control flow paths and thus the optimizer sometimes thought that the result of a SHA3 operation is already present on the stack. This could result in data corruption by accessing the wrong storage slot.",
|
||||
@ -380,6 +423,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-7",
|
||||
"name": "LibrariesNotCallableFromPayableFunctions",
|
||||
"summary": "Library functions threw an exception when called from a call that received Ether.",
|
||||
"description": "Library functions are protected against sending them Ether through a call. Since the DELEGATECALL opcode forwards the information about how much Ether was sent with a call, the library function incorrectly assumed that Ether was sent to the library and threw an exception.",
|
||||
@ -388,6 +432,7 @@
|
||||
"fixed": "0.4.2"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-6",
|
||||
"name": "SendFailsForZeroEther",
|
||||
"summary": "The send function did not provide enough gas to the recipient if no Ether was sent with it.",
|
||||
"description": "The recipient of an Ether transfer automatically receives a certain amount of gas from the EVM to handle the transfer. In the case of a zero-transfer, this gas is not provided which causes the recipient to throw an exception.",
|
||||
@ -395,6 +440,7 @@
|
||||
"fixed": "0.4.0"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-5",
|
||||
"name": "DynamicAllocationInfiniteLoop",
|
||||
"summary": "Dynamic allocation of an empty memory array caused an infinite loop and thus an exception.",
|
||||
"description": "Memory arrays can be created provided a length. If this length is zero, code was generated that did not terminate and thus consumed all gas.",
|
||||
@ -402,6 +448,7 @@
|
||||
"fixed": "0.3.6"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-4",
|
||||
"name": "OptimizerClearStateOnCodePathJoin",
|
||||
"summary": "The optimizer did not properly reset its internal state at jump destinations, which could lead to data corruption.",
|
||||
"description": "The optimizer performs symbolic execution at certain stages. At jump destinations, multiple code paths join and thus it has to compute a common state from the incoming edges. Computing this common state was not done correctly. This bug can cause data corruption, but it is probably quite hard to use for targeted attacks.",
|
||||
@ -412,6 +459,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-3",
|
||||
"name": "CleanBytesHigherOrderBits",
|
||||
"summary": "The higher order bits of short bytesNN types were not cleaned before comparison.",
|
||||
"description": "Two variables of type bytesNN were considered different if their higher order bits, which are not part of the actual value, were different. An attacker might use this to reach seemingly unreachable code paths by providing incorrectly formatted input data.",
|
||||
@ -419,6 +467,7 @@
|
||||
"fixed": "0.3.3"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-2",
|
||||
"name": "ArrayAccessCleanHigherOrderBits",
|
||||
"summary": "Access to array elements for arrays of types with less than 32 bytes did not correctly clean the higher order bits, causing corruption in other array elements.",
|
||||
"description": "Multiple elements of an array of values that are shorter than 17 bytes are packed into the same storage slot. Writing to a single element of such an array did not properly clean the higher order bytes and thus could lead to data corruption.",
|
||||
@ -426,6 +475,7 @@
|
||||
"fixed": "0.3.1"
|
||||
},
|
||||
{
|
||||
"uid": "SOL-2016-1",
|
||||
"name": "AncientCompiler",
|
||||
"summary": "This compiler version is ancient and might contain several undocumented or undiscovered bugs.",
|
||||
"description": "The list of bugs is only kept for compiler versions starting from 0.3.0, so older versions might contain undocumented bugs.",
|
||||
|
@ -33,6 +33,10 @@ contracts should consult this list according to the following criteria:
|
||||
The JSON file of known bugs below is an array of objects, one for each bug,
|
||||
with the following keys:
|
||||
|
||||
uid
|
||||
Unique identifier given to the bug in the form of ``SOL-<year>-<number>``.
|
||||
It is possible that multiple entries exists with the same uid. This means
|
||||
multiple version ranges are affected by the same bug.
|
||||
name
|
||||
Unique name given to the bug
|
||||
summary
|
||||
|
@ -1543,5 +1543,13 @@
|
||||
"0.8.4": {
|
||||
"bugs": [],
|
||||
"released": "2021-04-21"
|
||||
},
|
||||
"0.8.5": {
|
||||
"bugs": [],
|
||||
"released": "2021-06-10"
|
||||
},
|
||||
"0.8.6": {
|
||||
"bugs": [],
|
||||
"released": "2021-06-22"
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ contract in order to become the "richest", inspired by
|
||||
In the following contract, if you are no longer the richest,
|
||||
you receive the funds of the person who is now the richest.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -63,7 +63,7 @@ you receive the funds of the person who is now the richest.
|
||||
|
||||
This is as opposed to the more intuitive sending pattern:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -129,7 +129,7 @@ functions and this is what this section is about.
|
||||
The use of **function modifiers** makes these
|
||||
restrictions highly readable.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -292,7 +292,7 @@ function finishes.
|
||||
Starting with version 0.4.0, modifier code
|
||||
will run even if the function explicitly returns.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
@ -26,10 +26,10 @@ from pygments_lexer_solidity import SolidityLexer, YulLexer
|
||||
def setup(sphinx):
|
||||
thisdir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, thisdir + '/utils')
|
||||
sphinx.add_lexer('Solidity', SolidityLexer())
|
||||
sphinx.add_lexer('Yul', YulLexer())
|
||||
sphinx.add_lexer('Solidity', SolidityLexer)
|
||||
sphinx.add_lexer('Yul', YulLexer)
|
||||
|
||||
sphinx.add_stylesheet('css/custom.css')
|
||||
sphinx.add_css_file('css/custom.css')
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
|
@ -11,7 +11,9 @@ Contracts may be marked as abstract even though all functions are implemented.
|
||||
|
||||
This can be done by using the ``abstract`` keyword as shown in the following example. Note that this contract needs to be
|
||||
defined as abstract, because the function ``utterance()`` was defined, but no implementation was
|
||||
provided (no implementation body ``{ }`` was given).::
|
||||
provided (no implementation body ``{ }`` was given).
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -21,7 +23,9 @@ provided (no implementation body ``{ }`` was given).::
|
||||
}
|
||||
|
||||
Such abstract contracts can not be instantiated directly. This is also true, if an abstract contract itself does implement
|
||||
all defined functions. The usage of an abstract contract as a base class is shown in the following example::
|
||||
all defined functions. The usage of an abstract contract as a base class is shown in the following example:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -40,11 +44,15 @@ functions by overriding, it needs to be marked as abstract as well.
|
||||
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)::
|
||||
Example of function without implementation (a function declaration):
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function foo(address) external returns (address);
|
||||
|
||||
Example of a declaration of a variable whose type is a function type::
|
||||
Example of a declaration of a variable whose type is a function type:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function(address) external returns (address) foo;
|
||||
|
||||
|
@ -27,7 +27,7 @@ can sometimes be cheaper than immutable values.
|
||||
Not all types for constants and immutables are implemented at this time. The only supported types are
|
||||
:ref:`strings <strings>` (only for constants) and :ref:`value types <value-types>`.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.4;
|
||||
|
@ -32,7 +32,7 @@ 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.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
|
@ -14,7 +14,7 @@ which causes
|
||||
all changes in the current call to be reverted and passes the error data back to the
|
||||
caller.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -79,4 +79,4 @@ of the built-in type ``Panic(uint256)``.
|
||||
of inner calls is propagated back through the chain of external calls
|
||||
by default. This means that an inner call
|
||||
can "forge" revert data that looks like it could have come from the
|
||||
contract that called it.
|
||||
contract that called it.
|
||||
|
@ -24,9 +24,10 @@ 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).
|
||||
the data part of the log.
|
||||
A topic can only hold a single word (32 bytes) so if you use a :ref:`reference type
|
||||
<reference-types>` for an indexed argument, the Keccak-256 hash of the value is stored
|
||||
as a topic instead.
|
||||
|
||||
All parameters without the ``indexed`` attribute are :ref:`ABI-encoded <ABI>`
|
||||
into the data part of the log.
|
||||
@ -61,7 +62,16 @@ 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, you can
|
||||
only filter by the contract address. The advantage of anonymous events
|
||||
is that they are cheaper to deploy and call.
|
||||
is that they are cheaper to deploy and call. It also allows you to declare
|
||||
four indexed arguments rather than three.
|
||||
|
||||
.. note::
|
||||
Since the transaction log only stores the event data and not the type,
|
||||
you have to know the type of the event, including which parameter is
|
||||
indexed and if the event is anonymous in order to correctly interpret
|
||||
the data.
|
||||
In particular, it is possible to "fake" the signature of another event
|
||||
using an anonymous event.
|
||||
|
||||
::
|
||||
|
||||
|
@ -15,7 +15,7 @@ inheritable properties of contracts and may be overridden by derived contracts,
|
||||
if they are marked ``virtual``. For details, please see
|
||||
:ref:`Modifier Overriding <modifier-overriding>`.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0 <0.9.0;
|
||||
|
@ -12,7 +12,7 @@ Functions outside of a contract, also called "free functions", always have impli
|
||||
:ref:`visibility<visibility-and-getters>`. Their code is included in all contracts
|
||||
that call them, similar to internal library functions.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0 <0.9.0;
|
||||
@ -56,7 +56,9 @@ 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 the following::
|
||||
with two integers, you would use something like the following:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -89,7 +91,9 @@ 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::
|
||||
two integers passed as function parameters, then you use something like:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -114,7 +118,9 @@ You can either explicitly assign to return variables and
|
||||
then leave the function as above,
|
||||
or you can provide return values
|
||||
(either a single or :ref:`multiple ones<multi-return>`) directly with the ``return``
|
||||
statement::
|
||||
statement:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -178,7 +184,7 @@ The following statements are considered modifying the state:
|
||||
#. Using low-level calls.
|
||||
#. Using inline assembly that contains certain opcodes.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -224,7 +230,7 @@ In addition to the list of state modifying statements explained above, the follo
|
||||
#. Calling any function not marked ``pure``.
|
||||
#. Using inline assembly that contains certain opcodes.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -321,7 +327,7 @@ will consume more gas than the 2300 gas stipend:
|
||||
|
||||
Below you can see an example of a Sink contract that uses function ``receive``.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -383,7 +389,7 @@ operations as long as there is enough gas passed on to it.
|
||||
proper functions should be used instead.
|
||||
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
@ -461,7 +467,7 @@ 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``.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -480,7 +486,7 @@ The following example shows overloading of the function
|
||||
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.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -514,7 +520,7 @@ candidate, resolution fails.
|
||||
.. note::
|
||||
Return parameters are not taken into account for overload resolution.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
|
@ -36,7 +36,7 @@ some :ref:`differences <multi-inheritance>`.
|
||||
|
||||
Details are given in the following example.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -124,7 +124,9 @@ Details are given in the following example.
|
||||
|
||||
Note that above, we call ``Destructible.destroy()`` to "forward" the
|
||||
destruction request. The way this is done is problematic, as
|
||||
seen in the following example::
|
||||
seen in the following example:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -154,7 +156,9 @@ seen in the following example::
|
||||
|
||||
A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it
|
||||
explicitly in the final override, but this function will bypass
|
||||
``Base1.destroy``. The way around this is to use ``super``::
|
||||
``Base1.destroy``. The way around this is to use ``super``:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -211,7 +215,7 @@ The mutability may be changed to a more strict one following the order:
|
||||
|
||||
The following example demonstrates changing mutability and visibility:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -235,7 +239,7 @@ and have not yet been overridden by another base contract (on some path through
|
||||
Additionally, if a contract inherits the same function from multiple (unrelated)
|
||||
bases, it has to explicitly override it:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -262,7 +266,7 @@ the function is defined in a common base contract
|
||||
or if there is a unique function in a common base contract
|
||||
that already overrides all other functions.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -303,7 +307,7 @@ Public state variables can override external functions if the
|
||||
parameter and return types of the function matches the getter function
|
||||
of the variable:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -335,7 +339,7 @@ Function modifiers can override each other. This works in the same way as
|
||||
``virtual`` keyword must be used on the overridden modifier
|
||||
and the ``override`` keyword must be used in the overriding modifier:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -354,7 +358,7 @@ and the ``override`` keyword must be used in the overriding modifier:
|
||||
In case of multiple inheritance, all direct base contracts must be specified
|
||||
explicitly:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -402,7 +406,7 @@ If there is no
|
||||
constructor, the contract will assume the default constructor, which is
|
||||
equivalent to ``constructor() {}``. For example:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -439,7 +443,9 @@ 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::
|
||||
derived contracts need to specify all of them. This can be done in two ways:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -499,7 +505,7 @@ stopping at the first match. If a base contract has already been searched, it is
|
||||
In the following code, Solidity will give the
|
||||
error "Linearization of inheritance graph impossible".
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -520,7 +526,7 @@ C3 linearization is not too important in practice.
|
||||
|
||||
One area where inheritance linearization is especially important and perhaps not as clear is when there are multiple constructors in the inheritance hierarchy. The constructors will always be executed in the linearized order, regardless of the order in which their arguments are provided in the inheriting contract's constructor. For example:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
@ -20,7 +20,7 @@ an interface should be possible without any information loss.
|
||||
|
||||
Interfaces are denoted by their own keyword:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
@ -41,7 +41,7 @@ function is marked ``virtual``.
|
||||
Interfaces can inherit from other interfaces. This has the same rules as normal
|
||||
inheritance.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
@ -50,7 +50,7 @@ The following example illustrates how to use libraries (but using a manual metho
|
||||
be sure to check out :ref:`using for <using-for>` for a
|
||||
more advanced example to implement a set).
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -129,7 +129,7 @@ The following example shows how to use :ref:`types stored in memory <data-locati
|
||||
internal functions in libraries in order to implement
|
||||
custom types without the overhead of external function calls:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.8 <0.9.0;
|
||||
@ -238,7 +238,7 @@ The argument encoding is the same as for the regular contract ABI, except for st
|
||||
Similarly to the contract ABI, the selector consists of the first four bytes of the Keccak256-hash of the signature.
|
||||
Its value can be obtained from Solidity using the ``.selector`` member as follows:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.14 <0.9.0;
|
||||
|
@ -27,7 +27,9 @@ outside of the contract in which it is used. The directive
|
||||
may only be used inside a contract, not inside any of its functions.
|
||||
|
||||
Let us rewrite the set example from the
|
||||
:ref:`libraries` in this way::
|
||||
:ref:`libraries` in this way:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -80,7 +82,9 @@ Let us rewrite the set example from the
|
||||
}
|
||||
}
|
||||
|
||||
It is also possible to extend elementary types in that way::
|
||||
It is also possible to extend elementary types in that way:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.8 <0.9.0;
|
||||
|
@ -50,7 +50,7 @@ The visibility specifier is given after the type for
|
||||
state variables and between parameter list and
|
||||
return parameter list for functions.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -65,7 +65,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
|
||||
``data`` in state storage, but is not able to call ``f``. Contract ``E`` is derived from
|
||||
``C`` and, thus, can call ``compute``.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -110,7 +110,7 @@ arguments and returns a ``uint``, the value of the state
|
||||
variable ``data``. State variables can be initialized
|
||||
when they are declared.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -131,7 +131,7 @@ 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.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -148,10 +148,10 @@ If you have a ``public`` state variable of array type, then you can only retriev
|
||||
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
|
||||
``myArray(0)``. If you want to return an entire array in one call, then you need
|
||||
to write a function, for example:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -178,7 +178,7 @@ Now you can use ``getArray()`` to retrieve the entire array, instead of
|
||||
|
||||
The next example is more complex:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -195,7 +195,7 @@ The next example is more complex:
|
||||
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:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) {
|
||||
a = data[arg1][arg2][arg3].a;
|
||||
|
@ -112,7 +112,7 @@ starting from the current directory. The required file is called ``libevmone.so`
|
||||
``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that
|
||||
use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``,
|
||||
``libsolidity/SolidityEndToEndTest``, part of the soltest suite. To run all tests, download the library from
|
||||
`GitHub <https://github.com/ethereum/evmone/releases/tag/v0.4.1>`_
|
||||
`GitHub <https://github.com/ethereum/evmone/releases/tag/v0.7.0>`_
|
||||
and place it in the project root path or inside the ``deps`` folder.
|
||||
|
||||
If the ``libz3`` library is not installed on your system, you should disable the
|
||||
@ -149,7 +149,7 @@ See especially:
|
||||
|
||||
If you want to debug using GDB, make sure you build differently than the "usual".
|
||||
For example, you could run the following command in your ``build`` folder:
|
||||
::
|
||||
.. code-block:: bash
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
make
|
||||
@ -171,7 +171,7 @@ The test suite compiles and checks them against the given expectations.
|
||||
|
||||
For example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol``
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
contract test {
|
||||
uint256 variable;
|
||||
@ -190,7 +190,7 @@ In the above example, the state variable ``variable`` was declared twice, which
|
||||
The ``isoltest`` tool is used for these tests and you can find it under ``./build/test/tools/``. It is an interactive tool which allows
|
||||
editing of failing contracts using your preferred text editor. Let's try to break this test by removing the second declaration of ``variable``:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
contract test {
|
||||
uint256 variable;
|
||||
@ -200,7 +200,7 @@ editing of failing contracts using your preferred text editor. Let's try to brea
|
||||
|
||||
Running ``./build/test/isoltest`` again results in a test failure:
|
||||
|
||||
::
|
||||
.. code-block:: text
|
||||
|
||||
syntaxTests/double_stateVariable_declaration.sol: FAIL
|
||||
Contract:
|
||||
@ -228,7 +228,7 @@ All of these options apply to the current contract, expect ``quit`` which stops
|
||||
|
||||
Automatically updating the test above changes it to
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
contract test {
|
||||
uint256 variable;
|
||||
@ -237,7 +237,7 @@ Automatically updating the test above changes it to
|
||||
|
||||
and re-run the test. It now passes again:
|
||||
|
||||
::
|
||||
.. code-block:: text
|
||||
|
||||
Re-running test case...
|
||||
syntaxTests/double_stateVariable_declaration.sol: OK
|
||||
@ -263,7 +263,7 @@ We mainly use `AFL <https://lcamtuf.coredump.cx/afl/>`_ for fuzzing. You need to
|
||||
install the AFL packages from your repositories (afl, afl-clang) or build them manually.
|
||||
Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler:
|
||||
|
||||
::
|
||||
.. code-block:: bash
|
||||
|
||||
cd build
|
||||
# if needed
|
||||
@ -273,7 +273,7 @@ Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compile
|
||||
|
||||
At this stage you should be able to see a message similar to the following:
|
||||
|
||||
::
|
||||
.. code-block:: text
|
||||
|
||||
Scanning dependencies of target solfuzzer
|
||||
[ 98%] Building CXX object test/tools/CMakeFiles/solfuzzer.dir/fuzzer.cpp.o
|
||||
@ -284,7 +284,7 @@ At this stage you should be able to see a message similar to the following:
|
||||
|
||||
If the instrumentation messages did not appear, try switching the cmake flags pointing to AFL's clang binaries:
|
||||
|
||||
::
|
||||
.. code-block:: bash
|
||||
|
||||
# if previously failed
|
||||
make clean
|
||||
@ -293,7 +293,7 @@ If the instrumentation messages did not appear, try switching the cmake flags po
|
||||
|
||||
Otherwise, upon execution the fuzzer halts with an error saying binary is not instrumented:
|
||||
|
||||
::
|
||||
.. code-block:: text
|
||||
|
||||
afl-fuzz 2.52b by <lcamtuf@google.com>
|
||||
... (truncated messages)
|
||||
@ -317,7 +317,7 @@ Next, you need some example source files. This makes it much easier for the fuzz
|
||||
to find errors. You can either copy some files from the syntax tests or extract test files
|
||||
from the documentation or the other tests:
|
||||
|
||||
::
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir /tmp/test_cases
|
||||
cd /tmp/test_cases
|
||||
@ -334,7 +334,7 @@ that result in similar behaviour of the binary.
|
||||
|
||||
Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB):
|
||||
|
||||
::
|
||||
.. code-block:: bash
|
||||
|
||||
afl-fuzz -m 60 -i /tmp/test_cases -o /tmp/fuzzer_reports -- /path/to/solfuzzer
|
||||
|
||||
@ -388,7 +388,8 @@ local slang and references, making your language as clear to all readers as poss
|
||||
.. note::
|
||||
|
||||
While the official Solidity documentation is written in English, there are community contributed :ref:`translations`
|
||||
in other languages available.
|
||||
in other languages available. Please refer to the `translation guide <https://github.com/solidity-docs/translation-guide>`_
|
||||
for information on how to contribute to the community translations.
|
||||
|
||||
Title Case for Headings
|
||||
-----------------------
|
||||
|
@ -39,11 +39,14 @@ Internal Function Calls
|
||||
-----------------------
|
||||
|
||||
Functions of the current contract can be called directly ("internally"), also recursively, as seen in
|
||||
this nonsensical example::
|
||||
this nonsensical example:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
|
||||
// This will report a warning
|
||||
contract C {
|
||||
function g(uint a) public pure returns (uint ret) { return a + f(); }
|
||||
function f() internal pure returns (uint ret) { return g(7) + f(); }
|
||||
@ -81,7 +84,7 @@ Note that it is discouraged to specify gas values explicitly, since the gas cost
|
||||
of opcodes can change in the future. Any Wei you send to the contract is added
|
||||
to the total balance of that contract:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
@ -143,7 +146,7 @@ if they are enclosed in ``{ }`` as can be seen in the following
|
||||
example. The argument list has to coincide by name with the list of
|
||||
parameters from the function declaration, but can be in arbitrary order.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -167,7 +170,7 @@ Omitted Function Parameter Names
|
||||
The names of unused parameters (especially return parameters) can be omitted.
|
||||
Those parameters will still be present on the stack, but they are inaccessible.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
@ -191,7 +194,7 @@ A contract can create other contracts using the ``new`` keyword. The full
|
||||
code of the contract being created has to be known when the creating contract
|
||||
is compiled so recursive creation-dependencies are not possible.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -246,7 +249,7 @@ contracts creates other contracts in the meantime.
|
||||
The main use-case here is contracts that act as judges for off-chain interactions,
|
||||
which only need to be created if there is a dispute.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -315,7 +318,7 @@ or to pre-existing variables (or LValues in general).
|
||||
Tuples are not proper types in Solidity, they can only be used to form syntactic
|
||||
groupings of expressions.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -361,7 +364,7 @@ In the example below the call to ``g(x)`` has no effect on ``x`` because it crea
|
||||
an independent copy of the storage value in memory. However, ``h(x)`` successfully modifies ``x``
|
||||
because only a reference and not a copy is passed.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
@ -420,7 +423,7 @@ use state variables before they are declared and call functions recursively.
|
||||
As a consequence, the following examples will compile without warnings, since
|
||||
the two variables have the same name but disjoint scopes.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -442,7 +445,7 @@ As a special example of the C99 scoping rules, note that in the following,
|
||||
the first assignment to ``x`` will actually assign the outer and not the inner variable.
|
||||
In any case, you will get a warning about the outer variable being shadowed.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -464,7 +467,7 @@ In any case, you will get a warning about the outer variable being shadowed.
|
||||
for the entire function, regardless where it was declared. The following example shows a code snippet that used
|
||||
to compile but leads to an error starting from version 0.5.0.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -496,7 +499,7 @@ thus making the use of these libraries unnecessary.
|
||||
|
||||
To obtain the previous behaviour, an ``unchecked`` block can be used:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.0;
|
||||
@ -598,6 +601,7 @@ function calls which will cause a Panic.
|
||||
A Panic exception is generated in the following situations.
|
||||
The error code supplied with the error data indicates the kind of panic.
|
||||
|
||||
#. 0x00: Used for generic compiler inserted panics.
|
||||
#. 0x01: If you call ``assert`` with an argument that evaluates to false.
|
||||
#. 0x11: If an arithmetic operation results in underflow or overflow outside of an ``unchecked { ... }`` block.
|
||||
#. 0x12; If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``).
|
||||
@ -655,7 +659,7 @@ You can optionally provide a message string for ``require``, but not for ``asser
|
||||
The following example shows how you can use ``require`` to check conditions on inputs
|
||||
and ``assert`` for internal error checking.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -719,7 +723,7 @@ any costs.
|
||||
The following example shows how to use an error string and a custom error instance
|
||||
together with ``revert`` and the equivalent ``require``:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -779,7 +783,7 @@ The provided message can be retrieved by the caller using ``try``/``catch`` as s
|
||||
|
||||
A failure in an external call can be caught using a try/catch statement, as follows:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
|
@ -22,7 +22,7 @@ raised, the previous highest bidder gets their 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.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -191,7 +191,7 @@ transfers): Bidders can confuse competition by placing several high or low
|
||||
invalid bids.
|
||||
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
@ -40,7 +40,7 @@ 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.
|
||||
|
||||
::
|
||||
.. code-block:: javascript
|
||||
|
||||
/// Hashing first makes things easier
|
||||
var hash = web3.utils.sha3("message to sign");
|
||||
@ -90,7 +90,7 @@ 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:
|
||||
|
||||
::
|
||||
.. code-block:: javascript
|
||||
|
||||
// recipient is the address that should be paid.
|
||||
// amount, in wei, specifies how much ether should be sent.
|
||||
@ -139,7 +139,8 @@ The functions ``prefixed`` and ``recoverSigner`` do this in the ``claimPayment``
|
||||
The full contract
|
||||
-----------------
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -271,7 +272,7 @@ to prevent a message intended for one payment channel from being used for a diff
|
||||
|
||||
Here is the modified JavaScript code to cryptographically sign a message from the previous section:
|
||||
|
||||
::
|
||||
.. code-block:: javascript
|
||||
|
||||
function constructPaymentMessage(contractAddress, amount) {
|
||||
return abi.soliditySHA3(
|
||||
@ -335,7 +336,8 @@ so it is important that Bob closes the channel before the expiration is reached.
|
||||
The full contract
|
||||
-----------------
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -452,9 +454,9 @@ The recipient should verify each message using the following process:
|
||||
|
||||
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:
|
||||
and we use JavaScript. The following code borrows the ``constructPaymentMessage`` function from the signing **JavaScript code** above:
|
||||
|
||||
::
|
||||
.. code-block:: javascript
|
||||
|
||||
// this mimics the prefixing behavior of the eth_sign JSON-RPC method.
|
||||
function prefixed(hash) {
|
||||
|
@ -17,7 +17,7 @@ provides an isolated component that properly tracks balances of accounts.
|
||||
It is easy to verify that the ``Balances`` library never produces negative balances or overflows
|
||||
and the sum of all balances is an invariant across the lifetime of the contract.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
|
@ -23,7 +23,7 @@ This contract of course does not solve the problem, but gives an overview of how
|
||||
you can use state machine-like constructs inside a contract.
|
||||
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
@ -30,7 +30,7 @@ At the end of the voting time, ``winningProposal()``
|
||||
will return the proposal with the largest number
|
||||
of votes.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
@ -91,6 +91,12 @@ Community volunteers help translate this documentation into several languages.
|
||||
They have varying degrees of completeness and up-to-dateness. The English
|
||||
version stands as a reference.
|
||||
|
||||
.. note::
|
||||
|
||||
We recently set up a new GitHub organization and translation workflow to help streamline the
|
||||
community efforts. Please refer to the `translation guide <https://github.com/solidity-docs/translation-guide>`_
|
||||
for information on how to contribute to the community translations moving forward.
|
||||
|
||||
* `French <https://solidity-fr.readthedocs.io>`_ (in progress)
|
||||
* `Italian <https://github.com/damianoazzolini/solidity>`_ (in progress)
|
||||
* `Japanese <https://solidity-jp.readthedocs.io>`_
|
||||
@ -143,7 +149,7 @@ Contents
|
||||
internals/layout_in_calldata.rst
|
||||
internals/variable_cleanup.rst
|
||||
internals/source_mappings.rst
|
||||
internals/optimiser.rst
|
||||
internals/optimizer.rst
|
||||
metadata.rst
|
||||
abi-spec.rst
|
||||
|
||||
@ -160,6 +166,7 @@ Contents
|
||||
security-considerations.rst
|
||||
smtchecker.rst
|
||||
resources.rst
|
||||
path-resolution.rst
|
||||
yul.rst
|
||||
style-guide.rst
|
||||
common-patterns.rst
|
||||
|
@ -86,7 +86,9 @@ local folder for input and output, and specify the contract to compile. For exam
|
||||
docker run -v /local/path:/sources ethereum/solc:stable -o /sources/output --abi --bin /sources/Contract.sol
|
||||
|
||||
You can also use the standard JSON interface (which is recommended when using the compiler with tooling).
|
||||
When using this interface it is not necessary to mount any directories.
|
||||
When using this interface it is not necessary to mount any directories as long as the JSON input is
|
||||
self-contained (i.e. it does not refer to any external files that would have to be
|
||||
:ref:`loaded by the import callback <initial-vfs-content-standard-json-with-import-callback>`).
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@ -49,7 +49,7 @@ Example for Difference in Arrays
|
||||
The following array occupies 32 bytes (1 slot) in storage, but 128
|
||||
bytes (4 items with 32 bytes each) in memory.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
uint8[4] a;
|
||||
|
||||
@ -62,7 +62,7 @@ The following struct occupies 96 bytes (3 slots of 32 bytes) in storage,
|
||||
but 128 bytes (4 items with 32 bytes each) in memory.
|
||||
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
struct S {
|
||||
uint a;
|
||||
|
@ -98,7 +98,7 @@ for example, you have to add an offset corresponding to the struct member to rea
|
||||
|
||||
As an example, consider the following contract:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -216,7 +216,7 @@ The following example shows a contract and its storage layout, containing
|
||||
value and reference types, types that are encoded packed, and nested types.
|
||||
|
||||
|
||||
.. code::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -238,12 +238,12 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
bytes b1;
|
||||
}
|
||||
|
||||
.. code::
|
||||
.. code:: json
|
||||
|
||||
"storageLayout": {
|
||||
{
|
||||
"storage": [
|
||||
{
|
||||
"astId": 14,
|
||||
"astId": 15,
|
||||
"contract": "fileA:A",
|
||||
"label": "x",
|
||||
"offset": 0,
|
||||
@ -251,7 +251,7 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"type": "t_uint256"
|
||||
},
|
||||
{
|
||||
"astId": 16,
|
||||
"astId": 17,
|
||||
"contract": "fileA:A",
|
||||
"label": "y",
|
||||
"offset": 0,
|
||||
@ -259,15 +259,15 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"type": "t_uint256"
|
||||
},
|
||||
{
|
||||
"astId": 18,
|
||||
"astId": 20,
|
||||
"contract": "fileA:A",
|
||||
"label": "s",
|
||||
"offset": 0,
|
||||
"slot": "2",
|
||||
"type": "t_struct(S)12_storage"
|
||||
"type": "t_struct(S)13_storage"
|
||||
},
|
||||
{
|
||||
"astId": 20,
|
||||
"astId": 22,
|
||||
"contract": "fileA:A",
|
||||
"label": "addr",
|
||||
"offset": 0,
|
||||
@ -275,7 +275,7 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"type": "t_address"
|
||||
},
|
||||
{
|
||||
"astId": 26,
|
||||
"astId": 28,
|
||||
"contract": "fileA:A",
|
||||
"label": "map",
|
||||
"offset": 0,
|
||||
@ -283,7 +283,7 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))"
|
||||
},
|
||||
{
|
||||
"astId": 29,
|
||||
"astId": 31,
|
||||
"contract": "fileA:A",
|
||||
"label": "array",
|
||||
"offset": 0,
|
||||
@ -291,7 +291,7 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"type": "t_array(t_uint256)dyn_storage"
|
||||
},
|
||||
{
|
||||
"astId": 31,
|
||||
"astId": 33,
|
||||
"contract": "fileA:A",
|
||||
"label": "s1",
|
||||
"offset": 0,
|
||||
@ -299,7 +299,7 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"type": "t_string_storage"
|
||||
},
|
||||
{
|
||||
"astId": 33,
|
||||
"astId": 35,
|
||||
"contract": "fileA:A",
|
||||
"label": "b1",
|
||||
"offset": 0,
|
||||
@ -354,12 +354,12 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"label": "string",
|
||||
"numberOfBytes": "32"
|
||||
},
|
||||
"t_struct(S)12_storage": {
|
||||
"t_struct(S)13_storage": {
|
||||
"encoding": "inplace",
|
||||
"label": "struct A.S",
|
||||
"members": [
|
||||
{
|
||||
"astId": 2,
|
||||
"astId": 3,
|
||||
"contract": "fileA:A",
|
||||
"label": "a",
|
||||
"offset": 0,
|
||||
@ -367,7 +367,7 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"type": "t_uint128"
|
||||
},
|
||||
{
|
||||
"astId": 4,
|
||||
"astId": 5,
|
||||
"contract": "fileA:A",
|
||||
"label": "b",
|
||||
"offset": 16,
|
||||
@ -375,7 +375,7 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"type": "t_uint128"
|
||||
},
|
||||
{
|
||||
"astId": 8,
|
||||
"astId": 9,
|
||||
"contract": "fileA:A",
|
||||
"label": "staticArray",
|
||||
"offset": 0,
|
||||
@ -383,7 +383,7 @@ value and reference types, types that are encoded packed, and nested types.
|
||||
"type": "t_array(t_uint256)2_storage"
|
||||
},
|
||||
{
|
||||
"astId": 11,
|
||||
"astId": 12,
|
||||
"contract": "fileA:A",
|
||||
"label": "dynArray",
|
||||
"offset": 0,
|
||||
|
@ -1,141 +0,0 @@
|
||||
.. index:: optimizer, common subexpression elimination, constant propagation
|
||||
|
||||
*************
|
||||
The Optimiser
|
||||
*************
|
||||
|
||||
This section discusses the optimiser that was first added to Solidity,
|
||||
which operates on opcode streams. For information on the new Yul-based optimiser,
|
||||
please see the `readme on github <https://github.com/ethereum/solidity/blob/develop/libyul/optimiser/README.md>`_.
|
||||
|
||||
The Solidity optimiser operates on assembly. It splits the sequence of instructions into basic blocks
|
||||
at ``JUMPs`` and ``JUMPDESTs``. Inside these blocks, the optimiser
|
||||
analyses the instructions and records every modification to the stack,
|
||||
memory, or storage as an expression which consists of an instruction and
|
||||
a list of arguments which are pointers to other expressions. The optimiser
|
||||
uses a component called "CommonSubexpressionEliminator" that amongst other
|
||||
tasks, finds expressions that are always equal (on every input) and combines
|
||||
them into an expression class. The optimiser first tries to find each new
|
||||
expression in a list of already known expressions. If this does not work,
|
||||
it simplifies the expression according to rules like
|
||||
``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is
|
||||
a recursive process, we can also apply the latter rule if the second factor
|
||||
is a more complex expression where we know that it always evaluates to one.
|
||||
Modifications to storage and memory locations have to erase knowledge about
|
||||
storage and memory locations which are not known to be different. If we first
|
||||
write to location x and then to location y and both are input variables, the
|
||||
second could overwrite the first, so we do not know what is stored at x after
|
||||
we wrote to y. If simplification of the expression x - y evaluates to a
|
||||
non-zero constant, we know that we can keep our knowledge about what is stored at x.
|
||||
|
||||
After this process, we know which expressions have to be on the stack at
|
||||
the end, and have a list of modifications to memory and storage. This information
|
||||
is stored together with the basic blocks and is used to link them. Furthermore,
|
||||
knowledge about the stack, storage and memory configuration is forwarded to
|
||||
the next block(s). If we know the targets of all ``JUMP`` and ``JUMPI`` instructions,
|
||||
we can build a complete control flow graph of the program. If there is only
|
||||
one target we do not know (this can happen as in principle, jump targets can
|
||||
be computed from inputs), we have to erase all knowledge about the input state
|
||||
of a block as it can be the target of the unknown ``JUMP``. If the optimiser
|
||||
finds a ``JUMPI`` whose condition evaluates to a constant, it transforms it
|
||||
to an unconditional jump.
|
||||
|
||||
As the last step, the code in each block is re-generated. The optimiser creates
|
||||
a dependency graph from the expressions on the stack at the end of the block,
|
||||
and it drops every operation that is not part of this graph. It generates code
|
||||
that applies the modifications to memory and storage in the order they were
|
||||
made in the original code (dropping modifications which were found not to be
|
||||
needed). Finally, it generates all values that are required to be on the
|
||||
stack in the correct place.
|
||||
|
||||
These steps are applied to each basic block and the newly generated code
|
||||
is used as replacement if it is smaller. If a basic block is split at a
|
||||
``JUMPI`` and during the analysis, the condition evaluates to a constant,
|
||||
the ``JUMPI`` is replaced depending on the value of the constant. Thus code like
|
||||
|
||||
::
|
||||
|
||||
uint x = 7;
|
||||
data[7] = 9;
|
||||
if (data[x] != x + 2)
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
|
||||
still simplifies to code which you can compile even though the instructions contained
|
||||
a jump in the beginning of the process:
|
||||
|
||||
::
|
||||
|
||||
data[7] = 9;
|
||||
return 1;
|
||||
|
||||
Simple Inlining
|
||||
---------------
|
||||
|
||||
Since Solidity version 0.8.2, there is another optimizer step that replaces certain
|
||||
jumps to blocks containing "simple" instructions ending with a "jump" by a copy of these instructions.
|
||||
This corresponds to inlining of simple, small Solidity or Yul functions. In particular, the sequence
|
||||
``PUSHTAG(tag) JUMP`` may be replaced, whenever the ``JUMP`` is marked as jump "into" a
|
||||
function and behind ``tag`` there is a basic block (as described above for the
|
||||
"CommonSubexpressionEliminator") that ends in another ``JUMP`` which is marked as a jump
|
||||
"out of" a function.
|
||||
In particular, consider the following prototypical example of assembly generated for a
|
||||
call to an internal Solidity function:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
tag_return
|
||||
tag_f
|
||||
jump // in
|
||||
tag_return:
|
||||
...opcodes after call to f...
|
||||
|
||||
tag_f:
|
||||
...body of function f...
|
||||
jump // out
|
||||
|
||||
As long as the body of the function is a continuous basic block, the "Inliner" can replace ``tag_f jump`` by
|
||||
the block at ``tag_f`` resulting in:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
tag_return
|
||||
...body of function f...
|
||||
jump
|
||||
tag_return:
|
||||
...opcodes after call to f...
|
||||
|
||||
tag_f:
|
||||
...body of function f...
|
||||
jump // out
|
||||
|
||||
Now ideally, the other optimiser steps described above will result in the return tag push being moved
|
||||
towards the remaining jump resulting in:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
...body of function f...
|
||||
tag_return
|
||||
jump
|
||||
tag_return:
|
||||
...opcodes after call to f...
|
||||
|
||||
tag_f:
|
||||
...body of function f...
|
||||
jump // out
|
||||
|
||||
In this situation the "PeepholeOptimizer" will remove the return jump. Ideally, all of this can be done
|
||||
for all references to ``tag_f`` leaving it unused, s.t. it can be removed, yielding:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
...body of function f...
|
||||
...opcodes after call to f...
|
||||
|
||||
So the call to function ``f`` is inlined and the original definition of ``f`` can be removed.
|
||||
|
||||
Inlining like this is attempted, whenever a heuristics suggests that inlining is cheaper over the lifetime of a
|
||||
contract than not inlining. This heuristics depends on the size of the function body, the
|
||||
number of other references to its tag (approximating the number of calls to the function) and
|
||||
the expected number of executions of the contract (the global optimiser parameter "runs").
|
1306
docs/internals/optimizer.rst
Normal file
1306
docs/internals/optimizer.rst
Normal file
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ everything right now, we will go into more detail later.
|
||||
Storage Example
|
||||
===============
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -51,8 +51,10 @@ code that manages the database. In this example, the contract defines the
|
||||
functions ``set`` and ``get`` that can be used to modify
|
||||
or retrieve the value of the variable.
|
||||
|
||||
To access a state variable, you do not need the prefix ``this.`` as is common in
|
||||
other languages.
|
||||
To access a member (like a state variable) of the current contract, you do not typically add the ``this.`` prefix,
|
||||
you just access it directly via its name.
|
||||
Unlike in some other languages, omitting it is not just a matter of style,
|
||||
it results in a completely different way to access the member, but more on this later.
|
||||
|
||||
This contract does not do much yet apart from (due to the infrastructure
|
||||
built by Ethereum) allowing anyone to store a single number that is accessible by
|
||||
@ -80,7 +82,7 @@ cryptocurrency. The contract allows only its creator to create new coins (differ
|
||||
Anyone can send coins to each other without a need for
|
||||
registering with a username and password, all you need is an Ethereum keypair.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -224,8 +226,8 @@ a failure can more easily be debugged or reacted upon.
|
||||
|
||||
The ``send`` function can be used by anyone (who already
|
||||
has some of these coins) to send coins to anyone else. If the sender does not have
|
||||
enough coins to send, the ``require`` call fails and provides the
|
||||
sender with an appropriate error message string.
|
||||
enough coins to send, the ``if`` condition evaluates to true. As a result, the ``revert`` will cause the operation to fail
|
||||
while providing the sender with error details using the ``InsufficientBalance`` error.
|
||||
|
||||
.. note::
|
||||
If you use
|
||||
|
@ -14,7 +14,8 @@ hiding new and different behavior in existing code.
|
||||
* When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched.
|
||||
Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past).
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0;
|
||||
|
||||
@ -42,7 +43,8 @@ We have the same behavior for implicit delete, for example when array of structs
|
||||
The new code generator implements modifiers using actual functions and passes function parameters on.
|
||||
This means that multiple executions of a function will get the same values for the parameters.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0;
|
||||
contract C {
|
||||
@ -71,7 +73,9 @@ New order:
|
||||
2. Constructor, if present.
|
||||
|
||||
This causes differences in some contracts, for example:
|
||||
::
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0;
|
||||
|
||||
@ -93,7 +97,9 @@ With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, the
|
||||
|
||||
* Copying ``bytes`` arrays from memory to storage is implemented in a different way. The old code generator always copies full words, while the new one cuts the byte array after its end. The old behaviour can lead to dirty data being copied after the end of the array (but still in the same storage slot).
|
||||
This causes differences in some contracts, for example:
|
||||
::
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
|
||||
@ -123,7 +129,8 @@ Now it is returning ``0x64656164626565660000000000000000000000000000000000000000
|
||||
|
||||
For example:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
contract C {
|
||||
@ -138,10 +145,11 @@ The function ``preincr_u8(1)`` returns the following values:
|
||||
|
||||
.. index:: ! evaluation order; function arguments
|
||||
|
||||
On the other hand, function argument expressions are evaluated in the same order by both code generators.
|
||||
On the other hand, function argument expressions are evaluated in the same order by both code generators with the exception of the global functions ``addmod`` and ``mulmod``.
|
||||
For example:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
contract C {
|
||||
@ -157,6 +165,27 @@ The function ``g(1, 2)`` returns the following values:
|
||||
- Old code generator: ``10`` (``add(2 + 3, 2 + 3)``) but the return value is unspecified in general
|
||||
- New code generator: ``10`` but the return value is not guaranteed
|
||||
|
||||
The arguments to the global functions ``addmod`` and ``mulmod`` are evaluated right-to-left by the old code generator
|
||||
and left-to-right by the new code generator.
|
||||
For example:
|
||||
|
||||
::
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
contract C {
|
||||
function f() public pure returns (uint256 aMod, uint256 mMod) {
|
||||
uint256 x = 3;
|
||||
// Old code gen: add/mulmod(5, 4, 3)
|
||||
// New code gen: add/mulmod(4, 5, 5)
|
||||
aMod = addmod(++x, ++x, x);
|
||||
mMod = mulmod(++x, ++x, x);
|
||||
}
|
||||
}
|
||||
|
||||
The function ``f()`` returns the following values:
|
||||
- Old code generator: ``aMod = 0`` and ``mMod = 2``
|
||||
- New code generator: ``aMod = 4`` and ``mMod = 0``
|
||||
|
||||
|
||||
Internals
|
||||
=========
|
||||
@ -188,7 +217,9 @@ The old code generator only performs cleanup before an operation whose result co
|
||||
The new code generator performs cleanup after any operation that can result in dirty bits.
|
||||
|
||||
For example:
|
||||
::
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
contract C {
|
||||
|
@ -163,7 +163,7 @@ The component does not yet support all features of the Solidity language and
|
||||
likely outputs many warnings. In case it reports unsupported features, the
|
||||
analysis may not be fully sound.
|
||||
|
||||
.. index:: source file, ! import, module
|
||||
.. index:: source file, ! import, module, source unit
|
||||
|
||||
.. _import:
|
||||
|
||||
@ -184,6 +184,7 @@ At a global level, you can use import statements of the following form:
|
||||
|
||||
import "filename";
|
||||
|
||||
The ``filename`` part is called an *import path*.
|
||||
This statement imports all global symbols from "filename" (and symbols imported there) into the
|
||||
current global scope (different than in ES6 but backwards-compatible for Solidity).
|
||||
This form is not recommended for use, because it unpredictably pollutes the namespace.
|
||||
@ -216,101 +217,34 @@ the code below creates new global symbols ``alias`` and ``symbol2`` which refere
|
||||
|
||||
import {symbol1 as alias, symbol2} from "filename";
|
||||
|
||||
Paths
|
||||
-----
|
||||
.. index:: virtual filesystem, source unit name, import; path, filesystem path, import callback, Remix IDE
|
||||
|
||||
In the above, ``filename`` is always treated as a path with ``/`` as directory separator,
|
||||
and ``.`` as the current and ``..`` as the parent directory. When ``.`` or ``..`` is followed by a character except ``/``,
|
||||
it is not considered as the current or the parent directory.
|
||||
All path names are treated as absolute paths unless they start with the current ``.`` or the parent directory ``..``.
|
||||
Import Paths
|
||||
------------
|
||||
|
||||
To import a file ``filename`` from the same directory as the current file, use ``import "./filename" as symbolName;``.
|
||||
If you use ``import "filename" as symbolName;`` instead, a different file could be referenced
|
||||
(in a global "include directory").
|
||||
In order to be able to support reproducible builds on all platforms, the Solidity compiler has to
|
||||
abstract away the details of the filesystem where source files are stored.
|
||||
For this reason import paths do not refer directly to files in the host filesystem.
|
||||
Instead the compiler maintains an internal database (*virtual filesystem* or *VFS* for short) where
|
||||
each source unit is assigned a unique *source unit name* which is an opaque and unstructured identifier.
|
||||
The import path specified in an import statement is translated into a source unit name and used to
|
||||
find the corresponding source unit in this database.
|
||||
|
||||
It depends on the compiler (see :ref:`import-compiler`) how to actually resolve the paths.
|
||||
In general, the directory hierarchy does not need to strictly map onto your local
|
||||
filesystem, and the path can also map to resources such as ipfs, http or git.
|
||||
Using the :ref:`Standard JSON <compiler-api>` API it is possible to directly provide the names and
|
||||
content of all the source files as a part of the compiler input.
|
||||
In this case source unit names are truly arbitrary.
|
||||
If, however, you want the compiler to automatically find and load source code into the VFS, your
|
||||
source unit names need to be structured in a way that makes it possible for an :ref:`import callback
|
||||
<import-callback>` to locate them.
|
||||
When using the command-line compiler the default import callback supports only loading source code
|
||||
from the host filesystem, which means that your source unit names must be paths.
|
||||
Some environments provide custom callbacks that are more versatile.
|
||||
For example the `Remix IDE <https://remix.ethereum.org/>`_ provides one that
|
||||
lets you `import files from HTTP, IPFS and Swarm URLs or refer directly to packages in NPM registry
|
||||
<https://remix-ide.readthedocs.io/en/latest/import.html>`_.
|
||||
|
||||
.. note::
|
||||
Always use relative imports like ``import "./filename.sol";`` and avoid
|
||||
using ``..`` in path specifiers. In the latter case, it is probably better to use
|
||||
global paths and set up remappings as explained below.
|
||||
|
||||
.. _import-compiler:
|
||||
|
||||
Use in Actual Compilers
|
||||
-----------------------
|
||||
|
||||
When invoking the compiler, you can specify how to discover the first element
|
||||
of a path, and also path prefix remappings. For
|
||||
example you can setup a remapping so that everything imported from the virtual
|
||||
directory ``github.com/ethereum/dapp-bin/library`` would actually be read from
|
||||
your local directory ``/usr/local/dapp-bin/library``.
|
||||
If multiple remappings apply, the one with the longest key is tried first.
|
||||
An empty prefix is not allowed. The remappings can depend on a context,
|
||||
which allows you to configure packages to import e.g., different versions of a
|
||||
library of the same name.
|
||||
|
||||
**solc**:
|
||||
|
||||
For solc (the commandline compiler), you provide these path remappings as
|
||||
``context:prefix=target`` arguments, where both the ``context:`` and the
|
||||
``=target`` parts are optional (``target`` defaults to ``prefix`` in this
|
||||
case). All remapping values that are regular files are compiled (including
|
||||
their dependencies).
|
||||
|
||||
This mechanism is backwards-compatible (as long
|
||||
as no filename contains ``=`` or ``:``) and thus not a breaking change. All
|
||||
files in or below the ``context`` directory that import a file that starts with
|
||||
``prefix`` are redirected by replacing ``prefix`` by ``target``.
|
||||
|
||||
For example, if you clone ``github.com/ethereum/dapp-bin/`` locally to
|
||||
``/usr/local/dapp-bin``, you can use the following in your source file:
|
||||
|
||||
::
|
||||
|
||||
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
|
||||
|
||||
Then run the compiler:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol
|
||||
|
||||
As a more complex example, suppose you rely on a module that uses an old
|
||||
version of dapp-bin that you checked out to ``/usr/local/dapp-bin_old``, then you can run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \
|
||||
module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \
|
||||
source.sol
|
||||
|
||||
This means that all imports in ``module2`` point to the old version but imports
|
||||
in ``module1`` point to the new version.
|
||||
|
||||
.. note::
|
||||
|
||||
``solc`` only allows you to include files from certain directories. They have
|
||||
to be in the directory (or subdirectory) of one of the explicitly specified
|
||||
source files or in the directory (or subdirectory) of a remapping target. If
|
||||
you want to allow direct absolute includes, add the remapping ``/=/``.
|
||||
|
||||
If there are multiple remappings that lead to a valid file, the remapping
|
||||
with the longest common prefix is chosen.
|
||||
|
||||
**Remix**:
|
||||
|
||||
`Remix <https://remix.ethereum.org/>`_ provides an automatic remapping for
|
||||
GitHub and automatically retrieves the file over the network. You can import
|
||||
the iterable mapping as above, e.g.
|
||||
|
||||
::
|
||||
|
||||
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
|
||||
|
||||
Remix may add other source code providers in the future.
|
||||
For a complete description of the virtual filesystem and the path resolution logic used by the
|
||||
compiler see :ref:`Path Resolution <path-resolution>`.
|
||||
|
||||
.. index:: ! comment, natspec
|
||||
|
||||
|
@ -72,7 +72,7 @@ The following example shows a contract and a function using all available tags.
|
||||
/// @notice Calculate tree age in years, rounded up, for live trees
|
||||
/// @dev The Alexandr N. Tetearing algorithm could increase precision
|
||||
/// @param rings The number of rings from dendrochronological sample
|
||||
/// @return age in years, rounded up for partial years
|
||||
/// @return Age in years, rounded up for partial years
|
||||
function age(uint256 rings) external virtual pure returns (uint256) {
|
||||
return rings + 1;
|
||||
}
|
||||
@ -115,10 +115,10 @@ in the same way as if it were tagged with ``@notice``.
|
||||
=============== ====================================================================================== =============================
|
||||
Tag Context
|
||||
=============== ====================================================================================== =============================
|
||||
``@title`` A title that should describe the contract/interface contract, interface
|
||||
``@author`` The name of the author contract, interface
|
||||
``@notice`` Explain to an end user what this does contract, interface, function, public state variable, event
|
||||
``@dev`` Explain to a developer any extra details contract, interface, function, state variable, event
|
||||
``@title`` A title that should describe the contract/interface contract, library, interface
|
||||
``@author`` The name of the author contract, library, interface
|
||||
``@notice`` Explain to an end user what this does contract, library, interface, function, public state variable, event
|
||||
``@dev`` Explain to a developer any extra details contract, library, interface, function, state variable, event
|
||||
``@param`` Documents a parameter just like in Doxygen (must be followed by parameter name) function, event
|
||||
``@return`` Documents the return variables of a contract's function function, public state variable
|
||||
``@inheritdoc`` Copies all missing tags from the base function (must be followed by the contract name) function, public state variable
|
||||
|
486
docs/path-resolution.rst
Normal file
486
docs/path-resolution.rst
Normal file
@ -0,0 +1,486 @@
|
||||
.. _path-resolution:
|
||||
|
||||
**********************
|
||||
Import Path Resolution
|
||||
**********************
|
||||
|
||||
In order to be able to support reproducible builds on all platforms, the Solidity compiler has to
|
||||
abstract away the details of the filesystem where source files are stored.
|
||||
Paths used in imports must work the same way everywhere while the command-line interface must be
|
||||
able to work with platform-specific paths to provide good user experience.
|
||||
This section aims to explain in detail how Solidity reconciles these requirements.
|
||||
|
||||
.. index:: ! virtual filesystem, ! VFS, ! source unit name
|
||||
.. _virtual-filesystem:
|
||||
|
||||
Virtual Filesystem
|
||||
==================
|
||||
|
||||
The compiler maintains an internal database (*virtual filesystem* or *VFS* for short) where each
|
||||
source unit is assigned a unique *source unit name* which is an opaque and unstructured identifier.
|
||||
When you use the :ref:`import statement <import>`, you specify an *import path* that references a
|
||||
source unit name.
|
||||
|
||||
.. index:: ! import callback, ! Host Filesystem Loader
|
||||
.. _import-callback:
|
||||
|
||||
Import Callback
|
||||
---------------
|
||||
|
||||
The VFS is initially populated only with files the compiler has received as input.
|
||||
Additional files can be loaded during compilation using an *import callback*, which is different
|
||||
depending on the type of compiler you use (see below).
|
||||
If the compiler does not find any source unit name matching the import path in the VFS, it invokes
|
||||
the callback, which is responsible for obtaining the source code to be placed under that name.
|
||||
An import callback is free to interpret source unit names in an arbitrary way, not just as paths.
|
||||
If there is no callback available when one is needed or if it fails to locate the source code,
|
||||
compilation fails.
|
||||
|
||||
The command-line compiler provides the *Host Filesystem Loader* - a rudimentary callback
|
||||
that interprets a source unit name as a path in the local filesystem.
|
||||
The `JavaScript interface <https://github.com/ethereum/solc-js>`_ does not provide any by default,
|
||||
but one can be provided by the user.
|
||||
This mechanism can be used to obtain source code from locations other then the local filesystem
|
||||
(which may not even be accessible, e.g. when the compiler is running in a browser).
|
||||
For example the `Remix IDE <https://remix.ethereum.org/>`_ provides a versatile callback that
|
||||
lets you `import files from HTTP, IPFS and Swarm URLs or refer directly to packages in NPM registry
|
||||
<https://remix-ide.readthedocs.io/en/latest/import.html>`_.
|
||||
|
||||
.. note::
|
||||
|
||||
Host Filesystem Loader's file lookup is platform-dependent.
|
||||
For example backslashes in a source unit name can be interpreted as directory separators or not
|
||||
and the lookup can be case-sensitive or not, depending on the underlying platform.
|
||||
|
||||
For portability it is recommended to avoid using import paths that will work correctly only
|
||||
with a specific import callback or only on one platform.
|
||||
For example you should always use forward slashes since they work as path separators also on
|
||||
platforms that support backslashes.
|
||||
|
||||
Initial Content of the Virtual Filesystem
|
||||
-----------------------------------------
|
||||
|
||||
The initial content of the VFS depends on how you invoke the compiler:
|
||||
|
||||
#. **solc / command-line interface**
|
||||
|
||||
When you compile a file using the command-line interface of the compiler, you provide one or
|
||||
more paths to files containing Solidity code:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc contract.sol /usr/local/dapp-bin/token.sol
|
||||
|
||||
The source unit name of a file loaded this way is simply the specified path after shell expansion
|
||||
and with platform-specific separators converted to forward slashes.
|
||||
|
||||
.. index:: standard JSON
|
||||
|
||||
#. **Standard JSON**
|
||||
|
||||
When using the :ref:`Standard JSON <compiler-api>` API (via either the `JavaScript interface
|
||||
<https://github.com/ethereum/solc-js>`_ or the ``--standard-json`` command-line option)
|
||||
you provide input in JSON format, containing, among other things, the content of all your source
|
||||
files:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources": {
|
||||
"contract.sol": {
|
||||
"content": "import \"./util.sol\";\ncontract C {}"
|
||||
},
|
||||
"util.sol": {
|
||||
"content": "library Util {}"
|
||||
},
|
||||
"/usr/local/dapp-bin/token.sol": {
|
||||
"content": "contract Token {}"
|
||||
}
|
||||
},
|
||||
"settings": {"outputSelection": {"*": { "*": ["metadata", "evm.bytecode"]}}}
|
||||
}
|
||||
|
||||
The ``sources`` dictionary becomes the initial content of the virtual filesystem and its keys
|
||||
are used as source unit names.
|
||||
|
||||
.. _initial-vfs-content-standard-json-with-import-callback:
|
||||
|
||||
#. **Standard JSON (via import callback)**
|
||||
|
||||
With Standard JSON it is also possible to tell the compiler to use the import callback to obtain
|
||||
the source code:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources": {
|
||||
"/usr/local/dapp-bin/token.sol": {
|
||||
"urls": [
|
||||
"/projects/mytoken.sol",
|
||||
"https://example.com/projects/mytoken.sol"
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {"outputSelection": {"*": { "*": ["metadata", "evm.bytecode"]}}}
|
||||
}
|
||||
|
||||
If an import callback is available, the compiler will give it the strings specified in
|
||||
``urls`` one by one, until one is loaded successfully or the end of the list is reached.
|
||||
|
||||
The source unit names are determined the same way as when using ``content`` - they are keys of
|
||||
the ``sources`` dictionary and the content of ``urls`` does not affect them in any way.
|
||||
|
||||
.. index:: standard input, stdin, <stdin>
|
||||
|
||||
#. **Standard input**
|
||||
|
||||
On the command line it is also possible to provide the source by sending it to compiler's
|
||||
standard input:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
echo 'import "./util.sol"; contract C {}' | solc -
|
||||
|
||||
``-`` used as one of the arguments instructs the compiler to place the content of the standard
|
||||
input in the virtual filesystem under a special source unit name: ``<stdin>``.
|
||||
|
||||
Once the VFS is initialized, additional files can still be added to it only through the import
|
||||
callback.
|
||||
|
||||
.. index:: ! import; path
|
||||
|
||||
Imports
|
||||
=======
|
||||
|
||||
The import statement specifies an *import path*.
|
||||
Based on how the import path is specified, we can divide imports into two categories:
|
||||
|
||||
- :ref:`Direct imports <direct-imports>`, where you specify the full source unit name directly.
|
||||
- :ref:`Relative imports <relative-imports>`, where you specify a path starting with ``./`` or ``../``
|
||||
to be combined with the source unit name of the importing file.
|
||||
|
||||
|
||||
.. code-block:: solidity
|
||||
:caption: contracts/contract.sol
|
||||
|
||||
import "./math/math.sol";
|
||||
import "contracts/tokens/token.sol";
|
||||
|
||||
In the above ``./math/math.sol`` and ``contracts/tokens/token.sol`` are import paths while the
|
||||
source unit names they translate to are ``contracts/math/math.sol`` and ``contracts/tokens/token.sol``
|
||||
respectively.
|
||||
|
||||
.. index:: ! direct import, import; direct
|
||||
.. _direct-imports:
|
||||
|
||||
Direct Imports
|
||||
--------------
|
||||
|
||||
An import that does not start with ``./`` or ``../`` is a *direct import*.
|
||||
|
||||
::
|
||||
|
||||
import "/project/lib/util.sol"; // source unit name: /project/lib/util.sol
|
||||
import "lib/util.sol"; // source unit name: lib/util.sol
|
||||
import "@openzeppelin/address.sol"; // source unit name: @openzeppelin/address.sol
|
||||
import "https://example.com/token.sol"; // source unit name: https://example.com/token.sol
|
||||
|
||||
After applying any :ref:`import remappings <import-remapping>` the import path simply becomes the
|
||||
source unit name.
|
||||
|
||||
.. note::
|
||||
|
||||
A source unit name is just an identifier and even if its value happens to look like a path, it
|
||||
is not subject to the normalization rules you would typically expect in a shell.
|
||||
Any ``/./`` or ``/../`` seguments or sequences of multiple slashes remain a part of it.
|
||||
When the source is provided via Standard JSON interface it is entirely possible to associate
|
||||
different content with source unit names that would refer to the same file on disk.
|
||||
|
||||
When the source is not available in the virtual filesystem, the compiler passes the source unit name
|
||||
to the import callback.
|
||||
The Host Filesystem Loader will attempt to use it as a path and look up the file on disk.
|
||||
At this point the platform-specific normalization rules kick in and names that were considered
|
||||
different in the VFS may actually result in the same file being loaded.
|
||||
For example ``/project/lib/math.sol`` and ``/project/lib/../lib///math.sol`` are considered
|
||||
completely different in the VFS even though they refer to the same file on disk.
|
||||
|
||||
.. note::
|
||||
|
||||
Even if an import callback ends up loading source code for two different source unit names from
|
||||
the same file on disk, the compiler will still see them as separate source units.
|
||||
It is the source unit name that matters, not the physical location of the code.
|
||||
|
||||
.. index:: ! relative import, ! import; relative
|
||||
.. _relative-imports:
|
||||
|
||||
Relative Imports
|
||||
----------------
|
||||
|
||||
An import starting with ``./`` or ``../`` is a *relative import*.
|
||||
Such imports specify a path relative to the source unit name of the importing source unit:
|
||||
|
||||
.. code-block:: solidity
|
||||
:caption: /project/lib/math.sol
|
||||
|
||||
import "./util.sol" as util; // source unit name: /project/lib/util.sol
|
||||
import "../token.sol" as token; // source unit name: /project/token.sol
|
||||
|
||||
.. code-block:: solidity
|
||||
:caption: lib/math.sol
|
||||
|
||||
import "./util.sol" as util; // source unit name: lib/util.sol
|
||||
import "../token.sol" as token; // source unit name: token.sol
|
||||
|
||||
.. note::
|
||||
|
||||
Relative imports **always** start with ``./`` or ``../`` so ``import "util.sol"``, unlike
|
||||
``import "./util.sol"``, is a direct import.
|
||||
While both paths would be considered relative in the host filesystem, ``util.sol`` is actually
|
||||
absolute in the VFS.
|
||||
|
||||
Let us define a *path segment* as any non-empty part of the path that does not contain a separator
|
||||
and is bounded by two path separators.
|
||||
A separator is a forward slash or the beginning/end of the string.
|
||||
For example in ``./abc/..//`` there are three path segments: ``.``, ``abc`` and ``..``.
|
||||
|
||||
The compiler computes a source unit name from the import path in the following way:
|
||||
|
||||
1. First a prefix is computed
|
||||
|
||||
- Prefix is initialized with the source unit name of the importing source unit.
|
||||
- The last path segment with preceding slashes is removed from the prefix.
|
||||
- Then, the leading part of the normalized import path, consisting only of ``/`` and ``.``
|
||||
characters is considered.
|
||||
For every ``..`` segment found in this part the last path segment with preceding slashes is
|
||||
removed from the prefix.
|
||||
|
||||
2. Then the prefix is prepended to the normalized import path.
|
||||
If the prefix is non-empty, a single slash is inserted between it and the import path.
|
||||
|
||||
The removal of the last path segment with preceding slashes is understood to
|
||||
work as follows:
|
||||
|
||||
1. Everything past the last slash is removed (i.e. ``a/b//c.sol`` becomes ``a/b//``).
|
||||
2. All trailing slashes are removed (i.e. ``a/b//`` becomes ``a/b``).
|
||||
|
||||
The normalization rules are the same as for UNIX paths, namely:
|
||||
|
||||
- All the internal ``.`` segments are removed.
|
||||
- Every internal ``..`` segment backtracks one level up in the hierarchy.
|
||||
- Multiple slashes are squashed into a single one.
|
||||
|
||||
Note that normalization is performed only on the import path.
|
||||
The source unit name of the importing module that is used for the prefix remains unnormalized.
|
||||
This ensures that the ``protocol://`` part does not turn into ``protocol:/`` if the importing file
|
||||
is identified with a URL.
|
||||
|
||||
If your import paths are already normalized, you can expect the above algorithm to produce very
|
||||
intuitive results.
|
||||
Here are some examples of what you can expect if they are not:
|
||||
|
||||
.. code-block:: solidity
|
||||
:caption: lib/src/../contract.sol
|
||||
|
||||
import "./util/./util.sol"; // source unit name: lib/src/../util/util.sol
|
||||
import "./util//util.sol"; // source unit name: lib/src/../util/util.sol
|
||||
import "../util/../array/util.sol"; // source unit name: lib/src/array/util.sol
|
||||
import "../.././../util.sol"; // source unit name: util.sol
|
||||
import "../../.././../util.sol"; // source unit name: util.sol
|
||||
|
||||
.. note::
|
||||
|
||||
The use of relative imports containing leading ``..`` segments is not recommended.
|
||||
The same effect can be achieved in a more reliable way by using direct imports with
|
||||
:ref:`base path <base-path>` and :ref:`import remapping <import-remapping>`.
|
||||
|
||||
.. index:: ! base path, --base-path
|
||||
.. _base-path:
|
||||
|
||||
Base Path
|
||||
=========
|
||||
|
||||
The base path specifies the directory that the Host Filesystem Loader will load files from.
|
||||
It is simply prepended to a source unit name before the filesystem lookup is performed.
|
||||
|
||||
By default the base path is empty, which leaves the source unit name unchanged.
|
||||
When the source unit name is a relative path, this results in the file being looked up in the
|
||||
directory the compiler has been invoked from.
|
||||
It is also the only value that results in absolute paths in source unit names being actually
|
||||
interpreted as absolute paths on disk.
|
||||
|
||||
If the base path itself is relative, it is also interpreted as relative to the current working
|
||||
directory of the compiler.
|
||||
|
||||
.. index:: ! remapping; import, ! import; remapping, ! remapping; context, ! remapping; prefix, ! remapping; target
|
||||
.. _import-remapping:
|
||||
|
||||
Import Remapping
|
||||
================
|
||||
|
||||
Import remapping allows you to redirect imports to a different location in the virtual filesystem.
|
||||
The mechanism works by changing the translation between import paths and source unit names.
|
||||
For example you can set up a remapping so that any import from the virtual directory
|
||||
``github.com/ethereum/dapp-bin/library/`` would be seen as an import from ``dapp-bin/library/`` instead.
|
||||
|
||||
You can limit the scope of a remapping by specifying a *context*.
|
||||
This allows creating remappings that apply only to imports located in a specific library or a specific file.
|
||||
Without a context a remapping is applied to every matching import in all the files in the virtual
|
||||
filesystem.
|
||||
|
||||
Import remappings have the form of ``context:prefix=target``:
|
||||
|
||||
- ``context`` must match the beginning of the source unit name of the file containing the import.
|
||||
- ``prefix`` must match the beginning of the source unit name resulting from the import.
|
||||
- ``target`` is the value the prefix is replaced with.
|
||||
|
||||
For example, if you clone https://github.com/ethereum/dapp-bin/ locally to ``/project/dapp-bin``
|
||||
and run the compiler with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc github.com/ethereum/dapp-bin/=dapp-bin/ --base-path /project source.sol
|
||||
|
||||
you can use the following in your source file:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
import "github.com/ethereum/dapp-bin/library/math.sol"; // source unit name: dapp-bin/library/math.sol
|
||||
|
||||
The compiler will look for the file in the VFS under ``dapp-bin/library/math.sol``.
|
||||
If the file is not available there, the source unit name will be passed to the Host Filesystem
|
||||
Loader, which will then look in ``/project/dapp-bin/library/iterable_mapping.sol``.
|
||||
|
||||
.. warning::
|
||||
|
||||
Information about remappings is stored in contract metadata.
|
||||
Since the binary produced by the compiler has a hash of the metadata embedded in it, any
|
||||
modification to the remappings will result in different bytecode.
|
||||
|
||||
For this reason you should be careful not to include any local information in remapping targets.
|
||||
For example if your library is located in ``/home/user/packages/mymath/math.sol``, a remapping
|
||||
like ``@math/=/home/user/packages/mymath/`` would result in your home directory being included in
|
||||
the metadata.
|
||||
To be able to reproduce the same bytecode with such a remapping on a different machine, you
|
||||
would need to recreate parts of your local directory structure in the VFS and (if you rely on
|
||||
Host Filesystem Loader) also in the host filesystem.
|
||||
|
||||
As a more complex example, suppose you rely on a module that uses an old version of dapp-bin that
|
||||
you checked out to ``/project/dapp-bin_old``, then you can run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc module1:github.com/ethereum/dapp-bin/=dapp-bin/ \
|
||||
module2:github.com/ethereum/dapp-bin/=dapp-bin_old/ \
|
||||
--base-path /project \
|
||||
source.sol
|
||||
|
||||
This means that all imports in ``module2`` point to the old version but imports in ``module1``
|
||||
point to the new version.
|
||||
|
||||
Here are the detailed rules governing the behaviour of remappings:
|
||||
|
||||
#. **Remappings only affect the translation between import paths and source unit names.**
|
||||
|
||||
Source unit names added to the VFS in any other way cannot be remapped.
|
||||
For example the paths you specify on the command-line and the ones in ``sources.urls`` in
|
||||
Standard JSON are not affected.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc /project/=/contracts/ /project/contract.sol # source unit name: /project/contract.sol
|
||||
|
||||
In the example above the compiler will load the source code from ``/project/contract.sol`` and
|
||||
place it under that exact source unit name in the VFS, not under ``/contract/contract.sol``.
|
||||
|
||||
#. **Context and prefix must match source unit names, not import paths.**
|
||||
|
||||
- This means that you cannot remap ``./`` or ``../`` directly since they are replaced during
|
||||
the translation to source unit name but you can remap the part of the name they are replaced
|
||||
with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc ./=a/ /project/=b/ /project/contract.sol # source unit name: /project/contract.sol
|
||||
|
||||
.. code-block:: solidity
|
||||
:caption: /project/contract.sol
|
||||
|
||||
import "./util.sol" as util; // source unit name: b/util.sol
|
||||
|
||||
- You cannot remap base path or any other part of the path that is only added internally by an
|
||||
import callback:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc /project/=/contracts/ /project/contract.sol --base-path /project # source unit name: /project/contract.sol
|
||||
|
||||
.. code-block:: solidity
|
||||
:caption: /project/contract.sol
|
||||
|
||||
import "util.sol" as util; // source unit name: util.sol
|
||||
|
||||
#. **Target is inserted directly into the source unit name and does not necessarily have to be a valid path.**
|
||||
|
||||
- It can be anything as long as the import callback can handle it.
|
||||
In case of the Host Filesystem Loader this includes also relative paths.
|
||||
When using the JavaScript interface you can even use URLs and abstract identifiers if
|
||||
your callback can handle them.
|
||||
|
||||
- Remapping happens after relative imports have already been resolved into source unit names.
|
||||
This means that targets starting with ``./`` and ``../`` have no special meaning and are
|
||||
relative to the base path rather than to the location of the source file.
|
||||
|
||||
- Remapping targets are not normalized so ``@root/=./a/b//`` will remap ``@root/contract.sol``
|
||||
to ``./a/b//contract.sol`` and not ``a/b/contract.sol``.
|
||||
|
||||
- If the target does not end with a slash, the compiler will not add one automatically:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc /project/=/contracts /project/contract.sol # source unit name: /project/contract.sol
|
||||
|
||||
.. code-block:: solidity
|
||||
:caption: /project/contract.sol
|
||||
|
||||
import "/project/util.sol" as util; // source unit name: /contractsutil.sol
|
||||
|
||||
#. **Context and prefix are patterns and matches must be exact.**
|
||||
|
||||
- ``a//b=c`` will not match ``a/b``.
|
||||
- source unit names are not normalized so ``a/b=c`` will not match ``a//b`` either.
|
||||
- Parts of file and directory names can match as well.
|
||||
``/newProject/con:/new=old`` will match ``/newProject/contract.sol`` and remap it to
|
||||
``oldProject/contract.sol``.
|
||||
|
||||
#. **At most one remapping is applied to a single import.**
|
||||
|
||||
- If multiple remappings match the same source unit name, the one with the longest matching
|
||||
prefix is chosen.
|
||||
- If prefixes are identical, the one specified last wins.
|
||||
- Remappings do not work on other remappings. For example ``a=b b=c c=d`` will not result in ``a``
|
||||
being remapped to ``d``.
|
||||
|
||||
#. **Prefix cannot be empty but context and target are optional.**
|
||||
|
||||
If ``target`` is omitted, it defaults to the value of the ``prefix``.
|
||||
|
||||
.. index:: Remix IDE, file://
|
||||
|
||||
Using URLs in imports
|
||||
=====================
|
||||
|
||||
Most URL prefixes such as ``https://`` or ``data://`` have no special meaning in import paths.
|
||||
The only exception is ``file://`` which is stripped from source unit names by the Host Filesystem
|
||||
Loader.
|
||||
|
||||
When compiling locally you can use import remapping to replace the protocol and domain part with a
|
||||
local path:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
solc :https://github.com/ethereum/dapp-bin=/usr/local/dapp-bin contract.sol
|
||||
|
||||
Note the leading ``:``, which is necessary when the remapping context is empty.
|
||||
Otherwise the ``https:`` part would be interpreted by the compiler as the context.
|
@ -1,3 +1,6 @@
|
||||
sphinx_rtd_theme>=0.3.1
|
||||
pygments-lexer-solidity>=0.7.0
|
||||
sphinx-a4doc>=1.2.1
|
||||
|
||||
# Sphinx 2.1.0 is the oldest version that accepts a lexer class in add_lexer()
|
||||
sphinx>=2.1.0
|
||||
|
15
docs/robots.txt.template
Normal file
15
docs/robots.txt.template
Normal file
@ -0,0 +1,15 @@
|
||||
User-Agent: *
|
||||
Sitemap: http://docs.soliditylang.org/sitemap.xml
|
||||
Host: docs.soliditylang.org
|
||||
|
||||
Allow: /en/latest/
|
||||
Allow: /en/v0.7.6/
|
||||
Allow: /en/v{{ LATEST_VERSION }}/
|
||||
Allow: /_/downloads/en/latest/
|
||||
Allow: /_/downloads/en/0.7.6/
|
||||
Allow: /_/downloads/en/{{ LATEST_VERSION }}/
|
||||
|
||||
# Prevent documentation for the development branches and older Solidity
|
||||
# versions from showing up in search results.
|
||||
Disallow: /en/*
|
||||
Disallow: /_/downloads/en/*
|
@ -56,7 +56,7 @@ to call back into A before this interaction is completed. To give an example,
|
||||
the following code contains a bug (it is just a snippet and not a
|
||||
complete contract):
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -80,7 +80,7 @@ basically retrieve all the Ether in the contract. In particular, the
|
||||
following contract will allow an attacker to refund multiple times
|
||||
as it uses ``call`` which forwards all remaining gas by default:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
@ -100,7 +100,7 @@ as it uses ``call`` which forwards all remaining gas by default:
|
||||
To avoid re-entrancy, you can use the Checks-Effects-Interactions pattern as
|
||||
outlined further below:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -198,7 +198,7 @@ tx.origin
|
||||
|
||||
Never use tx.origin for authorization. Let's say you have a wallet contract like this:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -218,7 +218,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
|
||||
|
||||
Now someone tricks you into sending Ether to the address of this attack wallet:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -251,7 +251,7 @@ They resemble integers when the values are small, but cannot represent arbitrari
|
||||
The following code causes an overflow because the result of the addition is too large
|
||||
to be stored in the type ``uint8``:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
uint8 x = 255;
|
||||
uint8 y = 1;
|
||||
@ -289,7 +289,7 @@ field of a ``struct`` that is the base type of a dynamic storage array. The
|
||||
``mapping`` is also ignored in assignments of structs or arrays containing a
|
||||
``mapping``.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -346,7 +346,7 @@ Recommendations
|
||||
Take Warnings Seriously
|
||||
=======================
|
||||
|
||||
If the compiler warns you about something, you should better change it.
|
||||
If the compiler warns you about something, you should change it.
|
||||
Even if you do not think that this particular warning has security
|
||||
implications, there might be another issue buried beneath it.
|
||||
Any compiler warning we issue can be silenced by slight changes to the
|
||||
|
@ -503,6 +503,18 @@ which has the following form:
|
||||
|
||||
.. _smtchecker_engines:
|
||||
|
||||
Natspec Function Abstraction
|
||||
============================
|
||||
|
||||
Certain functions including common math methods such as ``pow``
|
||||
and ``sqrt`` may be too complex to be analyzed in a fully automated way.
|
||||
These functions can be annotated with Natspec tags that indicate to the
|
||||
SMTChecker that these functions should be abstracted. This means that the
|
||||
body of the function is not used, and when called, the function will:
|
||||
|
||||
- Return a nondeterministic value, and either keep the state variables unchanged if the abstracted function is view/pure, or also set the state variables to nondeterministic values otherwise. This can be used via the annotation ``/// @custom:smtchecker abstract-function-nondet``.
|
||||
- Act as an uninterpreted function. This means that the semantics of the function (given by the body) are ignored, and the only property this function has is that given the same input it guarantees the same output. This is currently under development and will be available via the annotation ``/// @custom:smtchecker abstract-function-uf``.
|
||||
|
||||
Model Checking Engines
|
||||
======================
|
||||
|
||||
@ -654,7 +666,7 @@ the arguments.
|
||||
Using abstraction means loss of precise knowledge, but in many cases it does
|
||||
not mean loss of proving power.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.8.0;
|
||||
@ -701,7 +713,7 @@ location is erased.
|
||||
If the type is nested, the knowledge removal also includes all the prefix base
|
||||
types.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.8.0;
|
||||
|
@ -24,7 +24,7 @@ State Variables
|
||||
State variables are variables whose values are permanently stored in contract
|
||||
storage.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -47,7 +47,7 @@ Functions are the executable units of code. Functions are usually
|
||||
defined inside a contract, but they can also be defined outside of
|
||||
contracts.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0 <0.9.0;
|
||||
@ -81,7 +81,7 @@ is not possible.
|
||||
|
||||
Like functions, modifiers can be :ref:`overridden <modifier-overriding>`.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
@ -109,7 +109,7 @@ Events
|
||||
|
||||
Events are convenience interfaces with the EVM logging facilities.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.21 <0.9.0;
|
||||
@ -137,7 +137,7 @@ In comparison to string descriptions, errors are much cheaper and allow you
|
||||
to encode additional data. You can use NatSpec to describe the error to
|
||||
the user.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -168,7 +168,7 @@ Struct Types
|
||||
Structs are custom defined types that can group several variables (see
|
||||
:ref:`structs` in types section).
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -190,7 +190,7 @@ Enum Types
|
||||
Enums can be used to create custom types with a finite set of 'constant values' (see
|
||||
:ref:`enums` in types section).
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
|
@ -53,7 +53,9 @@ Blank Lines
|
||||
|
||||
Surround top level declarations in solidity source with two blank lines.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -72,7 +74,9 @@ Yes::
|
||||
// ...
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -92,7 +96,9 @@ Within a contract surround function declarations with a single blank line.
|
||||
|
||||
Blank lines may be omitted between groups of related one-liners (such as stub functions for an abstract contract)
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -113,7 +119,9 @@ Yes::
|
||||
}
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -150,7 +158,9 @@ Wrapped lines should conform to the following guidelines.
|
||||
|
||||
Function Calls
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
thisFunctionCallIsReallyLong(
|
||||
longArgument1,
|
||||
@ -158,7 +168,9 @@ Yes::
|
||||
longArgument3
|
||||
);
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
thisFunctionCallIsReallyLong(longArgument1,
|
||||
longArgument2,
|
||||
@ -188,7 +200,9 @@ No::
|
||||
|
||||
Assignment Statements
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
thisIsALongNestedMapping[being][set][to_some_value] = someFunction(
|
||||
argument1,
|
||||
@ -197,7 +211,9 @@ Yes::
|
||||
argument4
|
||||
);
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
thisIsALongNestedMapping[being][set][to_some_value] = someFunction(argument1,
|
||||
argument2,
|
||||
@ -206,7 +222,9 @@ No::
|
||||
|
||||
Event Definitions and Event Emitters
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
event LongAndLotsOfArgs(
|
||||
address sender,
|
||||
@ -224,7 +242,9 @@ Yes::
|
||||
options
|
||||
);
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
event LongAndLotsOfArgs(address sender,
|
||||
address recipient,
|
||||
@ -248,7 +268,9 @@ Imports
|
||||
|
||||
Import statements should always be placed at the top of the file.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -263,7 +285,9 @@ Yes::
|
||||
// ...
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -297,7 +321,9 @@ Functions should be grouped according to their visibility and ordered:
|
||||
|
||||
Within a grouping, place the ``view`` and ``pure`` functions last.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -333,7 +359,9 @@ Yes::
|
||||
// ...
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -370,11 +398,15 @@ Avoid extraneous whitespace in the following situations:
|
||||
|
||||
Immediately inside parenthesis, brackets or braces, with the exception of single line function declarations.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
spam(ham[1], Coin({name: "ham"}));
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
spam( ham[ 1 ], Coin( { name: "ham" } ) );
|
||||
|
||||
@ -384,23 +416,31 @@ Exception::
|
||||
|
||||
Immediately before a comma, semicolon:
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function spam(uint i, Coin coin) public;
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function spam(uint i , Coin coin) public ;
|
||||
|
||||
More than one space around an assignment or other operator to align with another:
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
x = 1;
|
||||
y = 2;
|
||||
long_variable = 3;
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
x = 1;
|
||||
y = 2;
|
||||
@ -408,7 +448,9 @@ No::
|
||||
|
||||
Don't include a whitespace in the receive and fallback functions:
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
receive() external payable {
|
||||
...
|
||||
@ -418,7 +460,9 @@ Yes::
|
||||
...
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
receive () external payable {
|
||||
...
|
||||
@ -440,7 +484,9 @@ should:
|
||||
declaration.
|
||||
* The opening brace should be preceded by a single space.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -452,7 +498,9 @@ Yes::
|
||||
}
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -473,7 +521,9 @@ Additionally there should be a single space between the control structures
|
||||
conditional, as well as a single space between the conditional parenthetic
|
||||
block and the opening brace.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
if (...) {
|
||||
...
|
||||
@ -483,7 +533,9 @@ Yes::
|
||||
...
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
if (...)
|
||||
{
|
||||
@ -499,12 +551,16 @@ No::
|
||||
For control structures whose body contains a single statement, omitting the
|
||||
braces is ok *if* the statement is contained on a single line.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
if (x < 10)
|
||||
x += 1;
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
if (x < 10)
|
||||
someArray.push(Coin({
|
||||
@ -516,7 +572,9 @@ For ``if`` blocks which have an ``else`` or ``else if`` clause, the ``else`` sho
|
||||
placed on the same line as the ``if``'s closing brace. This is an exception compared
|
||||
to the rules of other block-like structures.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
if (x < 3) {
|
||||
x += 1;
|
||||
@ -532,7 +590,9 @@ Yes::
|
||||
else
|
||||
x -= 1;
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
if (x < 3) {
|
||||
x += 1;
|
||||
@ -552,7 +612,9 @@ declaration.
|
||||
|
||||
The opening brace should be preceded by a single space.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function increment(uint x) public pure returns (uint) {
|
||||
return x + 1;
|
||||
@ -562,7 +624,9 @@ Yes::
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function increment(uint x) public pure returns (uint)
|
||||
{
|
||||
@ -588,7 +652,9 @@ The modifier order for a function should be:
|
||||
4. Override
|
||||
5. Custom modifiers
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function balance(uint from) public view override returns (uint) {
|
||||
return balanceOf[from];
|
||||
@ -598,7 +664,9 @@ Yes::
|
||||
selfdestruct(owner);
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function balance(uint from) public override view returns (uint) {
|
||||
return balanceOf[from];
|
||||
@ -613,7 +681,9 @@ it's own line at the same indentation level as the function body. The closing
|
||||
parenthesis and opening bracket should be placed on their own line as well at
|
||||
the same indentation level as the function declaration.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function thisFunctionHasLotsOfArguments(
|
||||
address a,
|
||||
@ -628,7 +698,9 @@ Yes::
|
||||
doSomething();
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function thisFunctionHasLotsOfArguments(address a, address b, address c,
|
||||
address d, address e, address f) public {
|
||||
@ -657,7 +729,9 @@ No::
|
||||
If a long function declaration has modifiers, then each modifier should be
|
||||
dropped to its own line.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function thisFunctionNameIsReallyLong(address x, address y, address z)
|
||||
public
|
||||
@ -681,7 +755,9 @@ Yes::
|
||||
doSomething();
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function thisFunctionNameIsReallyLong(address x, address y, address z)
|
||||
public
|
||||
@ -707,7 +783,9 @@ No::
|
||||
|
||||
Multiline output parameters and return statements should follow the same style recommended for wrapping long lines found in the :ref:`Maximum Line Length <maximum_line_length>` section.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function thisFunctionNameIsReallyLong(
|
||||
address a,
|
||||
@ -730,7 +808,9 @@ Yes::
|
||||
);
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function thisFunctionNameIsReallyLong(
|
||||
address a,
|
||||
@ -753,7 +833,9 @@ For constructor functions on inherited contracts whose bases require arguments,
|
||||
it is recommended to drop the base constructors onto new lines in the same
|
||||
manner as modifiers if the function declaration is long or hard to read.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -784,7 +866,9 @@ Yes::
|
||||
}
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -834,7 +918,9 @@ No::
|
||||
|
||||
When declaring short functions with a single statement, it is permissible to do it on a single line.
|
||||
|
||||
Permissible::
|
||||
Permissible:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function shortFunction() public { doSomething(); }
|
||||
|
||||
@ -849,14 +935,18 @@ In variable declarations, do not separate the keyword ``mapping`` from its
|
||||
type by a space. Do not separate any nested ``mapping`` keyword from its type by
|
||||
whitespace.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
mapping(uint => uint) map;
|
||||
mapping(address => bool) registeredAddresses;
|
||||
mapping(uint => mapping(bool => Data[])) public data;
|
||||
mapping(uint => mapping(uint => s)) data;
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
mapping (uint => uint) map;
|
||||
mapping( address => bool ) registeredAddresses;
|
||||
@ -869,11 +959,15 @@ Variable Declarations
|
||||
Declarations of array variables should not have a space between the type and
|
||||
the brackets.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
uint[] x;
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
uint [] x;
|
||||
|
||||
@ -883,26 +977,34 @@ Other Recommendations
|
||||
|
||||
* Strings should be quoted with double-quotes instead of single-quotes.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
str = "foo";
|
||||
str = "Hamlet says, 'To be or not to be...'";
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
str = 'bar';
|
||||
str = '"Be yourself; everyone else is already taken." -Oscar Wilde';
|
||||
|
||||
* Surround operators with a single space on either side.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
x = 3;
|
||||
x = 100 / 10;
|
||||
x += 3 + 4;
|
||||
x |= y && z;
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
x=3;
|
||||
x = 100/10;
|
||||
@ -914,13 +1016,17 @@ No::
|
||||
improved readability for complex statement. You should always use the same
|
||||
amount of whitespace on either side of an operator:
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
x = 2**3 + 5;
|
||||
x = 2*y + 3*z;
|
||||
x = (a+b) * (a-b);
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
x = 2** 3 + 5;
|
||||
x = y+z;
|
||||
@ -1005,7 +1111,9 @@ Contract and Library Names
|
||||
|
||||
As shown in the example below, if the contract name is ``Congress`` and the library name is ``Owned``, then their associated filenames should be ``Congress.sol`` and ``Owned.sol``.
|
||||
|
||||
Yes::
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -1028,7 +1136,9 @@ Yes::
|
||||
}
|
||||
}
|
||||
|
||||
and in ``Congress.sol``::
|
||||
and in ``Congress.sol``:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -1040,7 +1150,9 @@ and in ``Congress.sol``::
|
||||
//...
|
||||
}
|
||||
|
||||
No::
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
@ -1063,7 +1175,9 @@ No::
|
||||
}
|
||||
}
|
||||
|
||||
and in ``Congress.sol``::
|
||||
and in ``Congress.sol``:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.7.0;
|
||||
@ -1147,7 +1261,9 @@ triple slash (``///``) or a double asterisk block (``/** ... */``) and
|
||||
they should be used directly above function declarations or statements.
|
||||
|
||||
For example, the contract from :ref:`a simple smart contract <simple-smart-contract>` with the comments
|
||||
added looks like the one below::
|
||||
added looks like the one below:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
|
@ -32,7 +32,7 @@ in the ``uint16`` type. The resulting type of the expression ``y + z`` is ``uint
|
||||
Because it is assigned to a variable of type ``uint32`` another implicit conversion
|
||||
is performed after the addition.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
uint8 y;
|
||||
uint16 z;
|
||||
@ -50,7 +50,7 @@ result is what you want and expect!
|
||||
|
||||
Take the following example that converts a negative ``int`` to a ``uint``:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
int y = -3;
|
||||
uint x = uint(y);
|
||||
@ -59,13 +59,17 @@ At the end of this code snippet, ``x`` will have the value ``0xfffff..fd`` (64 h
|
||||
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::
|
||||
cut off:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
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::
|
||||
The result of the conversion will compare equal to the original integer:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
uint16 a = 0x1234;
|
||||
uint32 b = uint32(a); // b will be 0x00001234 now
|
||||
@ -73,14 +77,18 @@ The result of the conversion will compare equal to the original integer::
|
||||
|
||||
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::
|
||||
sequence:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
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)::
|
||||
after the conversion (if the index is still in range):
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
bytes2 a = 0x1234;
|
||||
bytes4 b = bytes4(a); // b will be 0x12340000
|
||||
@ -91,7 +99,9 @@ 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::
|
||||
rules explicit:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
bytes2 a = 0x1234;
|
||||
uint32 b = uint16(a); // b will be 0x00001234
|
||||
@ -103,7 +113,7 @@ rules explicit::
|
||||
In case the array is longer than the target fixed bytes type, truncation at the end will happen.
|
||||
If the array is shorter than the target type, it will be padded with zeros at the end.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.5;
|
||||
@ -129,7 +139,9 @@ Integer Types
|
||||
-------------
|
||||
|
||||
Decimal and hexadecimal number literals can be implicitly converted to any integer type
|
||||
that is large enough to represent it without truncation::
|
||||
that is large enough to represent it without truncation:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
uint8 a = 12; // fine
|
||||
uint32 b = 1234; // fine
|
||||
@ -146,7 +158,9 @@ 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::
|
||||
converted to any fixed-size bytes type:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
bytes2 a = 54321; // not allowed
|
||||
bytes2 b = 0x12; // not allowed
|
||||
@ -157,7 +171,9 @@ converted to any fixed-size bytes type::
|
||||
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::
|
||||
if their number of characters matches the size of the bytes type:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
bytes2 a = hex"1234"; // fine
|
||||
bytes2 b = "xy"; // fine
|
||||
|
@ -40,7 +40,7 @@ an Ethereum address to an unsigned integer value. As ``uint`` is a value type, t
|
||||
returns a value that matches the type, which you can see in the ``MappingUser``
|
||||
contract that returns the value at the specified address.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -66,7 +66,7 @@ The example below is a simplified version of an
|
||||
``_allowances`` is an example of a mapping type inside another mapping type.
|
||||
The example below uses ``_allowances`` to record the amount someone else is allowed to withdraw from your account.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
@ -121,7 +121,7 @@ top of them and iterate over that. For example, the code below implements an
|
||||
``IterableMapping`` library that the ``User`` contract then adds data too, and
|
||||
the ``sum`` function iterates over to sum all the values.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.8 <0.9.0;
|
||||
|
@ -40,7 +40,7 @@ This distinction is visible when ``a`` is reference variable: It
|
||||
will only reset ``a`` itself, not the
|
||||
value it referred to previously.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
|
@ -60,7 +60,7 @@ Data locations are not only relevant for persistency of data, but also for the s
|
||||
variables of storage struct type, even if the local variable
|
||||
itself is just a reference.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@ -114,7 +114,7 @@ Indices are zero-based, and access is in the opposite direction of the
|
||||
declaration.
|
||||
|
||||
For example, if you have a variable ``uint[][5] memory x``, you access the
|
||||
second ``uint`` in the third dynamic array using ``x[2][1]``, and to access the
|
||||
seventh ``uint`` in the third dynamic array using ``x[2][6]``, 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``.
|
||||
@ -171,7 +171,7 @@ You can concatenate a variable number of ``bytes`` or ``bytes1 ... bytes32`` usi
|
||||
The function returns a single ``bytes memory`` array that contains the contents of the arguments without padding.
|
||||
If you want to use string parameters or other types, you need to convert them to ``bytes`` or ``bytes1``/.../``bytes32`` first.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@ -200,7 +200,7 @@ or create a new memory array and copy every element.
|
||||
As all variables in Solidity, the elements of newly allocated arrays are always initialized
|
||||
with the :ref:`default value<default-value>`.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -239,7 +239,7 @@ In the example below, the type of ``[1, 2, 3]`` is
|
||||
you want the result to be a ``uint[3] memory`` type, you need to convert
|
||||
the first element to ``uint``.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -261,7 +261,7 @@ Since fixed-size memory arrays of different type cannot be converted into each o
|
||||
(even if the base types can), you always have to specify a common base type explicitly
|
||||
if you want to use two-dimensional array literals:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -278,7 +278,7 @@ if you want to use two-dimensional array literals:
|
||||
Fixed size memory arrays cannot be assigned to dynamically-sized
|
||||
memory arrays, i.e. the following is not possible:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.0 <0.9.0;
|
||||
@ -298,7 +298,7 @@ complications because of how arrays are passed in the ABI.
|
||||
If you want to initialize dynamically-sized arrays, you have to assign the
|
||||
individual elements:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -356,7 +356,7 @@ Array Members
|
||||
that return dynamic arrays, make sure to use an EVM that is set to
|
||||
Byzantium mode.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
@ -489,7 +489,7 @@ they only exist in intermediate expressions.
|
||||
|
||||
Array slices are useful to ABI-decode secondary data passed in function parameters:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.4 <0.9.0;
|
||||
@ -528,7 +528,7 @@ Structs
|
||||
Solidity provides a way to define new types in the form of structs, which is
|
||||
shown in the following example:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
|
@ -66,16 +66,23 @@ Shifts
|
||||
^^^^^^
|
||||
|
||||
The result of a shift operation has the type of the left operand, truncating the result to match the type.
|
||||
Right operand must be unsigned type. Trying to shift by signed type will produce a compilation error.
|
||||
The right operand must be of unsigned type, trying to shift by an signed type will produce a compilation error.
|
||||
|
||||
- For positive and negative ``x`` values, ``x << y`` is equivalent to ``x * 2**y``.
|
||||
- For positive ``x`` values, ``x >> y`` is equivalent to ``x / 2**y``.
|
||||
- For negative ``x`` values, ``x >> y`` is equivalent to ``(x + 1) / 2**y - 1`` (which is the same as dividing ``x`` by ``2**y`` while rounding down towards negative infinity).
|
||||
Shifts can be "simulated" using multiplication by powers of two in the following way. Note that the truncation
|
||||
to the type of the left operand is always performed at the end, but not mentioned explicitly.
|
||||
|
||||
- ``x << y`` is equivalent to the mathematical expression ``x * 2**y``.
|
||||
- ``x >> y`` is equivalent to the mathematical expression ``x / 2**y``, rounded towards negative infinity.
|
||||
|
||||
.. warning::
|
||||
Before version ``0.5.0`` a right shift ``x >> y`` for negative ``x`` was equivalent to ``x / 2**y``,
|
||||
Before version ``0.5.0`` a right shift ``x >> y`` for negative ``x`` was equivalent to
|
||||
the mathematical expression ``x / 2**y`` rounded towards zero,
|
||||
i.e., right shifts used rounding up (towards zero) instead of rounding down (towards negative infinity).
|
||||
|
||||
.. note::
|
||||
Overflow checks are never performed for shift operations as they are done for arithmetic operations.
|
||||
Instead, the result is always truncated.
|
||||
|
||||
Addition, Subtraction and Multiplication
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@ -231,7 +238,7 @@ For a quick reference of all members of address, see :ref:`address_related`.
|
||||
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:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
address payable x = address(0x123);
|
||||
address myAddress = address(this);
|
||||
@ -265,7 +272,9 @@ return the success condition (as a ``bool``) and the returned data
|
||||
The functions ``abi.encode``, ``abi.encodePacked``, ``abi.encodeWithSelector``
|
||||
and ``abi.encodeWithSignature`` can be used to encode structured data.
|
||||
|
||||
Example::
|
||||
Example:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
bytes memory payload = abi.encodeWithSignature("register(string)", "MyName");
|
||||
(bool success, bytes memory returnData) = address(nameReg).call(payload);
|
||||
@ -284,15 +293,21 @@ Example::
|
||||
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::
|
||||
It is possible to adjust the supplied gas with the ``gas`` modifier:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName"));
|
||||
|
||||
Similarly, the supplied Ether value can be controlled too::
|
||||
Similarly, the supplied Ether value can be controlled too:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
|
||||
|
||||
Lastly, these modifiers can be combined. Their order does not matter::
|
||||
Lastly, these modifiers can be combined. Their order does not matter:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
|
||||
|
||||
@ -305,8 +320,8 @@ Since byzantium ``staticcall`` can be used as well. This is basically the same a
|
||||
|
||||
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``.
|
||||
The ``gas`` option is available on all three methods, while the ``value`` option is only available
|
||||
on ``call``.
|
||||
|
||||
.. note::
|
||||
It is best to avoid relying on hardcoded gas values in your smart contract code,
|
||||
@ -476,7 +491,7 @@ regardless of the type of the right (exponent) operand.
|
||||
for the type of ``2.5`` and ``uint128``, the Solidity compiler does not accept
|
||||
this code.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
uint128 a = 1;
|
||||
uint128 b = 2.5 + a + 0.5;
|
||||
@ -529,7 +544,7 @@ Unicode Literals
|
||||
While regular string literals can only contain ASCII, Unicode literals – prefixed with the keyword ``unicode`` – can contain any valid UTF-8 sequence.
|
||||
They also support the very same escape sequences as regular string literals.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
string memory a = unicode"Hello 😃";
|
||||
|
||||
@ -567,7 +582,7 @@ The data representation is the same as for enums in C: The options are represent
|
||||
subsequent unsigned integer values starting from ``0``.
|
||||
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -619,7 +634,9 @@ 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 types are notated as follows:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]
|
||||
|
||||
@ -688,7 +705,9 @@ External (or public) functions have the following members:
|
||||
respectively. See :ref:`External Function Calls <external-function-calls>` for
|
||||
more information.
|
||||
|
||||
Example that shows how to use the members::
|
||||
Example that shows how to use the members:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.4 <0.9.0;
|
||||
@ -704,7 +723,9 @@ Example that shows how to use the members::
|
||||
}
|
||||
}
|
||||
|
||||
Example that shows how to use internal function types::
|
||||
Example that shows how to use internal function types:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@ -762,7 +783,9 @@ Example that shows how to use internal function types::
|
||||
}
|
||||
}
|
||||
|
||||
Another example that uses external function types::
|
||||
Another example that uses external function types:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
|
@ -9,7 +9,7 @@ Ether Units
|
||||
|
||||
A literal number can take a suffix of ``wei``, ``gwei`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
assert(1 wei == 1);
|
||||
assert(1 gwei == 1e9);
|
||||
@ -45,7 +45,9 @@ library has to be updated by an external oracle.
|
||||
The suffix ``years`` has been removed in version 0.5.0 due to the reasons above.
|
||||
|
||||
These suffixes cannot be applied to variables. For example, if you want to
|
||||
interpret a function parameter in days, you can in the following way::
|
||||
interpret a function parameter in days, you can in the following way:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function f(uint start, uint daysAfter) public {
|
||||
if (block.timestamp >= start + daysAfter * 1 days) {
|
||||
@ -68,7 +70,7 @@ or are general-use utility functions.
|
||||
Block and Transaction Properties
|
||||
--------------------------------
|
||||
|
||||
- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks
|
||||
- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block when ``blocknumber`` is one of the 256 most recent blocks; otherwise returns zero
|
||||
- ``block.chainid`` (``uint``): current chain id
|
||||
- ``block.coinbase`` (``address payable``): current block miner's address
|
||||
- ``block.difficulty`` (``uint``): current block difficulty
|
||||
|
@ -2,7 +2,7 @@
|
||||
Using the Compiler
|
||||
******************
|
||||
|
||||
.. index:: ! commandline compiler, compiler;commandline, ! solc, ! linker
|
||||
.. index:: ! commandline compiler, compiler;commandline, ! solc
|
||||
|
||||
.. _commandline-compiler:
|
||||
|
||||
@ -33,11 +33,13 @@ This parameter has effects on the following (this might change in the future):
|
||||
- the size of the binary search in the function dispatch routine
|
||||
- the way constants like large numbers or strings are stored
|
||||
|
||||
Path Remapping
|
||||
--------------
|
||||
.. index:: allowed paths, --allow-paths, base path, --base-path
|
||||
|
||||
Base Path and Import Remapping
|
||||
------------------------------
|
||||
|
||||
The commandline compiler will automatically read imported files from the filesystem, but
|
||||
it is also possible to provide path redirects using ``prefix=path`` in the following way:
|
||||
it is also possible to provide :ref:`path redirects <import-remapping>` using ``prefix=path`` in the following way:
|
||||
|
||||
::
|
||||
|
||||
@ -49,19 +51,24 @@ This essentially instructs the compiler to search for anything starting with
|
||||
the remapping targets and outside of the directories where explicitly specified source
|
||||
files reside, so things like ``import "/etc/passwd";`` only work if you add ``/=/`` as a remapping.
|
||||
|
||||
An empty remapping prefix is not allowed.
|
||||
|
||||
If there are multiple matches due to remappings, the one with the longest common prefix is selected.
|
||||
|
||||
When accessing the filesystem to search for imports, all paths are treated as if they were fully qualified paths.
|
||||
This behaviour can be customized by adding the command line option ``--base-path`` with a path to be prepended
|
||||
before each filesystem access for imports is performed. Furthermore, the part added via ``--base-path``
|
||||
will not appear in the contract metadata.
|
||||
|
||||
For security reasons the compiler has restrictions what directories it can access. Paths (and their subdirectories) of source files specified on the commandline and paths defined by remappings are allowed for import statements, but everything else is rejected. Additional paths (and their subdirectories) can be allowed via the ``--allow-paths /sample/path,/another/sample/path`` switch.
|
||||
When accessing the filesystem to search for imports, :ref:`paths that do not start with ./
|
||||
or ../ <relative-imports>` are treated as relative to the directory specified using
|
||||
``--base-path`` option (or the current working directory if base path is not specified).
|
||||
Furthermore, the part added via ``--base-path`` will not appear in the contract metadata.
|
||||
|
||||
For security reasons the compiler has restrictions on what directories it can access.
|
||||
Directories of source files specified on the command line and target paths of
|
||||
remappings are automatically allowed to be accessed by the file reader, but everything
|
||||
else is rejected by default.
|
||||
Additional paths (and their subdirectories) can be allowed via the
|
||||
``--allow-paths /sample/path,/another/sample/path`` switch.
|
||||
Everything inside the path specified via ``--base-path`` is always allowed.
|
||||
|
||||
The above is only a simplification of how the compiler handles import paths.
|
||||
For a detailed explanation with examples and discussion of corner cases please refer to the section on
|
||||
:ref:`path resolution <path-resolution>`.
|
||||
|
||||
.. index:: ! linker, ! --link, ! --libraries
|
||||
.. _library-linking:
|
||||
|
||||
Library Linking
|
||||
@ -79,6 +86,8 @@ Either add ``--libraries "file.sol:Math=0x12345678901234567890123456789012345678
|
||||
.. note::
|
||||
Starting Solidity 0.8.1 accepts ``=`` as separator between library and address, and ``:`` as a separator is deprecated. It will be removed in the future. Currently ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` will work too.
|
||||
|
||||
.. index:: --standard-json, --base-path
|
||||
|
||||
If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. The process will always terminate in a "success" state and report any errors via the JSON output.
|
||||
The option ``--base-path`` is also processed in standard-json mode.
|
||||
|
||||
@ -158,11 +167,15 @@ at each version. Backward compatibility is not guaranteed between each version.
|
||||
- Shifting operators use shifting opcodes and thus need less gas.
|
||||
- ``petersburg``
|
||||
- The compiler behaves the same way as with constantinople.
|
||||
- ``istanbul`` (**default**)
|
||||
- ``istanbul``
|
||||
- Opcodes ``chainid`` and ``selfbalance`` are available in assembly.
|
||||
- ``berlin`` (**experimental**)
|
||||
- ``berlin`` (**default**)
|
||||
- Gas costs for ``SLOAD``, ``*CALL``, ``BALANCE``, ``EXT*`` and ``SELFDESTRUCT`` increased. The
|
||||
compiler assumes cold gas costs for such operations. This is relevant for gas estimation and
|
||||
the optimizer.
|
||||
|
||||
|
||||
.. index:: ! standard JSON, ! --standard-json
|
||||
.. _compiler-api:
|
||||
|
||||
Compiler Input and Output JSON Description
|
||||
@ -233,7 +246,10 @@ Input Description
|
||||
"remappings": [ ":g=/dir" ],
|
||||
// Optional: Optimizer settings
|
||||
"optimizer": {
|
||||
// disabled by default
|
||||
// Disabled by default.
|
||||
// NOTE: enabled=false still leaves some optimizations on. See comments below.
|
||||
// WARNING: Before version 0.8.6 omitting the 'enabled' key was not equivalent to setting
|
||||
// it to false and would actually disable all the optimizations.
|
||||
"enabled": true,
|
||||
// Optimize for how many times you intend to run the code.
|
||||
// Lower values will optimize more for initial deployment cost, higher
|
||||
@ -344,6 +360,7 @@ Input Description
|
||||
// storageLayout - Slots, offsets and types of the contract's state variables.
|
||||
// evm.assembly - New assembly format
|
||||
// evm.legacyAssembly - Old-style assembly format in JSON
|
||||
// evm.bytecode.functionDebugData - Debugging information at function level
|
||||
// evm.bytecode.object - Bytecode object
|
||||
// evm.bytecode.opcodes - Opcodes list
|
||||
// evm.bytecode.sourceMap - Source mapping (useful for debugging)
|
||||
@ -476,6 +493,17 @@ Output Description
|
||||
"legacyAssembly": {},
|
||||
// Bytecode and related details.
|
||||
"bytecode": {
|
||||
// Debugging data at the level of functions.
|
||||
"functionDebugData": {
|
||||
// Now follows a set of functions including compiler-internal and
|
||||
// user-defined function. The set does not have to be complete.
|
||||
"@mint_13": { // Internal name of the function
|
||||
"entryPoint": 128, // Byte offset into the bytecode where the function starts (optional)
|
||||
"id": 13, // AST ID of the function definition or null for compiler-internal functions (optional)
|
||||
"parameterSlots": 2, // Number of EVM stack slots for the function parameters (optional)
|
||||
"returnSlots": 1 // Number of EVM stack slots for the return values (optional)
|
||||
}
|
||||
},
|
||||
// The bytecode as a hex string.
|
||||
"object": "00fe",
|
||||
// Opcodes list (string)
|
||||
|
24
docs/yul.rst
24
docs/yul.rst
@ -1076,6 +1076,23 @@ regular strings in native encoding. For code,
|
||||
|
||||
Above, ``Block`` refers to ``Block`` in the Yul code grammar explained in the previous chapter.
|
||||
|
||||
.. note::
|
||||
|
||||
Data objects or sub-objects whose names contain a ``.`` can be defined
|
||||
but it is not possible to access them through ``datasize``,
|
||||
``dataoffset`` or ``datacopy`` because ``.`` is used as a separator
|
||||
to access objects inside another object.
|
||||
|
||||
.. note::
|
||||
|
||||
The data object called ``".metadata"`` has a special meaning:
|
||||
It cannot be accessed from code and is always appended to the very end of the
|
||||
bytecode, regardless of its position in the object.
|
||||
|
||||
Other data objects with special significance might be added in the
|
||||
future, but their names will always start with a ``.``.
|
||||
|
||||
|
||||
An example Yul Object is shown below:
|
||||
|
||||
.. code-block:: yul
|
||||
@ -1155,9 +1172,8 @@ Yul Optimizer
|
||||
The Yul optimizer operates on Yul code and uses the same language for input, output and
|
||||
intermediate states. This allows for easy debugging and verification of the optimizer.
|
||||
|
||||
Please see the
|
||||
`documentation in the source code <https://github.com/ethereum/solidity/blob/develop/libyul/optimiser/README.md>`_
|
||||
for more details about its internals.
|
||||
Please refer to the general :ref:`optimizer documentation <optimizer>`
|
||||
for more details about the different optimization stages and how to use the optimizer.
|
||||
|
||||
If you want to use Solidity in stand-alone Yul mode, you activate the optimizer using ``--optimize``:
|
||||
|
||||
@ -1167,7 +1183,7 @@ If you want to use Solidity in stand-alone Yul mode, you activate the optimizer
|
||||
|
||||
In Solidity mode, the Yul optimizer is activated together with the regular optimizer.
|
||||
|
||||
Optimization step sequence
|
||||
Optimization Step Sequence
|
||||
--------------------------
|
||||
|
||||
By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly.
|
||||
|
@ -348,12 +348,18 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices)
|
||||
return root;
|
||||
}
|
||||
|
||||
AssemblyItem Assembly::namedTag(string const& _name)
|
||||
AssemblyItem Assembly::namedTag(string const& _name, size_t _params, size_t _returns, optional<uint64_t> _sourceID)
|
||||
{
|
||||
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
|
||||
if (!m_namedTags.count(_name))
|
||||
m_namedTags[_name] = static_cast<size_t>(newTag().data());
|
||||
return AssemblyItem{Tag, m_namedTags.at(_name)};
|
||||
if (m_namedTags.count(_name))
|
||||
{
|
||||
assertThrow(m_namedTags.at(_name).params == _params, AssemblyException, "");
|
||||
assertThrow(m_namedTags.at(_name).returns == _returns, AssemblyException, "");
|
||||
assertThrow(m_namedTags.at(_name).sourceID == _sourceID, AssemblyException, "");
|
||||
}
|
||||
else
|
||||
m_namedTags[_name] = {static_cast<size_t>(newTag().data()), _sourceID, _params, _returns};
|
||||
return AssemblyItem{Tag, m_namedTags.at(_name).id};
|
||||
}
|
||||
|
||||
AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
|
||||
@ -722,13 +728,16 @@ LinkerObject const& Assembly::assemble() const
|
||||
ret.bytecode.resize(ret.bytecode.size() + 20);
|
||||
break;
|
||||
case Tag:
|
||||
{
|
||||
assertThrow(i.data() != 0, AssemblyException, "Invalid tag position.");
|
||||
assertThrow(i.splitForeignPushTag().first == numeric_limits<size_t>::max(), AssemblyException, "Foreign tag.");
|
||||
size_t tagId = static_cast<size_t>(i.data());
|
||||
assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large.");
|
||||
assertThrow(m_tagPositionsInBytecode[static_cast<size_t>(i.data())] == numeric_limits<size_t>::max(), AssemblyException, "Duplicate tag position.");
|
||||
m_tagPositionsInBytecode[static_cast<size_t>(i.data())] = ret.bytecode.size();
|
||||
assertThrow(m_tagPositionsInBytecode[tagId] == numeric_limits<size_t>::max(), AssemblyException, "Duplicate tag position.");
|
||||
m_tagPositionsInBytecode[tagId] = ret.bytecode.size();
|
||||
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::JUMPDEST));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling.");
|
||||
}
|
||||
@ -770,6 +779,17 @@ LinkerObject const& Assembly::assemble() const
|
||||
bytesRef r(ret.bytecode.data() + i.first, bytesPerTag);
|
||||
toBigEndian(pos, r);
|
||||
}
|
||||
for (auto const& [name, tagInfo]: m_namedTags)
|
||||
{
|
||||
size_t position = m_tagPositionsInBytecode.at(tagInfo.id);
|
||||
ret.functionDebugData[name] = {
|
||||
position == numeric_limits<size_t>::max() ? nullopt : optional<size_t>{position},
|
||||
tagInfo.sourceID,
|
||||
tagInfo.params,
|
||||
tagInfo.returns
|
||||
};
|
||||
}
|
||||
|
||||
for (auto const& dataItem: m_data)
|
||||
{
|
||||
auto references = dataRef.equal_range(dataItem.first);
|
||||
|
@ -30,11 +30,14 @@
|
||||
#include <libsolutil/Assertions.h>
|
||||
#include <libsolutil/Keccak256.h>
|
||||
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
namespace solidity::evmasm
|
||||
{
|
||||
@ -49,7 +52,7 @@ public:
|
||||
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
|
||||
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
|
||||
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
|
||||
AssemblyItem namedTag(std::string const& _name);
|
||||
AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID);
|
||||
AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
|
||||
bytes const& data(util::h256 const& _i) const { return m_data.at(_i); }
|
||||
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
|
||||
@ -91,7 +94,7 @@ public:
|
||||
void pushSubroutineOffset(size_t _subRoutine) { append(AssemblyItem(PushSub, _subRoutine)); }
|
||||
|
||||
/// Appends @a _data literally to the very end of the bytecode.
|
||||
void appendAuxiliaryDataToEnd(bytes const& _data) { m_auxiliaryData += _data; }
|
||||
void appendToAuxiliaryData(bytes const& _data) { m_auxiliaryData += _data; }
|
||||
|
||||
/// Returns the assembly items.
|
||||
AssemblyItems const& items() const { return m_items; }
|
||||
@ -123,7 +126,7 @@ public:
|
||||
langutil::EVMVersion evmVersion;
|
||||
/// This specifies an estimate on how often each opcode in this assembly will be executed,
|
||||
/// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
|
||||
size_t expectedExecutionsPerDeployment = 200;
|
||||
size_t expectedExecutionsPerDeployment = frontend::OptimiserSettings{}.expectedExecutionsPerDeployment;
|
||||
};
|
||||
|
||||
/// Modify and return the current assembly such that creation and execution gas usage
|
||||
@ -184,7 +187,16 @@ private:
|
||||
protected:
|
||||
/// 0 is reserved for exception
|
||||
unsigned m_usedTags = 1;
|
||||
std::map<std::string, size_t> m_namedTags;
|
||||
|
||||
struct NamedTagInfo
|
||||
{
|
||||
size_t id;
|
||||
std::optional<size_t> sourceID;
|
||||
size_t params;
|
||||
size_t returns;
|
||||
};
|
||||
|
||||
std::map<std::string, NamedTagInfo> m_namedTags;
|
||||
AssemblyItems m_items;
|
||||
std::map<util::h256, bytes> m_data;
|
||||
/// Data that is appended to the very end of the contract.
|
||||
|
@ -29,7 +29,6 @@
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
|
@ -71,9 +71,9 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
||||
m_state->storageContent().count(slot) &&
|
||||
classes.knownNonZero(m_state->storageContent().at(slot))
|
||||
))
|
||||
gas = GasCosts::sstoreResetGas; //@todo take refunds into account
|
||||
gas = GasCosts::totalSstoreResetGas(m_evmVersion); //@todo take refunds into account
|
||||
else
|
||||
gas = GasCosts::sstoreSetGas;
|
||||
gas = GasCosts::totalSstoreSetGas(m_evmVersion);
|
||||
break;
|
||||
}
|
||||
case Instruction::SLOAD:
|
||||
|
@ -18,6 +18,11 @@
|
||||
/** @file GasMeter.cpp
|
||||
* @author Christian <c@ethdev.com>
|
||||
* @date 2015
|
||||
*
|
||||
* Utilities for tracking gas costs.
|
||||
*
|
||||
* With respect to EIP-2929, we do not track warm accounts or storage slots and they are always
|
||||
* charged the worst-case, i.e., cold-access.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@ -47,19 +52,6 @@ namespace GasCosts
|
||||
static unsigned const tier5Gas = 10;
|
||||
static unsigned const tier6Gas = 20;
|
||||
static unsigned const tier7Gas = 0;
|
||||
inline unsigned extCodeGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 700 : 20;
|
||||
}
|
||||
inline unsigned balanceGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
if (_evmVersion >= langutil::EVMVersion::istanbul())
|
||||
return 700;
|
||||
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
|
||||
return 400;
|
||||
else
|
||||
return 20;
|
||||
}
|
||||
static unsigned const expGas = 10;
|
||||
inline unsigned expByteGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
@ -67,18 +59,65 @@ namespace GasCosts
|
||||
}
|
||||
static unsigned const keccak256Gas = 30;
|
||||
static unsigned const keccak256WordGas = 6;
|
||||
/// Corresponds to COLD_SLOAD_COST from EIP-2929
|
||||
static unsigned const coldSloadCost = 2100;
|
||||
/// Corresponds to COLD_ACCOUNT_ACCESS_COST from EIP-2929
|
||||
static unsigned const coldAccountAccessCost = 2600;
|
||||
/// Corresponds to WARM_STORAGE_READ_COST from EIP-2929
|
||||
static unsigned const warmStorageReadCost = 100;
|
||||
inline unsigned sloadGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
if (_evmVersion >= langutil::EVMVersion::istanbul())
|
||||
if (_evmVersion >= langutil::EVMVersion::berlin())
|
||||
return coldSloadCost;
|
||||
else if (_evmVersion >= langutil::EVMVersion::istanbul())
|
||||
return 800;
|
||||
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
|
||||
return 200;
|
||||
else
|
||||
return 50;
|
||||
}
|
||||
/// Corresponds to SSTORE_SET_GAS
|
||||
static unsigned const sstoreSetGas = 20000;
|
||||
static unsigned const sstoreResetGas = 5000;
|
||||
/// Corresponds to SSTORE_RESET_GAS from EIP-2929
|
||||
static unsigned const sstoreResetGas = 5000 - coldSloadCost;
|
||||
static unsigned const sstoreRefundGas = 15000;
|
||||
inline static unsigned totalSstoreSetGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
if (_evmVersion >= langutil::EVMVersion::berlin())
|
||||
return sstoreSetGas + coldSloadCost;
|
||||
else
|
||||
return sstoreSetGas;
|
||||
}
|
||||
/// Corresponds to SSTORE_RESET_GAS from EIP-2929
|
||||
/// For Berlin, the maximum is SSTORE_RESET_GAS + COLD_SLOAD_COST = 5000
|
||||
/// For previous versions, it's a fixed 5000
|
||||
inline unsigned totalSstoreResetGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
if (_evmVersion >= langutil::EVMVersion::berlin())
|
||||
return sstoreResetGas + coldSloadCost;
|
||||
else
|
||||
return 5000;
|
||||
}
|
||||
inline unsigned extCodeGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
if (_evmVersion >= langutil::EVMVersion::berlin())
|
||||
return coldAccountAccessCost;
|
||||
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
|
||||
return 700;
|
||||
else
|
||||
return 20;
|
||||
}
|
||||
inline unsigned balanceGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
if (_evmVersion >= langutil::EVMVersion::berlin())
|
||||
return coldAccountAccessCost;
|
||||
else if (_evmVersion >= langutil::EVMVersion::istanbul())
|
||||
return 700;
|
||||
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
|
||||
return 400;
|
||||
else
|
||||
return 20;
|
||||
}
|
||||
static unsigned const jumpdestGas = 1;
|
||||
static unsigned const logGas = 375;
|
||||
static unsigned const logDataGas = 8;
|
||||
@ -86,14 +125,24 @@ namespace GasCosts
|
||||
static unsigned const createGas = 32000;
|
||||
inline unsigned callGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 700 : 40;
|
||||
if (_evmVersion >= langutil::EVMVersion::berlin())
|
||||
return coldAccountAccessCost;
|
||||
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
|
||||
return 700;
|
||||
else
|
||||
return 40;
|
||||
}
|
||||
static unsigned const callStipend = 2300;
|
||||
static unsigned const callValueTransferGas = 9000;
|
||||
static unsigned const callNewAccountGas = 25000;
|
||||
inline unsigned selfdestructGas(langutil::EVMVersion _evmVersion)
|
||||
{
|
||||
return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 5000 : 0;
|
||||
if (_evmVersion >= langutil::EVMVersion::berlin())
|
||||
return coldAccountAccessCost;
|
||||
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
|
||||
return 5000;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
static unsigned const selfdestructRefundGas = 24000;
|
||||
static unsigned const memoryGas = 3;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolutil/Common.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <libevmasm/AssemblyItem.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
@ -75,7 +76,7 @@ private:
|
||||
|
||||
AssemblyItems& m_items;
|
||||
std::set<size_t> const& m_tagsReferencedFromOutside;
|
||||
size_t const m_runs = 200;
|
||||
size_t const m_runs = Assembly::OptimiserSettings{}.expectedExecutionsPerDeployment;
|
||||
bool const m_isCreation = false;
|
||||
langutil::EVMVersion const m_evmVersion;
|
||||
};
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include <libsolutil/Common.h>
|
||||
#include <libsolutil/CommonIO.h>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
|
@ -45,6 +45,17 @@ struct LinkerObject
|
||||
/// to a list of offsets into the bytecode that refer to their values.
|
||||
std::map<u256, std::pair<std::string, std::vector<size_t>>> immutableReferences;
|
||||
|
||||
struct FunctionDebugData
|
||||
{
|
||||
std::optional<size_t> bytecodeOffset;
|
||||
std::optional<size_t> sourceID;
|
||||
size_t params = {};
|
||||
size_t returns = {};
|
||||
};
|
||||
|
||||
/// Bytecode offsets of named tags like function entry points.
|
||||
std::map<std::string, FunctionDebugData> functionDebugData;
|
||||
|
||||
/// Appends the bytecode of @a _other and incorporates its link references.
|
||||
void append(LinkerObject const& _other);
|
||||
|
||||
|
@ -26,7 +26,6 @@
|
||||
|
||||
#include <libevmasm/ExpressionClasses.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||
#include <libevmasm/RuleList.h>
|
||||
#include <libsolutil/Assertions.h>
|
||||
|
||||
|
@ -99,7 +99,7 @@ private:
|
||||
|
||||
EVMVersion(Version _version): m_version(_version) {}
|
||||
|
||||
Version m_version = Version::Istanbul;
|
||||
Version m_version = Version::Berlin;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
|
@ -89,7 +89,7 @@ void ParserBase::expectToken(Token _value, bool _advance)
|
||||
_advance = false;
|
||||
}
|
||||
if (_advance)
|
||||
m_scanner->next();
|
||||
advance();
|
||||
}
|
||||
|
||||
void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentNodeName, bool _advance)
|
||||
@ -102,7 +102,7 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN
|
||||
SourceLocation errorLoc = currentLocation();
|
||||
int startPosition = errorLoc.start;
|
||||
while (m_scanner->currentToken() != _value && m_scanner->currentToken() != Token::EOS)
|
||||
m_scanner->next();
|
||||
advance();
|
||||
|
||||
string const expectedToken = ParserBase::tokenName(_value);
|
||||
if (m_scanner->currentToken() == Token::EOS)
|
||||
@ -126,7 +126,7 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN
|
||||
}
|
||||
|
||||
if (_advance)
|
||||
m_scanner->next();
|
||||
advance();
|
||||
}
|
||||
|
||||
void ParserBase::increaseRecursionDepth()
|
||||
|
@ -81,7 +81,7 @@ protected:
|
||||
Token peekNextToken() const;
|
||||
std::string tokenName(Token _token);
|
||||
std::string currentLiteral() const;
|
||||
Token advance();
|
||||
virtual Token advance();
|
||||
///@}
|
||||
|
||||
/// Increases the recursion depth and throws an exception if it is too deep.
|
||||
|
@ -62,7 +62,8 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace solidity::langutil {
|
||||
namespace solidity::langutil
|
||||
{
|
||||
|
||||
string to_string(ScannerError _errorCode)
|
||||
{
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
#include <liblangutil/CharStream.h>
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
|
@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
#include <liblangutil/SourceReferenceFormatter.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolutil/UTF8.h>
|
||||
#include <iomanip>
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <range/v3/view.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@ -53,8 +55,7 @@ void CHCSmtLib2Interface::reset()
|
||||
m_accumulatedOutput.clear();
|
||||
m_variables.clear();
|
||||
m_unhandledQueries.clear();
|
||||
if (m_queryTimeout)
|
||||
write("(set-option :timeout " + to_string(*m_queryTimeout) + ")");
|
||||
m_sortNames.clear();
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::registerRelation(Expression const& _expr)
|
||||
@ -64,27 +65,25 @@ void CHCSmtLib2Interface::registerRelation(Expression const& _expr)
|
||||
if (!m_variables.count(_expr.name))
|
||||
{
|
||||
auto fSort = dynamic_pointer_cast<FunctionSort>(_expr.sort);
|
||||
string domain = m_smtlib2->toSmtLibSort(fSort->domain);
|
||||
string domain = toSmtLibSort(fSort->domain);
|
||||
// Relations are predicates which have implicit codomain Bool.
|
||||
m_variables.insert(_expr.name);
|
||||
write(
|
||||
"(declare-rel |" +
|
||||
"(declare-fun |" +
|
||||
_expr.name +
|
||||
"| " +
|
||||
domain +
|
||||
")"
|
||||
" Bool)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::addRule(Expression const& _expr, std::string const& _name)
|
||||
void CHCSmtLib2Interface::addRule(Expression const& _expr, std::string const& /*_name*/)
|
||||
{
|
||||
write(
|
||||
"(rule (! " +
|
||||
"(assert\n(forall " + forall() + "\n" +
|
||||
m_smtlib2->toSExpr(_expr) +
|
||||
" :named " +
|
||||
_name +
|
||||
"))"
|
||||
"))\n\n"
|
||||
);
|
||||
}
|
||||
|
||||
@ -92,22 +91,29 @@ pair<CheckResult, CHCSolverInterface::CexGraph> CHCSmtLib2Interface::query(Expre
|
||||
{
|
||||
string accumulated{};
|
||||
swap(m_accumulatedOutput, accumulated);
|
||||
for (auto const& var: m_smtlib2->variables())
|
||||
declareVariable(var.first, var.second);
|
||||
solAssert(m_smtlib2, "");
|
||||
writeHeader();
|
||||
for (auto const& decl: m_smtlib2->userSorts() | ranges::views::values)
|
||||
write(decl);
|
||||
m_accumulatedOutput += accumulated;
|
||||
|
||||
string queryRule = "(assert\n(forall " + forall() + "\n" +
|
||||
"(=> " + _block.name + " false)"
|
||||
"))";
|
||||
string response = querySolver(
|
||||
m_accumulatedOutput +
|
||||
"\n(query " + _block.name + " :print-certificate true)"
|
||||
queryRule +
|
||||
"\n(check-sat)"
|
||||
);
|
||||
swap(m_accumulatedOutput, accumulated);
|
||||
|
||||
CheckResult result;
|
||||
// TODO proper parsing
|
||||
if (boost::starts_with(response, "sat\n"))
|
||||
result = CheckResult::SATISFIABLE;
|
||||
else if (boost::starts_with(response, "unsat\n"))
|
||||
if (boost::starts_with(response, "sat"))
|
||||
result = CheckResult::UNSATISFIABLE;
|
||||
else if (boost::starts_with(response, "unknown\n"))
|
||||
else if (boost::starts_with(response, "unsat"))
|
||||
result = CheckResult::SATISFIABLE;
|
||||
else if (boost::starts_with(response, "unknown"))
|
||||
result = CheckResult::UNKNOWN;
|
||||
else
|
||||
result = CheckResult::ERROR;
|
||||
@ -124,10 +130,46 @@ void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const
|
||||
else if (!m_variables.count(_name))
|
||||
{
|
||||
m_variables.insert(_name);
|
||||
write("(declare-var |" + _name + "| " + m_smtlib2->toSmtLibSort(*_sort) + ')');
|
||||
write("(declare-var |" + _name + "| " + toSmtLibSort(*_sort) + ')');
|
||||
}
|
||||
}
|
||||
|
||||
string CHCSmtLib2Interface::toSmtLibSort(Sort const& _sort)
|
||||
{
|
||||
if (!m_sortNames.count(&_sort))
|
||||
m_sortNames[&_sort] = m_smtlib2->toSmtLibSort(_sort);
|
||||
return m_sortNames.at(&_sort);
|
||||
}
|
||||
|
||||
string CHCSmtLib2Interface::toSmtLibSort(vector<SortPointer> const& _sorts)
|
||||
{
|
||||
string ssort("(");
|
||||
for (auto const& sort: _sorts)
|
||||
ssort += toSmtLibSort(*sort) + " ";
|
||||
ssort += ")";
|
||||
return ssort;
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::writeHeader()
|
||||
{
|
||||
if (m_queryTimeout)
|
||||
write("(set-option :timeout " + to_string(*m_queryTimeout) + ")");
|
||||
write("(set-logic HORN)\n");
|
||||
}
|
||||
|
||||
string CHCSmtLib2Interface::forall()
|
||||
{
|
||||
string vars("(");
|
||||
for (auto const& [name, sort]: m_smtlib2->variables())
|
||||
{
|
||||
solAssert(sort, "");
|
||||
if (sort->kind != Kind::Function)
|
||||
vars += " (" + name + " " + toSmtLibSort(*sort) + ")";
|
||||
}
|
||||
vars += ")";
|
||||
return vars;
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
smtAssert(_sort, "");
|
||||
@ -137,8 +179,8 @@ void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const
|
||||
{
|
||||
auto fSort = dynamic_pointer_cast<FunctionSort>(_sort);
|
||||
smtAssert(fSort->codomain, "");
|
||||
string domain = m_smtlib2->toSmtLibSort(fSort->domain);
|
||||
string codomain = m_smtlib2->toSmtLibSort(*fSort->codomain);
|
||||
string domain = toSmtLibSort(fSort->domain);
|
||||
string codomain = toSmtLibSort(*fSort->codomain);
|
||||
m_variables.insert(_name);
|
||||
write(
|
||||
"(declare-fun |" +
|
||||
|
@ -53,6 +53,12 @@ public:
|
||||
SMTLib2Interface* smtlib2Interface() const { return m_smtlib2.get(); }
|
||||
|
||||
private:
|
||||
std::string toSmtLibSort(Sort const& _sort);
|
||||
std::string toSmtLibSort(std::vector<SortPointer> const& _sort);
|
||||
|
||||
void writeHeader();
|
||||
std::string forall();
|
||||
|
||||
void declareFunction(std::string const& _name, SortPointer const& _sort);
|
||||
|
||||
void write(std::string _data);
|
||||
@ -70,6 +76,8 @@ private:
|
||||
std::vector<std::string> m_unhandledQueries;
|
||||
|
||||
frontend::ReadCallback::Callback m_smtCallback;
|
||||
|
||||
std::map<Sort const*, std::string> m_sortNames;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
|
||||
return arguments[0].andExpr(arguments[1]);
|
||||
else if (n == "or")
|
||||
return arguments[0].orExpr(arguments[1]);
|
||||
else if (n == "implies")
|
||||
else if (n == "=>")
|
||||
return m_context.mkExpr(CVC4::kind::IMPLIES, arguments[0], arguments[1]);
|
||||
else if (n == "=")
|
||||
return m_context.mkExpr(CVC4::kind::EQUAL, arguments[0], arguments[1]);
|
||||
@ -188,7 +188,7 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
|
||||
return m_context.mkExpr(CVC4::kind::MINUS, arguments[0], arguments[1]);
|
||||
else if (n == "*")
|
||||
return m_context.mkExpr(CVC4::kind::MULT, arguments[0], arguments[1]);
|
||||
else if (n == "/")
|
||||
else if (n == "div")
|
||||
return m_context.mkExpr(CVC4::kind::INTS_DIVISION_TOTAL, arguments[0], arguments[1]);
|
||||
else if (n == "mod")
|
||||
return m_context.mkExpr(CVC4::kind::INTS_MODULUS, arguments[0], arguments[1]);
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <range/v3/algorithm/find_if.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@ -173,7 +175,7 @@ string SMTLib2Interface::toSExpr(Expression const& _expr)
|
||||
sexpr += string("ite ") +
|
||||
"(= ((_ extract " + pos + " " + pos + ")" + arg + ") #b0) " +
|
||||
nat + " " +
|
||||
"(- (bvneg " + arg + "))";
|
||||
"(- (bv2nat (bvneg " + arg + ")))";
|
||||
}
|
||||
else if (_expr.name == "const_array")
|
||||
{
|
||||
@ -231,14 +233,15 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort)
|
||||
{
|
||||
auto const& tupleSort = dynamic_cast<TupleSort const&>(_sort);
|
||||
string tupleName = "|" + tupleSort.name + "|";
|
||||
if (!m_userSorts.count(tupleName))
|
||||
auto isName = [&](auto entry) { return entry.first == tupleName; };
|
||||
if (ranges::find_if(m_userSorts, isName) == m_userSorts.end())
|
||||
{
|
||||
m_userSorts.insert(tupleName);
|
||||
string decl("(declare-datatypes ((" + tupleName + " 0)) (((" + tupleName);
|
||||
smtAssert(tupleSort.members.size() == tupleSort.components.size(), "");
|
||||
for (unsigned i = 0; i < tupleSort.members.size(); ++i)
|
||||
decl += " (|" + tupleSort.members.at(i) + "| " + toSmtLibSort(*tupleSort.components.at(i)) + ")";
|
||||
decl += "))))";
|
||||
m_userSorts.emplace_back(tupleName, decl);
|
||||
write(decl);
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,8 @@ public:
|
||||
|
||||
std::map<std::string, SortPointer> variables() { return m_variables; }
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> const& userSorts() const { return m_userSorts; }
|
||||
|
||||
private:
|
||||
void declareFunction(std::string const& _name, SortPointer const& _sort);
|
||||
|
||||
@ -79,7 +81,12 @@ private:
|
||||
|
||||
std::vector<std::string> m_accumulatedOutput;
|
||||
std::map<std::string, SortPointer> m_variables;
|
||||
std::set<std::string> m_userSorts;
|
||||
|
||||
/// Each pair in this vector represents an SMTChecker created
|
||||
/// sort (a user sort), and the smtlib2 declaration of that sort.
|
||||
/// It needs to be a vector so that the declaration order is kept,
|
||||
/// otherwise solvers cannot parse the queries.
|
||||
std::vector<std::pair<std::string, std::string>> m_userSorts;
|
||||
|
||||
std::map<util::h256, std::string> m_queryResponses;
|
||||
std::vector<std::string> m_unhandledQueries;
|
||||
|
@ -64,8 +64,20 @@ public:
|
||||
name(std::move(_name)), arguments(std::move(_arguments)), sort(std::move(_sort)) {}
|
||||
Expression(size_t _number): Expression(std::to_string(_number), {}, SortProvider::sintSort) {}
|
||||
Expression(u256 const& _number): Expression(_number.str(), {}, SortProvider::sintSort) {}
|
||||
Expression(s256 const& _number): Expression(_number.str(), {}, SortProvider::sintSort) {}
|
||||
Expression(bigint const& _number): Expression(_number.str(), {}, SortProvider::sintSort) {}
|
||||
Expression(s256 const& _number): Expression(
|
||||
_number >= 0 ? _number.str() : "-",
|
||||
_number >= 0 ?
|
||||
std::vector<Expression>{} :
|
||||
std::vector<Expression>{Expression(size_t(0)), bigint(-_number)},
|
||||
SortProvider::sintSort
|
||||
) {}
|
||||
Expression(bigint const& _number): Expression(
|
||||
_number >= 0 ? _number.str() : "-",
|
||||
_number >= 0 ?
|
||||
std::vector<Expression>{} :
|
||||
std::vector<Expression>{Expression(size_t(0)), bigint(-_number)},
|
||||
SortProvider::sintSort
|
||||
) {}
|
||||
|
||||
Expression(Expression const&) = default;
|
||||
Expression(Expression&&) = default;
|
||||
@ -86,7 +98,7 @@ public:
|
||||
{"not", 1},
|
||||
{"and", 2},
|
||||
{"or", 2},
|
||||
{"implies", 2},
|
||||
{"=>", 2},
|
||||
{"=", 2},
|
||||
{"<", 2},
|
||||
{"<=", 2},
|
||||
@ -95,7 +107,7 @@ public:
|
||||
{"+", 2},
|
||||
{"-", 2},
|
||||
{"*", 2},
|
||||
{"/", 2},
|
||||
{"div", 2},
|
||||
{"mod", 2},
|
||||
{"bvnot", 1},
|
||||
{"bvand", 2},
|
||||
@ -126,7 +138,7 @@ public:
|
||||
static Expression implies(Expression _a, Expression _b)
|
||||
{
|
||||
return Expression(
|
||||
"implies",
|
||||
"=>",
|
||||
std::move(_a),
|
||||
std::move(_b),
|
||||
Kind::Bool
|
||||
@ -300,7 +312,7 @@ public:
|
||||
friend Expression operator/(Expression _a, Expression _b)
|
||||
{
|
||||
auto intSort = _a.sort;
|
||||
return Expression("/", {std::move(_a), std::move(_b)}, intSort);
|
||||
return Expression("div", {std::move(_a), std::move(_b)}, intSort);
|
||||
}
|
||||
friend Expression operator%(Expression _a, Expression _b)
|
||||
{
|
||||
|
@ -189,7 +189,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
||||
return arguments[0] && arguments[1];
|
||||
else if (n == "or")
|
||||
return arguments[0] || arguments[1];
|
||||
else if (n == "implies")
|
||||
else if (n == "=>")
|
||||
return z3::implies(arguments[0], arguments[1]);
|
||||
else if (n == "=")
|
||||
return arguments[0] == arguments[1];
|
||||
@ -207,7 +207,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
||||
return arguments[0] - arguments[1];
|
||||
else if (n == "*")
|
||||
return arguments[0] * arguments[1];
|
||||
else if (n == "/")
|
||||
else if (n == "div")
|
||||
return arguments[0] / arguments[1];
|
||||
else if (n == "mod")
|
||||
return z3::mod(arguments[0], arguments[1]);
|
||||
|
@ -25,8 +25,6 @@
|
||||
#include <libsolidity/interface/StandardCompiler.h>
|
||||
#include <libsolidity/interface/Version.h>
|
||||
#include <libyul/YulString.h>
|
||||
#include <libsolutil/Common.h>
|
||||
#include <libsolutil/JSON.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <list>
|
||||
|
@ -10,6 +10,8 @@ set(sources
|
||||
analysis/ControlFlowBuilder.h
|
||||
analysis/ControlFlowGraph.cpp
|
||||
analysis/ControlFlowGraph.h
|
||||
analysis/ControlFlowRevertPruner.cpp
|
||||
analysis/ControlFlowRevertPruner.h
|
||||
analysis/DeclarationContainer.cpp
|
||||
analysis/DeclarationContainer.h
|
||||
analysis/DeclarationTypeChecker.cpp
|
||||
|
@ -86,6 +86,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract)
|
||||
|
||||
checkDuplicateFunctions(_contract);
|
||||
checkDuplicateEvents(_contract);
|
||||
checkReceiveFunction(_contract);
|
||||
m_overrideChecker.check(_contract);
|
||||
checkBaseConstructorArguments(_contract);
|
||||
checkAbstractDefinitions(_contract);
|
||||
@ -162,6 +163,35 @@ void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contr
|
||||
findDuplicateDefinitions(events);
|
||||
}
|
||||
|
||||
void ContractLevelChecker::checkReceiveFunction(ContractDefinition const& _contract)
|
||||
{
|
||||
for (FunctionDefinition const* function: _contract.definedFunctions())
|
||||
{
|
||||
solAssert(function, "");
|
||||
if (function->isReceive())
|
||||
{
|
||||
if (function->libraryFunction())
|
||||
m_errorReporter.declarationError(4549_error, function->location(), "Libraries cannot have receive ether functions.");
|
||||
|
||||
if (function->stateMutability() != StateMutability::Payable)
|
||||
m_errorReporter.declarationError(
|
||||
7793_error,
|
||||
function->location(),
|
||||
"Receive ether function must be payable, but is \"" +
|
||||
stateMutabilityToString(function->stateMutability()) +
|
||||
"\"."
|
||||
);
|
||||
if (function->visibility() != Visibility::External)
|
||||
m_errorReporter.declarationError(4095_error, function->location(), "Receive ether function must be defined as \"external\".");
|
||||
|
||||
if (!function->returnParameters().empty())
|
||||
m_errorReporter.fatalDeclarationError(6899_error, function->returnParameterList()->location(), "Receive ether function cannot return values.");
|
||||
if (!function->parameters().empty())
|
||||
m_errorReporter.fatalDeclarationError(6857_error, function->parameterList().location(), "Receive ether function cannot take parameters.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions)
|
||||
{
|
||||
|
@ -63,6 +63,7 @@ private:
|
||||
/// arguments and that there is at most one constructor.
|
||||
void checkDuplicateFunctions(ContractDefinition const& _contract);
|
||||
void checkDuplicateEvents(ContractDefinition const& _contract);
|
||||
void checkReceiveFunction(ContractDefinition const& _contract);
|
||||
template <class T>
|
||||
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions);
|
||||
/// Checks for unimplemented functions and modifiers.
|
||||
|
@ -22,28 +22,45 @@
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <boost/range/algorithm/sort.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::placeholders;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
bool ControlFlowAnalyzer::analyze(ASTNode const& _astRoot)
|
||||
|
||||
bool ControlFlowAnalyzer::run()
|
||||
{
|
||||
_astRoot.accept(*this);
|
||||
for (auto& [pair, flow]: m_cfg.allFunctionFlows())
|
||||
analyze(*pair.function, pair.contract, *flow);
|
||||
|
||||
return Error::containsOnlyWarnings(m_errorReporter.errors());
|
||||
}
|
||||
|
||||
bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function)
|
||||
void ControlFlowAnalyzer::analyze(FunctionDefinition const& _function, ContractDefinition const* _contract, FunctionFlow const& _flow)
|
||||
{
|
||||
if (_function.isImplemented())
|
||||
{
|
||||
auto const& functionFlow = m_cfg.functionFlow(_function);
|
||||
checkUninitializedAccess(functionFlow.entry, functionFlow.exit, _function.body().statements().empty());
|
||||
checkUnreachable(functionFlow.entry, functionFlow.exit, functionFlow.revert, functionFlow.transactionReturn);
|
||||
}
|
||||
return false;
|
||||
if (!_function.isImplemented())
|
||||
return;
|
||||
|
||||
optional<string> mostDerivedContractName;
|
||||
|
||||
// The name of the most derived contract only required if it differs from
|
||||
// the functions contract
|
||||
if (_contract && _contract != _function.annotation().contract)
|
||||
mostDerivedContractName = _contract->name();
|
||||
|
||||
checkUninitializedAccess(
|
||||
_flow.entry,
|
||||
_flow.exit,
|
||||
_function.body().statements().empty(),
|
||||
mostDerivedContractName
|
||||
);
|
||||
checkUnreachable(_flow.entry, _flow.exit, _flow.revert, _flow.transactionReturn);
|
||||
}
|
||||
|
||||
void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit, bool _emptyBody) const
|
||||
|
||||
void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit, bool _emptyBody, optional<string> _contractName)
|
||||
{
|
||||
struct NodeInfo
|
||||
{
|
||||
@ -156,16 +173,27 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
|
||||
" without prior assignment, which would lead to undefined behaviour."
|
||||
);
|
||||
else if (!_emptyBody && varDecl.name().empty())
|
||||
{
|
||||
if (!m_unassignedReturnVarsAlreadyWarnedFor.emplace(&varDecl).second)
|
||||
continue;
|
||||
|
||||
m_errorReporter.warning(
|
||||
6321_error,
|
||||
varDecl.location(),
|
||||
"Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable."
|
||||
"Unnamed return variable can remain unassigned" +
|
||||
(
|
||||
_contractName.has_value() ?
|
||||
" when the function is called when \"" + _contractName.value() + "\" is the most derived contract." :
|
||||
"."
|
||||
) +
|
||||
" Add an explicit return with value to all non-reverting code paths or name the variable."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert, CFGNode const* _transactionReturn) const
|
||||
void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert, CFGNode const* _transactionReturn)
|
||||
{
|
||||
// collect all nodes reachable from the entry point
|
||||
std::set<CFGNode const*> reachable = util::BreadthFirstSearch<CFGNode const*>{{_entry}}.run(
|
||||
@ -193,6 +221,8 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const*
|
||||
// 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(5740_error, location, "Unreachable code.");
|
||||
|
||||
if (m_unreachableLocationsAlreadyWarnedFor.emplace(location).second)
|
||||
m_errorReporter.warning(5740_error, location, "Unreachable code.");
|
||||
}
|
||||
}
|
||||
|
@ -19,30 +19,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <set>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
class ControlFlowAnalyzer: private ASTConstVisitor
|
||||
class ControlFlowAnalyzer
|
||||
{
|
||||
public:
|
||||
explicit ControlFlowAnalyzer(CFG const& _cfg, langutil::ErrorReporter& _errorReporter):
|
||||
m_cfg(_cfg), m_errorReporter(_errorReporter) {}
|
||||
|
||||
bool analyze(ASTNode const& _astRoot);
|
||||
|
||||
bool visit(FunctionDefinition const& _function) override;
|
||||
bool run();
|
||||
|
||||
private:
|
||||
void analyze(FunctionDefinition const& _function, ContractDefinition const* _contract, FunctionFlow const& _flow);
|
||||
/// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit.
|
||||
void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit, bool _emptyBody) const;
|
||||
/// @param _entry entry node
|
||||
/// @param _exit exit node
|
||||
/// @param _emptyBody whether the body of the function is empty (true) or not (false)
|
||||
/// @param _contractName name of the most derived contract, should be empty
|
||||
/// if the function is also defined in it
|
||||
void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit, bool _emptyBody, std::optional<std::string> _contractName = {});
|
||||
/// Checks for unreachable code, i.e. code ending in @param _exit, @param _revert or @param _transactionReturn
|
||||
/// that can not be reached from @param _entry.
|
||||
void checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert, CFGNode const* _transactionReturn) const;
|
||||
void checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert, CFGNode const* _transactionReturn);
|
||||
|
||||
CFG const& m_cfg;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
|
||||
std::set<langutil::SourceLocation> m_unreachableLocationsAlreadyWarnedFor;
|
||||
std::set<VariableDeclaration const*> m_unassignedReturnVarsAlreadyWarnedFor;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -260,6 +260,7 @@ bool ControlFlowBuilder::visit(PlaceholderStatement const&)
|
||||
|
||||
bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
||||
{
|
||||
solAssert(!!m_revertNode, "");
|
||||
solAssert(!!m_currentNode, "");
|
||||
solAssert(!!_functionCall.expression().annotation().type, "");
|
||||
|
||||
@ -267,26 +268,44 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
||||
switch (functionType->kind())
|
||||
{
|
||||
case FunctionType::Kind::Revert:
|
||||
solAssert(!!m_revertNode, "");
|
||||
visitNode(_functionCall);
|
||||
_functionCall.expression().accept(*this);
|
||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||
|
||||
connect(m_currentNode, m_revertNode);
|
||||
|
||||
m_currentNode = newLabel();
|
||||
return false;
|
||||
case FunctionType::Kind::Require:
|
||||
case FunctionType::Kind::Assert:
|
||||
{
|
||||
solAssert(!!m_revertNode, "");
|
||||
visitNode(_functionCall);
|
||||
_functionCall.expression().accept(*this);
|
||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||
|
||||
connect(m_currentNode, m_revertNode);
|
||||
|
||||
auto nextNode = newLabel();
|
||||
|
||||
connect(m_currentNode, nextNode);
|
||||
m_currentNode = nextNode;
|
||||
return false;
|
||||
}
|
||||
case FunctionType::Kind::Internal:
|
||||
{
|
||||
visitNode(_functionCall);
|
||||
_functionCall.expression().accept(*this);
|
||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||
|
||||
m_currentNode->functionCalls.emplace_back(&_functionCall);
|
||||
|
||||
auto nextNode = newLabel();
|
||||
|
||||
connect(m_currentNode, nextNode);
|
||||
m_currentNode = nextNode;
|
||||
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -303,7 +322,7 @@ bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
|
||||
_modifierInvocation.name().annotation().referencedDeclaration
|
||||
);
|
||||
if (!modifierDefinition) return false;
|
||||
solAssert(!!modifierDefinition, "");
|
||||
if (!modifierDefinition->isImplemented()) return false;
|
||||
solAssert(!!m_returnNode, "");
|
||||
|
||||
m_placeholderEntry = newLabel();
|
||||
@ -482,7 +501,7 @@ void ControlFlowBuilder::operator()(yul::Identifier const& _identifier)
|
||||
m_currentNode->variableOccurrences.emplace_back(
|
||||
*declaration,
|
||||
VariableOccurrence::Kind::Access,
|
||||
_identifier.location
|
||||
_identifier.debugData->location
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -498,7 +517,7 @@ void ControlFlowBuilder::operator()(yul::Assignment const& _assignment)
|
||||
m_currentNode->variableOccurrences.emplace_back(
|
||||
*declaration,
|
||||
VariableOccurrence::Kind::Assignment,
|
||||
variable.location
|
||||
variable.debugData->location
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -26,11 +26,11 @@
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
namespace solidity::frontend {
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
/** Helper class that builds the control flow of a function or modifier.
|
||||
* Modifiers are not yet applied to the functions. This is done in a second
|
||||
* step in the CFG class.
|
||||
/**
|
||||
* Helper class that builds the control flow of a function or modifier.
|
||||
*/
|
||||
class ControlFlowBuilder: private ASTConstVisitor, private yul::ASTWalker
|
||||
{
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
@ -34,15 +33,25 @@ bool CFG::constructFlow(ASTNode const& _astRoot)
|
||||
|
||||
bool CFG::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
if (_function.isImplemented())
|
||||
m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
|
||||
if (_function.isImplemented() && _function.isFree())
|
||||
m_functionControlFlow[{nullptr, &_function}] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
|
||||
return false;
|
||||
}
|
||||
|
||||
FunctionFlow const& CFG::functionFlow(FunctionDefinition const& _function) const
|
||||
bool CFG::visit(ContractDefinition const& _contract)
|
||||
{
|
||||
solAssert(m_functionControlFlow.count(&_function), "");
|
||||
return *m_functionControlFlow.find(&_function)->second;
|
||||
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
|
||||
for (FunctionDefinition const* function: contract->definedFunctions())
|
||||
if (function->isImplemented())
|
||||
m_functionControlFlow[{&_contract, function}] =
|
||||
ControlFlowBuilder::createFunctionFlow(m_nodeContainer, *function);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FunctionFlow const& CFG::functionFlow(FunctionDefinition const& _function, ContractDefinition const* _contract) const
|
||||
{
|
||||
return *m_functionControlFlow.at({_contract, &_function});
|
||||
}
|
||||
|
||||
CFGNode* CFG::NodeContainer::newNode()
|
||||
|
@ -98,6 +98,8 @@ struct CFGNode
|
||||
std::vector<CFGNode*> entries;
|
||||
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
|
||||
std::vector<CFGNode*> exits;
|
||||
/// Function calls done by this node
|
||||
std::vector<FunctionCall const*> functionCalls;
|
||||
|
||||
/// Variable occurrences in the node.
|
||||
std::vector<VariableOccurrence> variableOccurrences;
|
||||
@ -118,7 +120,7 @@ struct FunctionFlow
|
||||
/// (e.g. all return statements of the function).
|
||||
CFGNode* exit = nullptr;
|
||||
/// Revert node. Control flow of the function in case of revert.
|
||||
/// This node is empty does not have any exits, but may have multiple entries
|
||||
/// This node is empty and does not have any exits, but may have multiple entries
|
||||
/// (e.g. all assert, require, revert and throw statements).
|
||||
CFGNode* revert = nullptr;
|
||||
/// Transaction return node. Destination node for inline assembly "return" calls.
|
||||
@ -130,13 +132,37 @@ struct FunctionFlow
|
||||
class CFG: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
struct FunctionContractTuple
|
||||
{
|
||||
ContractDefinition const* contract = nullptr;
|
||||
FunctionDefinition const* function = nullptr;
|
||||
|
||||
// Use AST ids for comparison to keep a deterministic order in the
|
||||
// containers using this struct
|
||||
bool operator<(FunctionContractTuple const& _other) const
|
||||
{
|
||||
return
|
||||
std::make_pair(contract ? contract->id() : -1, function->id()) <
|
||||
std::make_pair(_other.contract ? _other.contract->id() : -1, _other.function->id());
|
||||
}
|
||||
};
|
||||
explicit CFG(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
|
||||
|
||||
bool constructFlow(ASTNode const& _astRoot);
|
||||
|
||||
bool visit(FunctionDefinition const& _function) override;
|
||||
bool visit(ContractDefinition const& _contract) override;
|
||||
|
||||
FunctionFlow const& functionFlow(FunctionDefinition const& _function) const;
|
||||
/// Get the function flow for the given function, using `_contract` as the
|
||||
/// most derived contract
|
||||
/// @param _function function to find the function flow for
|
||||
/// @param _contract most derived contract or nullptr for free functions
|
||||
FunctionFlow const& functionFlow(FunctionDefinition const& _function, ContractDefinition const* _contract = nullptr) const;
|
||||
|
||||
std::map<FunctionContractTuple, std::unique_ptr<FunctionFlow>> const& allFunctionFlows() const
|
||||
{
|
||||
return m_functionControlFlow;
|
||||
}
|
||||
|
||||
class NodeContainer
|
||||
{
|
||||
@ -153,7 +179,7 @@ private:
|
||||
/// are owned by the CFG class and stored in this container.
|
||||
NodeContainer m_nodeContainer;
|
||||
|
||||
std::map<FunctionDefinition const*, std::unique_ptr<FunctionFlow>> m_functionControlFlow;
|
||||
std::map<FunctionContractTuple, std::unique_ptr<FunctionFlow>> m_functionControlFlow;
|
||||
};
|
||||
|
||||
}
|
||||
|
171
libsolidity/analysis/ControlFlowRevertPruner.cpp
Normal file
171
libsolidity/analysis/ControlFlowRevertPruner.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowRevertPruner.h>
|
||||
|
||||
#include <libsolutil/Algorithms.h>
|
||||
|
||||
#include <range/v3/algorithm/remove.hpp>
|
||||
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/// Find the right scope for the called function: When calling a base function,
|
||||
/// we keep the most derived, but we use the called contract in case it is a
|
||||
/// library function or nullptr for a free function.
|
||||
ContractDefinition const* findScopeContract(FunctionDefinition const& _function, ContractDefinition const* _callingContract)
|
||||
{
|
||||
if (auto const* functionContract = _function.annotation().contract)
|
||||
{
|
||||
if (_callingContract && _callingContract->derivesFrom(*functionContract))
|
||||
return _callingContract;
|
||||
else
|
||||
return functionContract;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ControlFlowRevertPruner::run()
|
||||
{
|
||||
for (auto& [pair, flow]: m_cfg.allFunctionFlows())
|
||||
m_functions[pair] = RevertState::Unknown;
|
||||
|
||||
findRevertStates();
|
||||
modifyFunctionFlows();
|
||||
}
|
||||
|
||||
void ControlFlowRevertPruner::findRevertStates()
|
||||
{
|
||||
std::set<CFG::FunctionContractTuple> pendingFunctions = keys(m_functions);
|
||||
// We interrupt the search whenever we encounter a call to a function with (yet) unknown
|
||||
// revert behaviour. The ``wakeUp`` data structure contains information about which
|
||||
// searches to restart once we know about the behaviour.
|
||||
std::map<CFG::FunctionContractTuple, std::set<CFG::FunctionContractTuple>> wakeUp;
|
||||
|
||||
while (!pendingFunctions.empty())
|
||||
{
|
||||
CFG::FunctionContractTuple item = *pendingFunctions.begin();
|
||||
pendingFunctions.erase(pendingFunctions.begin());
|
||||
|
||||
if (m_functions[item] != RevertState::Unknown)
|
||||
continue;
|
||||
|
||||
bool foundExit = false;
|
||||
bool foundUnknown = false;
|
||||
|
||||
FunctionFlow const& functionFlow = m_cfg.functionFlow(*item.function, item.contract);
|
||||
|
||||
solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run(
|
||||
[&](CFGNode* _node, auto&& _addChild) {
|
||||
if (_node == functionFlow.exit)
|
||||
foundExit = true;
|
||||
|
||||
for (auto const* functionCall: _node->functionCalls)
|
||||
{
|
||||
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.contract);
|
||||
|
||||
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
||||
continue;
|
||||
|
||||
CFG::FunctionContractTuple calledFunctionTuple{
|
||||
findScopeContract(*resolvedFunction, item.contract),
|
||||
resolvedFunction
|
||||
};
|
||||
switch (m_functions.at(calledFunctionTuple))
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
wakeUp[calledFunctionTuple].insert(item);
|
||||
foundUnknown = true;
|
||||
return;
|
||||
case RevertState::AllPathsRevert:
|
||||
return;
|
||||
case RevertState::HasNonRevertingPath:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (CFGNode* exit: _node->exits)
|
||||
_addChild(exit);
|
||||
}
|
||||
);
|
||||
|
||||
auto& revertState = m_functions[item];
|
||||
|
||||
if (foundExit)
|
||||
revertState = RevertState::HasNonRevertingPath;
|
||||
else if (!foundUnknown)
|
||||
revertState = RevertState::AllPathsRevert;
|
||||
|
||||
if (revertState != RevertState::Unknown && wakeUp.count(item))
|
||||
{
|
||||
// Restart all searches blocked by this function.
|
||||
for (CFG::FunctionContractTuple const& nextItem: wakeUp[item])
|
||||
if (m_functions.at(nextItem) == RevertState::Unknown)
|
||||
pendingFunctions.insert(nextItem);
|
||||
wakeUp.erase(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControlFlowRevertPruner::modifyFunctionFlows()
|
||||
{
|
||||
for (auto& item: m_functions)
|
||||
{
|
||||
FunctionFlow const& functionFlow = m_cfg.functionFlow(*item.first.function, item.first.contract);
|
||||
solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run(
|
||||
[&](CFGNode* _node, auto&& _addChild) {
|
||||
for (auto const* functionCall: _node->functionCalls)
|
||||
{
|
||||
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.first.contract);
|
||||
|
||||
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
||||
continue;
|
||||
|
||||
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
[[fallthrough]];
|
||||
case RevertState::AllPathsRevert:
|
||||
// If the revert states of the functions do not
|
||||
// change anymore, we treat all "unknown" states as
|
||||
// "reverting", since they can only be caused by
|
||||
// recursion.
|
||||
for (CFGNode * node: _node->exits)
|
||||
ranges::remove(node->entries, _node);
|
||||
|
||||
_node->exits = {functionFlow.revert};
|
||||
functionFlow.revert->entries.push_back(_node);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (CFGNode* exit: _node->exits)
|
||||
_addChild(exit);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
libsolidity/analysis/ControlFlowRevertPruner.h
Normal file
66
libsolidity/analysis/ControlFlowRevertPruner.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||
|
||||
#include <libsolutil/Algorithms.h>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
/**
|
||||
* Analyses all function flows and recursively removes all exit edges from CFG
|
||||
* nodes that make function calls that will always revert.
|
||||
*/
|
||||
class ControlFlowRevertPruner
|
||||
{
|
||||
public:
|
||||
ControlFlowRevertPruner(CFG& _cfg): m_cfg(_cfg) {}
|
||||
|
||||
void run();
|
||||
private:
|
||||
/// Possible revert states of a function call
|
||||
enum class RevertState
|
||||
{
|
||||
AllPathsRevert,
|
||||
HasNonRevertingPath,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
/// Identify revert states of all function flows
|
||||
void findRevertStates();
|
||||
|
||||
/// Modify function flows so that edges with reverting function calls are removed
|
||||
void modifyFunctionFlows();
|
||||
|
||||
/// Control Flow Graph object.
|
||||
CFG& m_cfg;
|
||||
|
||||
/// function/contract pairs mapped to their according revert state
|
||||
std::map<CFG::FunctionContractTuple, RevertState> m_functions;
|
||||
|
||||
std::map<
|
||||
std::tuple<FunctionCall const*, ContractDefinition const*>,
|
||||
FunctionDefinition const*
|
||||
> m_resolveCache;
|
||||
|
||||
};
|
||||
}
|
@ -24,9 +24,11 @@
|
||||
#include <libsolidity/analysis/DeclarationContainer.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#include <range/v3/view/filter.hpp>
|
||||
#include <range/v3/range/conversion.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::frontend;
|
||||
@ -121,11 +123,7 @@ bool DeclarationContainer::registerDeclaration(
|
||||
if (conflictingDeclaration(_declaration, _name))
|
||||
return false;
|
||||
|
||||
// Do not warn about shadowing for structs and enums because their members are
|
||||
// not accessible without prefixes. Also do not warn about event parameters
|
||||
// because they do not participate in any proper scope.
|
||||
bool special = _declaration.scope() && (_declaration.isStructMember() || _declaration.isEnumValue() || _declaration.isEventOrErrorParameter());
|
||||
if (m_enclosingContainer && !special)
|
||||
if (m_enclosingContainer && _declaration.isVisibleAsUnqualifiedName())
|
||||
m_homonymCandidates.emplace_back(*_name, _location ? _location : &_declaration.location());
|
||||
}
|
||||
|
||||
@ -144,16 +142,35 @@ bool DeclarationContainer::registerDeclaration(
|
||||
return registerDeclaration(_declaration, nullptr, nullptr, _invisible, _update);
|
||||
}
|
||||
|
||||
vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const
|
||||
vector<Declaration const*> DeclarationContainer::resolveName(
|
||||
ASTString const& _name,
|
||||
bool _recursive,
|
||||
bool _alsoInvisible,
|
||||
bool _onlyVisibleAsUnqualifiedNames
|
||||
) const
|
||||
{
|
||||
solAssert(!_name.empty(), "Attempt to resolve empty name.");
|
||||
vector<Declaration const*> result;
|
||||
|
||||
if (m_declarations.count(_name))
|
||||
result = m_declarations.at(_name);
|
||||
{
|
||||
if (_onlyVisibleAsUnqualifiedNames)
|
||||
result += m_declarations.at(_name) | ranges::views::filter(&Declaration::isVisibleAsUnqualifiedName) | ranges::to_vector;
|
||||
else
|
||||
result += m_declarations.at(_name);
|
||||
}
|
||||
|
||||
if (_alsoInvisible && m_invisibleDeclarations.count(_name))
|
||||
result += m_invisibleDeclarations.at(_name);
|
||||
{
|
||||
if (_onlyVisibleAsUnqualifiedNames)
|
||||
result += m_invisibleDeclarations.at(_name) | ranges::views::filter(&Declaration::isVisibleAsUnqualifiedName) | ranges::to_vector;
|
||||
else
|
||||
result += m_invisibleDeclarations.at(_name);
|
||||
}
|
||||
|
||||
if (result.empty() && _recursive && m_enclosingContainer)
|
||||
result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible);
|
||||
result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible, _onlyVisibleAsUnqualifiedNames);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user