mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5416 from ethereum/develop
Merge develop into release for 0.5.0
This commit is contained in:
commit
1d4f565a64
@ -14,7 +14,7 @@ defaults:
|
||||
command: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKE_OPTIONS
|
||||
make -j4
|
||||
- run_tests: &run_tests
|
||||
name: Tests
|
||||
@ -122,6 +122,7 @@ jobs:
|
||||
- image: buildpack-deps:artful
|
||||
environment:
|
||||
TERM: xterm
|
||||
CMAKE_OPTIONS: -DCOVERAGE=OFF
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@ -129,14 +130,41 @@ jobs:
|
||||
command: |
|
||||
apt-get -qq update
|
||||
apt-get -qy install cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev
|
||||
./scripts/install_obsolete_jsoncpp_1_7_4.sh
|
||||
- run: *setup_prerelease_commit_hash
|
||||
- run: *run_build
|
||||
- store_artifacts: *solc_artifact
|
||||
- persist_to_workspace: *all_artifacts
|
||||
- persist_to_workspace:
|
||||
root: build
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
build_x86_clang7:
|
||||
docker:
|
||||
- image: buildpack-deps:cosmic
|
||||
environment:
|
||||
TERM: xterm
|
||||
CC: /usr/bin/clang-7
|
||||
CXX: /usr/bin/clang++-7
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install build dependencies
|
||||
command: |
|
||||
apt-get -qq update
|
||||
apt-get -qy install clang-7 cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev
|
||||
./scripts/install_obsolete_jsoncpp_1_7_4.sh
|
||||
- run: *setup_prerelease_commit_hash
|
||||
- run: *run_build
|
||||
- store_artifacts: *solc_artifact
|
||||
- persist_to_workspace:
|
||||
root: build
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
build_x86_mac:
|
||||
macos:
|
||||
xcode: "9.0"
|
||||
xcode: "10.0.0"
|
||||
environment:
|
||||
TERM: xterm
|
||||
steps:
|
||||
@ -150,11 +178,39 @@ jobs:
|
||||
brew install z3
|
||||
brew install boost
|
||||
brew install cmake
|
||||
brew install wget
|
||||
./scripts/install_obsolete_jsoncpp_1_7_4.sh
|
||||
- run: *setup_prerelease_commit_hash
|
||||
- run: *run_build
|
||||
- store_artifacts: *solc_artifact
|
||||
- persist_to_workspace: *all_artifacts
|
||||
|
||||
test_check_spelling:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
environment:
|
||||
TERM: xterm
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: build
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
pip install --user codespell
|
||||
- run:
|
||||
name: Check spelling
|
||||
command: ~/.local/bin/codespell -S "*.enc,.git" -I ./scripts/codespell_whitelist.txt
|
||||
|
||||
test_check_style:
|
||||
docker:
|
||||
- image: buildpack-deps:artful
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Check for trailing whitespace
|
||||
command: ./scripts/check_style.sh
|
||||
|
||||
test_buglist:
|
||||
docker:
|
||||
- image: circleci/node
|
||||
@ -185,15 +241,28 @@ jobs:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
apt-get -qq update
|
||||
apt-get -qy install libz3-dev libleveldb1v5
|
||||
apt-get -qy install libz3-dev libleveldb1v5 python-pip
|
||||
pip install codecov
|
||||
- run: mkdir -p test_results
|
||||
- run:
|
||||
name: Test type checker
|
||||
command: build/test/soltest -t 'syntaxTest*' -- --no-ipc --testpath test
|
||||
- run:
|
||||
name: Coverage of type checker
|
||||
command: codecov --flags syntax --gcov-root build
|
||||
- run: *run_tests
|
||||
- run:
|
||||
name: Coverage of all
|
||||
command: codecov --flags all --gcov-root build
|
||||
- store_test_results:
|
||||
path: test_results/
|
||||
- store_artifacts:
|
||||
path: test_results/
|
||||
destination: test_results/
|
||||
|
||||
test_x86_mac:
|
||||
macos:
|
||||
xcode: "9.0"
|
||||
xcode: "10.0.0"
|
||||
environment:
|
||||
TERM: xterm
|
||||
steps:
|
||||
@ -211,6 +280,9 @@ jobs:
|
||||
- run: *run_tests
|
||||
- store_test_results:
|
||||
path: test_results/
|
||||
- store_artifacts:
|
||||
path: test_results/
|
||||
destination: test_results/
|
||||
|
||||
docs:
|
||||
docker:
|
||||
@ -221,7 +293,7 @@ jobs:
|
||||
name: Install build dependencies
|
||||
command: |
|
||||
apt-get -qq update
|
||||
apt-get -qy install python-sphinx
|
||||
apt-get -qy install python-sphinx python-pip
|
||||
- run: *setup_prerelease_commit_hash
|
||||
- run:
|
||||
name: Build documentation
|
||||
@ -234,6 +306,8 @@ workflows:
|
||||
version: 2
|
||||
build_all:
|
||||
jobs:
|
||||
- test_check_spelling: *build_on_tags
|
||||
- test_check_style: *build_on_tags
|
||||
- test_buglist: *build_on_tags
|
||||
- build_emscripten: *build_on_tags
|
||||
- test_emscripten_solcjs:
|
||||
@ -245,6 +319,7 @@ workflows:
|
||||
requires:
|
||||
- build_emscripten
|
||||
- build_x86_linux: *build_on_tags
|
||||
- build_x86_clang7: *build_on_tags
|
||||
- build_x86_mac: *build_on_tags
|
||||
- test_x86_linux:
|
||||
<<: *build_on_tags
|
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@ -0,0 +1,6 @@
|
||||
# out-of-tree builds usually go here. This helps improving performance of uploading
|
||||
# the build context to the docker image build server
|
||||
/build
|
||||
|
||||
# in-tree builds
|
||||
/deps
|
@ -8,6 +8,7 @@ trim_trailing_whitespace = true
|
||||
|
||||
[*.{cpp,h}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.{py,rst,sh,yml}]
|
||||
indent_style = space
|
||||
|
39
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Bug reports about the Solidity Compiler.
|
||||
---
|
||||
|
||||
<!--## Prerequisites
|
||||
|
||||
- First, many thanks for taking part in the community. We really appreciate that.
|
||||
- We realize there is a lot of information requested here. We ask only that you do your best to provide as much information as possible so we can better help you.
|
||||
- Support questions are better asked in one of the following locations:
|
||||
- [Solidity chat](https://gitter.im/ethereum/solidity)
|
||||
- [Stack Overflow](https://ethereum.stackexchange.com/)
|
||||
- Ensure the issue isn't already reported.
|
||||
- The issue should be reproducible with the latest solidity version; however, this isn't a hard requirement and being reproducible with an older version is sufficient.
|
||||
-->
|
||||
|
||||
## Description
|
||||
|
||||
<!--Please shortly describe the bug you have found, and what you expect instead.-->
|
||||
|
||||
## Environment
|
||||
|
||||
- Compiler version:
|
||||
- Framework/IDE (e.g. Truffle or Remix):
|
||||
- EVM execution environment / backend / blockchain client:
|
||||
- Operating system:
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
<!--
|
||||
Please provide a *minimal* source code example to trigger the bug you have found.
|
||||
Please also mention any command line flags that are necessary for triggering the bug.
|
||||
Provide as much information as necessary to reproduce the bug.
|
||||
|
||||
```
|
||||
// Some *minimal* Solidity source code to reproduce the bug.
|
||||
// ...
|
||||
```
|
||||
-->
|
47
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
47
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Solidity language or infrastructure feature requests.
|
||||
---
|
||||
|
||||
<!--## Prerequisites
|
||||
|
||||
- First, many thanks for taking part in the community. We really appreciate that.
|
||||
- We realize there is a lot of data requested here. We ask only that you do your best to provide as much information as possible so we can better help you.
|
||||
- Support questions are better asked in one of the following locations:
|
||||
- [Solidity chat](https://gitter.im/ethereum/solidity)
|
||||
- [Stack Overflow](https://ethereum.stackexchange.com/)
|
||||
- Ensure the issue isn't already reported (check `feature` and `language design` labels).
|
||||
|
||||
*Delete the above section and the instructions in the sections below before submitting*
|
||||
|
||||
-->
|
||||
|
||||
## Abstract
|
||||
|
||||
<!--
|
||||
Please describe by example what problem you see in the current Solidity language
|
||||
and reason about it.
|
||||
-->
|
||||
|
||||
## Motivation
|
||||
|
||||
<!--
|
||||
In this section you describe how you propose to address the problem you described earlier,
|
||||
including by giving one or more exemplary source code snippets for demonstration.
|
||||
-->
|
||||
|
||||
## Specification
|
||||
|
||||
<!--
|
||||
The technical specification should describe the syntax and semantics of any new feature. The
|
||||
specification should be detailed enough to allow any developer to implement the functionality.
|
||||
-->
|
||||
|
||||
## Backwards Compatibility
|
||||
|
||||
<!--
|
||||
All language changes that introduce backwards incompatibilities must include a section describing
|
||||
these incompatibilities and their severity.
|
||||
|
||||
Please describe how you propose to deal with these incompatibilities.
|
||||
-->
|
21
.github/ISSUE_TEMPLATE/general.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/general.md
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
name: General Feedback
|
||||
about: Any general feedback (neither feature request nor bug reports)
|
||||
---
|
||||
|
||||
<!--## Prerequisites
|
||||
|
||||
- First, many thanks for taking part in the community. We really appreciate that.
|
||||
- Read the [contributing guidelines](http://solidity.readthedocs.io/en/latest/contributing.html).
|
||||
- Support questions are better asked in one of the following locations:
|
||||
- [Solidity chat](https://gitter.im/ethereum/solidity)
|
||||
- [Stack Overflow](https://ethereum.stackexchange.com/)
|
||||
- Ensure the issue isn't already reported.
|
||||
|
||||
*Delete the above section and the instructions in the sections below before submitting*
|
||||
-->
|
||||
## Description
|
||||
|
||||
<!--
|
||||
Please describe the purpose of your ticket.
|
||||
-->
|
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<!--### Your checklist for this pull request
|
||||
|
||||
Please review the [guidelines for contributing](http://solidity.readthedocs.io/en/latest/contributing.html) to this repository.
|
||||
|
||||
Please also note that this project is released with a [Contributor Code of Conduct](CONDUCT.md). By participating in this project you agree to abide by its terms.
|
||||
-->
|
||||
|
||||
### Description
|
||||
|
||||
<!--
|
||||
Please explain the changes you made here.
|
||||
|
||||
Thank you for your help!
|
||||
-->
|
||||
|
||||
### Checklist
|
||||
- [ ] Code compiles correctly
|
||||
- [ ] All tests are passing
|
||||
- [ ] New tests have been created which fail without the change (if possible)
|
||||
- [ ] README / documentation was extended, if necessary
|
||||
- [ ] Changelog entry (if change is visible to the user)
|
||||
- [ ] Used meaningful commit messages
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -36,13 +36,19 @@ docs/_build
|
||||
docs/utils/__pycache__
|
||||
docs/utils/*.pyc
|
||||
/deps/downloads/
|
||||
deps/install
|
||||
deps/cache
|
||||
|
||||
# vim stuff
|
||||
*.swp
|
||||
[._]*.sw[a-p]
|
||||
[._]sw[a-p]
|
||||
|
||||
# IDE files
|
||||
.idea
|
||||
.vscode
|
||||
browse.VC.db
|
||||
CMakeLists.txt.user
|
||||
/CMakeSettings.json
|
||||
/.vs
|
||||
/.cproject
|
||||
/.project
|
||||
|
@ -184,7 +184,7 @@ deploy:
|
||||
# Both the build and deploy steps for Emscripten are only run within the Ubuntu
|
||||
# configurations (not for macOS). That is controlled by conditionals within the bash
|
||||
# scripts because TravisCI doesn't provide much in the way of conditional logic.
|
||||
|
||||
|
||||
- provider: script
|
||||
script: test $SOLC_EMSCRIPTEN != On || (scripts/release_emscripten.sh)
|
||||
skip_cleanup: true
|
||||
|
@ -8,7 +8,7 @@ include(EthPolicy)
|
||||
eth_policy()
|
||||
|
||||
# project name and version should be set after cmake_policy CMP0048
|
||||
set(PROJECT_VERSION "0.4.25")
|
||||
set(PROJECT_VERSION "0.5.0")
|
||||
project(solidity VERSION ${PROJECT_VERSION})
|
||||
|
||||
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
|
||||
|
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at chris@ethereum.org which only goes to
|
||||
Christian Reitwiessner or axic@ethereum.org which only goes to Alex Beregszaszi.
|
||||
To report an issue involving either of them please email Hudson Jameson at
|
||||
hudson@ethereum.org.
|
||||
All complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
@ -146,7 +146,7 @@ for (auto i = x->begin(); i != x->end(); ++i) {}
|
||||
```
|
||||
|
||||
No:
|
||||
```cp
|
||||
```cpp
|
||||
const double d = 0;
|
||||
int i, j;
|
||||
char *s;
|
||||
@ -193,7 +193,7 @@ for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end();
|
||||
2. Generally avoid shortening a standard form that already includes all important information:
|
||||
- e.g. stick to `shared_ptr<X>` rather than shortening to `ptr<X>`.
|
||||
3. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently:
|
||||
- e.g. `using Guard = std::lock_guard<std::mutex>;` ///< Guard is used throughout the codebase since it is clear in meaning and used commonly.
|
||||
- e.g. `using Guard = std::lock_guard<std::mutex>;` ///< Guard is used throughout the codebase since it is clear in meaning and used commonly.
|
||||
4. In general expressions should be roughly as important/semantically meaningful as the space they occupy.
|
||||
5. Avoid introducing aliases for types unless they are very complicated. Consider the number of items a brain can keep track of at the same time.
|
||||
|
||||
|
144
Changelog.md
144
Changelog.md
@ -1,3 +1,142 @@
|
||||
### 0.5.0 (2018-11-13)
|
||||
|
||||
How to update your code:
|
||||
* Change every ``.call()`` to a ``.call("")`` and every ``.call(signature, a, b, c)`` to use ``.call(abi.encodeWithSignature(signature, a, b, c))`` (the last one only works for value types).
|
||||
* Change every ``keccak256(a, b, c)`` to ``keccak256(abi.encodePacked(a, b, c))``.
|
||||
* Add ``public`` to every function and ``external`` to every fallback or interface function that does not specify its visibility already.
|
||||
* Make your fallback functions ``external``.
|
||||
* Explicitly state the data location for all variables of struct, array or mapping types (including function parameters), e.g. change ``uint[] x = m_x`` to ``uint[] storage x = m_x``. Note that ``external`` functions require parameters with a data location of ``calldata``.
|
||||
* Explicitly convert values of contract type to addresses before using an ``address`` member. Example: if ``c`` is a contract, change ``c.transfer(...)`` to ``address(c).transfer(...)``.
|
||||
* Declare variables and especially function arguments as ``address payable``, if you want to call ``transfer`` on them.
|
||||
|
||||
Breaking Changes:
|
||||
* ABI Encoder: Properly pad data from calldata (``msg.data`` and external function parameters). Use ``abi.encodePacked`` for unpadded encoding.
|
||||
* C API (``libsolc`` / raw ``soljson.js``): Removed the ``version``, ``license``, ``compileSingle``, ``compileJSON``, ``compileJSONCallback`` methods
|
||||
and replaced them with the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods.
|
||||
* Code Generator: Signed right shift uses proper arithmetic shift, i.e. rounding towards negative infinity. Warning: this may silently change the semantics of existing code!
|
||||
* Code Generator: Revert at runtime if calldata is too short or points out of bounds. This is done inside the ``ABI decoder`` and therefore also applies to ``abi.decode()``.
|
||||
* Code Generator: Use ``STATICCALL`` for ``pure`` and ``view`` functions. This was already the case in the experimental 0.5.0 mode.
|
||||
* Commandline interface: Remove obsolete ``--formal`` option.
|
||||
* Commandline interface: Rename the ``--julia`` option to ``--yul``.
|
||||
* Commandline interface: Require ``-`` if standard input is used as source.
|
||||
* Commandline interface: Use hash of library name for link placeholder instead of name itself.
|
||||
* Compiler interface: Disallow remappings with empty prefix.
|
||||
* Control Flow Analyzer: Consider mappings as well when checking for uninitialized return values.
|
||||
* Control Flow Analyzer: Turn warning about returning uninitialized storage pointers into an error.
|
||||
* General: ``continue`` in a ``do...while`` loop jumps to the condition (it used to jump to the loop body). Warning: this may silently change the semantics of existing code.
|
||||
* General: Disallow declaring empty structs.
|
||||
* General: Disallow raw ``callcode`` (was already deprecated in 0.4.12). It is still possible to use it via inline assembly.
|
||||
* General: Disallow ``var`` keyword.
|
||||
* General: Disallow ``sha3`` and ``suicide`` aliases.
|
||||
* General: Disallow the ``throw`` statement. This was already the case in the experimental 0.5.0 mode.
|
||||
* General: Disallow the ``years`` unit denomination (was already deprecated in 0.4.24)
|
||||
* General: Introduce ``emit`` as a keyword instead of parsing it as identifier.
|
||||
* General: New keywords: ``calldata`` and ``constructor``
|
||||
* General: New reserved keywords: ``alias``, ``apply``, ``auto``, ``copyof``, ``define``, ``immutable``,
|
||||
``implements``, ``macro``, ``mutable``, ``override``, ``partial``, ``promise``, ``reference``, ``sealed``,
|
||||
``sizeof``, ``supports``, ``typedef`` and ``unchecked``.
|
||||
* General: Remove assembly instruction aliases ``sha3`` and ``suicide``
|
||||
* General: C99-style scoping rules are enforced now. This was already the case in the experimental 0.5.0 mode.
|
||||
* General: Disallow combining hex numbers with unit denominations (e.g. ``0x1e wei``). This was already the case in the experimental 0.5.0 mode.
|
||||
* JSON AST: Remove ``constant`` and ``payable`` fields (the information is encoded in the ``stateMutability`` field).
|
||||
* JSON AST: Replace the ``isConstructor`` field by a new ``kind`` field, which can be ``constructor``, ``fallback`` or ``function``.
|
||||
* Interface: Remove "clone contract" feature. The ``--clone-bin`` and ``--combined-json clone-bin`` commandline options are not available anymore.
|
||||
* Name Resolver: Do not exclude public state variables when looking for conflicting declarations.
|
||||
* Optimizer: Remove the no-op ``PUSH1 0 NOT AND`` sequence.
|
||||
* Parser: Disallow trailing dots that are not followed by a number.
|
||||
* Parser: Remove ``constant`` as function state mutability modifier.
|
||||
* Parser: Disallow uppercase X in hex number literals
|
||||
* Type Checker: Disallow assignments between tuples with different numbers of components. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Disallow values for constants that are not compile-time constants. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Disallow arithmetic operations for boolean variables.
|
||||
* Type Checker: Disallow tight packing of literals. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Disallow calling base constructors without parentheses. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Disallow conversions between ``bytesX`` and ``uintY`` of different size.
|
||||
* Type Checker: Disallow conversions between unrelated contract types. Explicit conversion via ``address`` can still achieve it.
|
||||
* Type Checker: Disallow empty return statements for functions with one or more return values.
|
||||
* Type Checker: Disallow empty tuple components. This was partly already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Disallow multi-variable declarations with mismatching number of values. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Disallow specifying base constructor arguments multiple times in the same inheritance hierarchy. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Disallow calling constructor with wrong argument count. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Disallow uninitialized storage variables. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Detecting cyclic dependencies in variables and structs is limited in recursion to 256.
|
||||
* Type Checker: Require explicit data location for all variables, including function parameters. This was partly already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Only accept a single ``bytes`` type for ``.call()`` (and family), ``keccak256()``, ``sha256()`` and ``ripemd160()``.
|
||||
* Type Checker: Fallback function must be external. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Interface functions must be declared external. This was already the case in the experimental 0.5.0 mode.
|
||||
* Type Checker: Address members are not included in contract types anymore. An explicit conversion is now required before invoking an ``address`` member from a contract.
|
||||
* Type Checker: Disallow "loose assembly" syntax entirely. This means that jump labels, jumps and non-functional instructions cannot be used anymore.
|
||||
* Type System: Disallow explicit and implicit conversions from decimal literals to ``bytesXX`` types.
|
||||
* Type System: Disallow explicit and implicit conversions from hex literals to ``bytesXX`` types of different size.
|
||||
* Type System: Distinguish between payable and non-payable address types.
|
||||
* View Pure Checker: Disallow ``msg.value`` in (or introducing it via a modifier to) a non-payable function.
|
||||
* Remove obsolete ``std`` directory from the Solidity repository. This means accessing ``https://github.com/ethereum/solidity/blob/develop/std/*.sol`` (or ``https://github.com/ethereum/solidity/std/*.sol`` in Remix) will not be possible.
|
||||
* References Resolver: Turn missing storage locations into an error. This was already the case in the experimental 0.5.0 mode.
|
||||
* Syntax Checker: Disallow functions without implementation to use modifiers. This was already the case in the experimental 0.5.0 mode.
|
||||
* Syntax Checker: Named return values in function types are an error.
|
||||
* Syntax Checker: Strictly require visibility specifier for functions. This was already the case in the experimental 0.5.0 mode.
|
||||
* Syntax Checker: Disallow unary ``+``. This was already the case in the experimental 0.5.0 mode.
|
||||
* Syntax Checker: Disallow single statement variable declaration inside if/while/for bodies that are not blocks.
|
||||
* View Pure Checker: Strictly enforce state mutability. This was already the case in the experimental 0.5.0 mode.
|
||||
|
||||
Language Features:
|
||||
* General: Add ``staticcall`` to ``address``.
|
||||
* General: Allow appending ``calldata`` keyword to types, to explicitly specify data location for arguments of external functions.
|
||||
* General: Support ``pop()`` for storage arrays.
|
||||
* General: Scoping rules now follow the C99-style.
|
||||
* General: Allow ``enum``s in interfaces.
|
||||
* General: Allow ``mapping`` storage pointers as arguments and return values in all internal functions.
|
||||
* General: Allow ``struct``s in interfaces.
|
||||
* General: Provide access to the ABI decoder through ``abi.decode(bytes memory data, (...))``.
|
||||
* General: Disallow zero length for fixed-size arrays.
|
||||
* Parser: Accept the ``address payable`` type during parsing.
|
||||
|
||||
Compiler Features:
|
||||
* Build System: Support for Mojave version of macOS added.
|
||||
* Code Generator: ``CREATE2`` instruction has been updated to match EIP1014 (aka "Skinny CREATE2"). It also is accepted as part of Constantinople.
|
||||
* Code Generator: ``EXTCODEHASH`` instruction has been added based on EIP1052.
|
||||
* Type Checker: Nicer error message when trying to reference overloaded identifiers in inline assembly.
|
||||
* Type Checker: Show named argument in case of error.
|
||||
* Type System: IntegerType is split into IntegerType and AddressType internally.
|
||||
* Tests: Determine transaction status during IPC calls.
|
||||
* Code Generator: Allocate and free local variables according to their scope.
|
||||
* Removed ``pragma experimental "v0.5.0";``.
|
||||
* Syntax Checker: Improved error message for lookup in function types.
|
||||
* Name Resolver: Updated name suggestion look up function to take into account length of the identifier: 1: no search, 2-3: at most one change, 4-: at most two changes
|
||||
* SMTChecker: Support calls to internal functions that return none or a single value.
|
||||
|
||||
Bugfixes:
|
||||
* Build System: Support versions of CVC4 linked against CLN instead of GMP. In case of compilation issues due to the experimental SMT solver support, the solvers can be disabled when configuring the project with CMake using ``-DUSE_CVC4=OFF`` or ``-DUSE_Z3=OFF``.
|
||||
* Tests: Fix chain parameters to make ipc tests work with newer versions of cpp-ethereum.
|
||||
* Code Generator: Fix allocation of byte arrays (zeroed out too much memory).
|
||||
* Code Generator: Properly handle negative number literals in ABIEncoderV2.
|
||||
* Code Generator: Do not crash on using a length of zero for multidimensional fixed-size arrays.
|
||||
* Commandline Interface: Correctly handle paths with backslashes on windows.
|
||||
* Control Flow Analyzer: Ignore unimplemented functions when detecting uninitialized storage pointer returns.
|
||||
* Fix NatSpec json output for `@notice` and `@dev` tags on contract definitions.
|
||||
* Optimizer: Correctly estimate gas costs of constants for special cases.
|
||||
* Optimizer: Fix simplification rule initialization bug that appeared on some emscripten platforms.
|
||||
* References Resolver: Do not crash on using ``_slot`` and ``_offset`` suffixes on their own.
|
||||
* References Resolver: Enforce ``storage`` as data location for mappings.
|
||||
* References Resolver: Properly handle invalid references used together with ``_slot`` and ``_offset``.
|
||||
* References Resolver: Report error instead of assertion fail when FunctionType has an undeclared type as parameter.
|
||||
* References Resolver: Fix high CPU usage when using large variable names issue. Only suggest similar name if identifiers shorter than 80 characters.
|
||||
* Type Checker: Default data location for type conversions (e.g. from literals) is memory and not storage.
|
||||
* Type Checker: Disallow assignments to mappings within tuple assignments as well.
|
||||
* Type Checker: Disallow packed encoding of arrays of structs.
|
||||
* Type Checker: Allow assignments to local variables of mapping types.
|
||||
* Type Checker: Consider fixed size arrays when checking for recursive structs.
|
||||
* Type Checker: Fix crashes in erroneous tuple assignments in which the type of the right hand side cannot be determined.
|
||||
* Type Checker: Fix freeze for negative fixed-point literals very close to ``0``, such as ``-1e-100``.
|
||||
* Type Checker: Dynamic types as key for public mappings return error instead of assertion fail.
|
||||
* Type Checker: Fix internal error when array index value is too large.
|
||||
* Type Checker: Fix internal error when fixed-size array is too large to be encoded.
|
||||
* Type Checker: Fix internal error for array type conversions.
|
||||
* Type Checker: Fix internal error when array index is not an unsigned.
|
||||
* Type System: Allow arbitrary exponents for literals with a mantissa of zero.
|
||||
* Parser: Fix incorrect source location for nameless parameters.
|
||||
* Command Line Interface: Fix internal error when compiling stdin with no content and --ast option.
|
||||
|
||||
### 0.4.25 (2018-09-12)
|
||||
|
||||
Important Bugfixes:
|
||||
@ -63,6 +202,7 @@ Features:
|
||||
* General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature.
|
||||
* General: Limit the number of errors output in a single run to 256.
|
||||
* General: Support accessing dynamic return data in post-byzantium EVMs.
|
||||
* General: Allow underscores in numeric and hex literals to separate thousands and quads.
|
||||
* Inheritance: Error when using empty parentheses for base class constructors that require arguments as experimental 0.5.0 feature.
|
||||
* Inheritance: Error when using no parentheses in modifier-style constructor calls as experimental 0.5.0 feature.
|
||||
* Interfaces: Allow overriding external functions in interfaces with public in an implementing contract.
|
||||
@ -106,7 +246,7 @@ Bugfixes:
|
||||
Features:
|
||||
* Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
|
||||
* Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead).
|
||||
* Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature.
|
||||
* Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimental 0.5.0 feature.
|
||||
* General: C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature.
|
||||
* General: Improved messaging when error spans multiple lines of a sourcefile
|
||||
* General: Support and recommend using ``emit EventName();`` to call events explicitly.
|
||||
@ -659,7 +799,7 @@ Bugfixes:
|
||||
* Conditional: `x ? y : z`
|
||||
* Bugfix: Fixed several bugs where the optimizer generated invalid code.
|
||||
* Bugfix: Enums and structs were not accessible to other contracts.
|
||||
* Bugfix: Fixed segfault connected to function paramater types, appeared during gas estimation.
|
||||
* Bugfix: Fixed segfault connected to function parameter types, appeared during gas estimation.
|
||||
* Bugfix: Type checker crash for wrong number of base constructor parameters.
|
||||
* Bugfix: Allow function overloads with different array types.
|
||||
* Bugfix: Allow assignments of type `(x) = 7`.
|
||||
|
67
README.md
67
README.md
@ -1,19 +1,66 @@
|
||||
# The Solidity Contract-Oriented Programming Language
|
||||
[![Join the chat at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/ethereum/solidity.svg?branch=develop)](https://travis-ci.org/ethereum/solidity)
|
||||
Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform.
|
||||
|
||||
## Useful links
|
||||
To get started you can find an introduction to the language in the [Solidity documentation](https://solidity.readthedocs.org). In the documentation, you can find [code examples](https://solidity.readthedocs.io/en/latest/solidity-by-example.html) as well as [a reference](https://solidity.readthedocs.io/en/latest/solidity-in-depth.html) of the syntax and details on how to write smart contracts.
|
||||
## Table of Contents
|
||||
|
||||
You can start using [Solidity in your browser](http://remix.ethereum.org) with no need to download or compile anything.
|
||||
- [Background](#background)
|
||||
- [Build and Install](#build-and-install)
|
||||
- [Example](#example)
|
||||
- [Documentation](#documentation)
|
||||
- [Development](#development)
|
||||
- [Maintainers](#maintainers)
|
||||
- [License](#license)
|
||||
|
||||
The changelog for this project can be found [here](https://github.com/ethereum/solidity/blob/develop/Changelog.md).
|
||||
## Background
|
||||
|
||||
Solidity is still under development. So please do not hesitate and open an [issue in GitHub](https://github.com/ethereum/solidity/issues) if you encounter anything strange.
|
||||
Solidity is a statically-typed curly-braces programming language designed for developing smart contracts
|
||||
that run on the Ethereum Virtual Machine. Smart contracts are programs that are executed inside a peer-to-peer
|
||||
network where nobody has special authority over the execution and thus they allow to implement tokens of value,
|
||||
ownership, voting and other kinds of logics.
|
||||
|
||||
## Building
|
||||
See the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source) for build instructions.
|
||||
## Build and Install
|
||||
|
||||
## How to Contribute
|
||||
Please see our [contribution guidelines](https://solidity.readthedocs.io/en/latest/contributing.html) in the Solidity documentation.
|
||||
Instructions about how to build and install the Solidity compiler can be found in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source)
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
A "Hello World" program in Solidity is of even less use than in other languages, but still:
|
||||
|
||||
```
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract HelloWorld {
|
||||
function helloWorld() external pure returns (string memory) {
|
||||
return "Hello, World!";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is an
|
||||
browser-based IDE. Here are some example contracts:
|
||||
|
||||
1. [Voting](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#voting)
|
||||
2. [Blind Auction](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#blind-auction)
|
||||
3. [Safe remote purchase](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#safe-remote-purchase)
|
||||
4. [Micropayment Channel](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#micropayment-channel)
|
||||
|
||||
## Documentation
|
||||
|
||||
The Solidity documentation is hosted at [Read the docs](https://solidity.readthedocs.io).
|
||||
|
||||
## Development
|
||||
|
||||
Solidity is still under development. Contributions are always welcome!
|
||||
Please follow the
|
||||
[Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html)
|
||||
if you want to help.
|
||||
|
||||
## Maintainers
|
||||
[@axic](https://github.com/axic)
|
||||
[@chriseth](https://github.com/chriseth)
|
||||
|
||||
## License
|
||||
Solidity is licensed under [GNU General Public License v3.0](https://github.com/ethereum/solidity/blob/develop/LICENSE.txt)
|
||||
|
||||
Any contributions are welcome!
|
||||
|
@ -8,7 +8,7 @@ Checklist for making a release:
|
||||
- [ ] Make a final check that there are no platform-dependency issues in the ``solc-test-bytecode`` repository.
|
||||
- [ ] Wait for the tests for the commit on ``release``, create a release in Github, creating the tag.
|
||||
- [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``).
|
||||
- [ ] Wait for the CI runs on the tag itself (they should push artefacts onto the Github release page).
|
||||
- [ ] Wait for the CI runs on the tag itself (they should push artifacts onto the Github release page).
|
||||
- [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key).
|
||||
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh release``).
|
||||
- [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash)
|
||||
|
@ -55,7 +55,6 @@ install:
|
||||
$fileContent += "`n-----END RSA PRIVATE KEY-----`n";
|
||||
Set-Content c:\users\appveyor\.ssh\id_rsa $fileContent
|
||||
}
|
||||
- git submodule update --init --recursive
|
||||
- ps: $prerelease = "nightly."
|
||||
- ps: $prerelease += Get-Date -format "yyyy.M.d"
|
||||
- ps: if($env:appveyor_repo_branch -eq 'release') { Set-Content prerelease.txt $null } else { Set-Content prerelease.txt $prerelease }
|
||||
|
@ -31,7 +31,7 @@ function(create_build_info NAME)
|
||||
# Generate header file containing useful build information
|
||||
add_custom_target(${NAME}_BuildInfo.h ALL
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -DETH_SOURCE_DIR="${PROJECT_SOURCE_DIR}" -DETH_BUILDINFO_IN="${ETH_CMAKE_DIR}/templates/BuildInfo.h.in" -DETH_DST_DIR="${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}" -DETH_CMAKE_DIR="${ETH_CMAKE_DIR}"
|
||||
COMMAND ${CMAKE_COMMAND} -DETH_SOURCE_DIR=${PROJECT_SOURCE_DIR} -DETH_BUILDINFO_IN=${ETH_CMAKE_DIR}/templates/BuildInfo.h.in -DETH_DST_DIR=${PROJECT_BINARY_DIR}/include/${PROJECT_NAME} -DETH_CMAKE_DIR=${ETH_CMAKE_DIR}
|
||||
-DETH_BUILD_TYPE="${_cmake_build_type}"
|
||||
-DETH_BUILD_OS="${ETH_BUILD_OS}"
|
||||
-DETH_BUILD_COMPILER="${ETH_BUILD_COMPILER}"
|
||||
|
@ -26,7 +26,7 @@ eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
|
||||
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
||||
|
||||
# Use ISO C++11 standard language.
|
||||
set(CMAKE_CXX_FLAGS -std=c++11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
|
||||
# Enables all the warnings about constructions that some users consider questionable,
|
||||
# and that are easy to avoid. Also enable some extra warning flags that are not
|
||||
@ -36,13 +36,6 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
||||
add_compile_options(-Wextra)
|
||||
add_compile_options(-Werror)
|
||||
|
||||
# Disable warnings about unknown pragmas (which is enabled by -Wall). I assume we have external
|
||||
# dependencies (probably Boost) which have some of these. Whatever the case, we shouldn't be
|
||||
# disabling these globally. Instead, we should pragma around just the problem #includes.
|
||||
#
|
||||
# TODO - Track down what breaks if we do NOT do this.
|
||||
add_compile_options(-Wno-unknown-pragmas)
|
||||
|
||||
# Configuration-specific compiler settings.
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG")
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
|
||||
@ -73,13 +66,13 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
||||
# TODO - Is this even necessary? Why?
|
||||
# See http://stackoverflow.com/questions/19774778/when-is-it-necessary-to-use-use-the-flag-stdlib-libstdc.
|
||||
add_compile_options(-stdlib=libstdc++)
|
||||
|
||||
|
||||
# Tell Boost that we're using Clang's libc++. Not sure exactly why we need to do.
|
||||
add_definitions(-DBOOST_ASIO_HAS_CLANG_LIBCXX)
|
||||
|
||||
|
||||
# Use fancy colors in the compiler diagnostics
|
||||
add_compile_options(-fcolor-diagnostics)
|
||||
|
||||
|
||||
# See "How to silence unused command line argument error with clang without disabling it?"
|
||||
# When using -Werror with clang, it transforms "warning: argument unused during compilation" messages
|
||||
# into errors, which makes sense.
|
||||
@ -90,7 +83,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
||||
if (EMSCRIPTEN)
|
||||
# Do not emit a separate memory initialiser file
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0")
|
||||
# Leave only exported symbols as public and agressively remove others
|
||||
# Leave only exported symbols as public and aggressively remove others
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections -fvisibility=hidden")
|
||||
# Optimisation level
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
@ -146,26 +139,29 @@ endif ()
|
||||
|
||||
if (SANITIZE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=${SANITIZE}")
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/sanitizer-blacklist.txt")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
|
||||
set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "-g ${CMAKE_C_FLAGS}")
|
||||
add_definitions(-DETH_PROFILING_GPERF)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lprofiler")
|
||||
# set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -lprofiler")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lprofiler")
|
||||
endif ()
|
||||
# Code coverage support.
|
||||
# Copied from Cable:
|
||||
# https://github.com/ethereum/cable/blob/v0.2.4/CableCompilerSettings.cmake#L118-L132
|
||||
option(COVERAGE "Build with code coverage support" OFF)
|
||||
if(COVERAGE)
|
||||
# Set the linker flags first, they are required to properly test the compiler flag.
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
|
||||
if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")))
|
||||
set(CMAKE_CXX_FLAGS "-g --coverage ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "-g --coverage ${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS} -lprofiler")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS} -lprofiler")
|
||||
endif ()
|
||||
set(CMAKE_REQUIRED_LIBRARIES "--coverage ${CMAKE_REQUIRED_LIBRARIES}")
|
||||
check_cxx_compiler_flag(--coverage have_coverage)
|
||||
string(REPLACE "--coverage " "" CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
||||
if(NOT have_coverage)
|
||||
message(FATAL_ERROR "Coverage not supported")
|
||||
endif()
|
||||
add_compile_options(-g --coverage)
|
||||
endif()
|
||||
|
||||
# SMT Solvers integration
|
||||
option(USE_Z3 "Allow compiling with Z3 SMT solver integration" ON)
|
||||
option(USE_CVC4 "Allow compiling with CVC4 SMT solver integration" ON)
|
||||
|
||||
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
||||
option(USE_LD_GOLD "Use GNU gold linker" ON)
|
||||
|
@ -2,7 +2,7 @@ macro(configure_project)
|
||||
set(NAME ${PROJECT_NAME})
|
||||
|
||||
# features
|
||||
eth_default_option(PROFILING OFF)
|
||||
eth_default_option(COVERAGE OFF)
|
||||
|
||||
# components
|
||||
eth_default_option(TESTS ON)
|
||||
@ -27,7 +27,7 @@ macro(print_config NAME)
|
||||
message("-- CMAKE_BUILD_TYPE Build type ${CMAKE_BUILD_TYPE}")
|
||||
message("-- TARGET_PLATFORM Target platform ${CMAKE_SYSTEM_NAME}")
|
||||
message("--------------------------------------------------------------- features")
|
||||
message("-- PROFILING Profiling support ${PROFILING}")
|
||||
message("-- COVERAGE Coverage support ${COVERAGE}")
|
||||
message("------------------------------------------------------------- components")
|
||||
if (SUPPORT_TESTS)
|
||||
message("-- TESTS Build tests ${TESTS}")
|
||||
|
@ -4,32 +4,16 @@ macro (eth_policy)
|
||||
# link_directories() treats paths relative to the source dir.
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
|
||||
# let cmake autolink dependencies on windows
|
||||
cmake_policy(SET CMP0020 NEW)
|
||||
# Avoid warnings in CMake 3.0.2:
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
cmake_policy(SET CMP0043 NEW)
|
||||
|
||||
# CMake 2.8.12 and lower allowed the use of targets and files with double
|
||||
# colons in target_link_libraries,
|
||||
cmake_policy(SET CMP0028 OLD)
|
||||
# allow VERSION argument in project()
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
|
||||
if (${CMAKE_VERSION} VERSION_GREATER 3.0)
|
||||
|
||||
# fix MACOSX_RPATH
|
||||
cmake_policy(SET CMP0042 OLD)
|
||||
|
||||
# ignore COMPILE_DEFINITIONS_<Config> properties
|
||||
cmake_policy(SET CMP0043 OLD)
|
||||
|
||||
# allow VERSION argument in project()
|
||||
cmake_policy(SET CMP0048 NEW)
|
||||
|
||||
endif()
|
||||
|
||||
if (${CMAKE_VERSION} VERSION_GREATER 3.1)
|
||||
|
||||
if (POLICY CMP0054)
|
||||
# do not interpret if() arguments as variables!
|
||||
cmake_policy(SET CMP0054 NEW)
|
||||
|
||||
endif()
|
||||
|
||||
endmacro()
|
||||
|
||||
|
3
cmake/FindCLN.cmake
Normal file
3
cmake/FindCLN.cmake
Normal file
@ -0,0 +1,3 @@
|
||||
find_library(CLN_LIBRARY NAMES cln)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(CLN DEFAULT_MSG CLN_LIBRARY)
|
@ -1,4 +1,26 @@
|
||||
find_path(CVC4_INCLUDE_DIR cvc4/cvc4.h)
|
||||
find_library(CVC4_LIBRARY NAMES cvc4 )
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(CVC4 DEFAULT_MSG CVC4_LIBRARY CVC4_INCLUDE_DIR)
|
||||
if (USE_CVC4)
|
||||
find_path(CVC4_INCLUDE_DIR cvc4/cvc4.h)
|
||||
find_library(CVC4_LIBRARY NAMES cvc4)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(CVC4 DEFAULT_MSG CVC4_LIBRARY CVC4_INCLUDE_DIR)
|
||||
if(CVC4_FOUND)
|
||||
# CVC4 may depend on either CLN or GMP.
|
||||
# We can assume that the one it requires is present on the system,
|
||||
# so we quietly try to find both and link against them, if they are
|
||||
# present.
|
||||
find_package(CLN QUIET)
|
||||
find_package(GMP QUIET)
|
||||
|
||||
set(CVC4_LIBRARIES ${CVC4_LIBRARY})
|
||||
|
||||
if (CLN_FOUND)
|
||||
set(CVC4_LIBRARIES ${CVC4_LIBRARIES} ${CLN_LIBRARY})
|
||||
endif ()
|
||||
|
||||
if (GMP_FOUND)
|
||||
set(CVC4_LIBRARIES ${CVC4_LIBRARIES} ${GMP_LIBRARY})
|
||||
endif ()
|
||||
endif()
|
||||
else()
|
||||
set(CVC4_FOUND FALSE)
|
||||
endif()
|
||||
|
@ -1,7 +1,9 @@
|
||||
find_path(Z3_INCLUDE_DIR z3++.h)
|
||||
find_library(Z3_LIBRARY NAMES z3 )
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR)
|
||||
|
||||
if (USE_Z3)
|
||||
find_path(Z3_INCLUDE_DIR NAMES z3++.h PATH_SUFFIXES z3)
|
||||
find_library(Z3_LIBRARY NAMES z3 )
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR)
|
||||
else()
|
||||
set(Z3_FOUND FALSE)
|
||||
endif()
|
||||
# TODO: Create IMPORTED library for Z3.
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# generates BuildInfo.h
|
||||
#
|
||||
#
|
||||
# this module expects
|
||||
# ETH_SOURCE_DIR - main CMAKE_SOURCE_DIR
|
||||
# ETH_DST_DIR - main CMAKE_BINARY_DIR
|
||||
@ -19,7 +19,7 @@ if (NOT ETH_BUILD_PLATFORM)
|
||||
set(ETH_BUILD_PLATFORM "unknown")
|
||||
endif()
|
||||
|
||||
# Logic here: If prereleases.txt exists but is empty, it is a non-pre release.
|
||||
# Logic here: If prerelease.txt exists but is empty, it is a non-pre release.
|
||||
# If it does not exist, create our own prerelease string
|
||||
if (EXISTS ${ETH_SOURCE_DIR}/prerelease.txt)
|
||||
file(READ ${ETH_SOURCE_DIR}/prerelease.txt SOL_VERSION_PRERELEASE)
|
||||
|
@ -66,6 +66,37 @@ jsoncpp:
|
||||
Public Domain "license" you can re-license your copy using whatever
|
||||
license you like.
|
||||
|
||||
scanner/token:
|
||||
The libsolidity/parsing/{scanner,token}.{h,cpp} files are dervied from
|
||||
code originating from the V8 project licensed under the following terms:
|
||||
|
||||
Copyright 2006-2012, the V8 project authors. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
All other code is licensed under GPL version 3:
|
||||
|
||||
)"};
|
||||
|
18
codecov.yml
Normal file
18
codecov.yml
Normal file
@ -0,0 +1,18 @@
|
||||
codecov:
|
||||
branch: develop
|
||||
coverage:
|
||||
range: 70...100
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
paths: "!test/"
|
||||
syntax:
|
||||
target: auto
|
||||
paths: "libsolidity/analysis"
|
||||
flags: syntax
|
||||
tests:
|
||||
target: auto
|
||||
paths: "test/"
|
||||
comment:
|
||||
layout: "reach, diff, flags"
|
511
docs/050-breaking-changes.rst
Normal file
511
docs/050-breaking-changes.rst
Normal file
@ -0,0 +1,511 @@
|
||||
********************************
|
||||
Solidity v0.5.0 Breaking Changes
|
||||
********************************
|
||||
|
||||
This section highlights the main breaking changes introduced in Solidity
|
||||
version 0.5.0, along with the reasoning behind the changes and how to update
|
||||
affected code.
|
||||
For the full list check
|
||||
`the release changelog <https://github.com/ethereum/solidity/releases/tag/v0.5.0>`_.
|
||||
|
||||
.. note::
|
||||
Contracts compiled with Solidity v0.5.0 can still interface with contracts
|
||||
and even libraries compiled with older versions without recompiling or
|
||||
redeploying them. Changing the interfaces to include data locations and
|
||||
visibility and mutability specifiers suffices. See the
|
||||
:ref:`Interoperability With Older Contracts <interoperability>` section below.
|
||||
|
||||
Semantic Only Changes
|
||||
=====================
|
||||
|
||||
This section lists the changes that are semantic-only, thus potentially
|
||||
hiding new and different behavior in existing code.
|
||||
|
||||
* Signed right shift now uses proper arithmetic shift, i.e. rounding towards
|
||||
negative infinity, instead of rounding towards zero. Signed and unsigned
|
||||
shift will have dedicated opcodes in Constantinople, and are emulated by
|
||||
Solidity for the moment.
|
||||
|
||||
* The ``continue`` statement in a ``do...while`` loop now jumps to the
|
||||
condition, which is the common behavior in such cases. It used to jump to the
|
||||
loop body. Thus, if the condition is false, the loop terminates.
|
||||
|
||||
* The functions ``.call()``, ``.delegatecall()`` and ``.staticcall()`` do not
|
||||
pad anymore when given a single ``bytes`` parameter.
|
||||
|
||||
* Pure and view functions are now called using the opcode ``STATICCALL``
|
||||
instead of ``CALL`` if the EVM version is Byzantium or later. This
|
||||
disallows state changes on the EVM level.
|
||||
|
||||
* The ABI encoder now properly pads byte arrays and strings from calldata
|
||||
(``msg.data`` and external function parameters) when used in external
|
||||
function calls and in ``abi.encode``. For unpadded encoding, use
|
||||
``abi.encodePacked``.
|
||||
|
||||
* The ABI decoder reverts in the beginning of functions and in
|
||||
``abi.decode()`` if passed calldata is too short or points out of bounds.
|
||||
Note that dirty higher order bits are still simply ignored.
|
||||
|
||||
* Forward all available gas with external function calls starting from
|
||||
Tangerine Whistle.
|
||||
|
||||
Semantic and Syntactic Changes
|
||||
==============================
|
||||
|
||||
This section highlights changes that affect syntax and semantics.
|
||||
|
||||
* The functions ``.call()``, ``.delegatecall()``, ``staticcall()``,
|
||||
``keccak256()``, ``sha256()`` and ``ripemd160()`` now accept only a single
|
||||
``bytes`` argument. Moreover, the argument is not padded. This was changed to
|
||||
make more explicit and clear how the arguments are concatenated. Change every
|
||||
``.call()`` (and family) to a ``.call("")`` and every ``.call(signature, a,
|
||||
b, c)`` to use ``.call(abi.encodeWithSignature(signature, a, b, c))`` (the
|
||||
last one only works for value types). Change every ``keccak256(a, b, c)`` to
|
||||
``keccak256(abi.encodePacked(a, b, c))``. Even though it is not a breaking
|
||||
change, it is suggested that developers change
|
||||
``x.call(bytes4(keccak256("f(uint256)"), a, b)`` to
|
||||
``x.call(abi.encodeWithSignature("f(uint256)", a, b))``.
|
||||
|
||||
* Functions ``.call()``, ``.delegatecall()`` and ``.staticcall()`` now return
|
||||
``(bool, bytes memory)`` to provide access to the return data. Change
|
||||
``bool success = otherContract.call("f")`` to ``(bool success, bytes memory
|
||||
data) = otherContract.call("f")``.
|
||||
|
||||
* Solidity now implements C99-style scoping rules for function local
|
||||
variables, that is, variables can only be used after they have been
|
||||
declared and only in the same or nested scopes. Variables declared in the
|
||||
initialization block of a ``for`` loop are valid at any point inside the
|
||||
loop.
|
||||
|
||||
Explicitness Requirements
|
||||
=========================
|
||||
|
||||
This section lists changes where the code now needs to be more explicit.
|
||||
For most of the topics the compiler will provide suggestions.
|
||||
|
||||
* Explicit function visibility is now mandatory. Add ``public`` to every
|
||||
function and constructor, and ``external`` to every fallback or interface
|
||||
function that does not specify its visibility already.
|
||||
|
||||
* Explicit data location for all variables of struct, array or mapping types is
|
||||
now mandatory. This is also applied to function parameters and return
|
||||
variables. For example, change ``uint[] x = m_x`` to ``uint[] storage x =
|
||||
m_x``, and ``function f(uint[][] x)`` to ``function f(uint[][] memory x)``
|
||||
where ``memory`` is the data location and might be replaced by ``storage`` or
|
||||
``calldata`` accordingly. Note that ``external`` functions require
|
||||
parameters with a data location of ``calldata``.
|
||||
|
||||
* Contract types do not include ``address`` members anymore in
|
||||
order to separate the namespaces. Therefore, it is now necessary to
|
||||
explicitly convert values of contract type to addresses before using an
|
||||
``address`` member. Example: if ``c`` is a contract, change
|
||||
``c.transfer(...)`` to ``address(c).transfer(...)``,
|
||||
and ``c.balance`` to ``address(c).balance``.
|
||||
|
||||
* Explicit conversions between unrelated contract types are now disallowed. You can only
|
||||
convert from a contract type to one of its base or ancestor types. If you are sure that
|
||||
a contract is compatible with the contract type you want to convert to, although it does not
|
||||
inherit from it, you can work around this by converting to ``address`` first.
|
||||
Example: if ``A`` and ``B`` are contract types, ``B`` does not inherit from ``A`` and
|
||||
``b`` is a contract of type ``B``, you can still convert ``b`` to type ``A`` using ``A(address(b))``.
|
||||
Note that you still need to watch out for matching payable fallback functions, as explained below.
|
||||
|
||||
* The ``address`` type was split into ``address`` and ``address payable``,
|
||||
where only ``address payable`` provides the ``transfer`` function. An
|
||||
``address payable`` can be directly converted to an ``address``, but the
|
||||
other way around is not allowed. Converting ``address`` to ``address
|
||||
payable`` is possible via conversion through ``uint160``. If ``c`` is a
|
||||
contract, ``address(c)`` results in ``address payable`` only if ``c`` has a
|
||||
payable fallback function. If you use the :ref:`withdraw pattern<withdrawal_pattern>`,
|
||||
you most likely do not have to change your code because ``transfer``
|
||||
is only used on ``msg.sender`` instead of stored addresses and ``msg.sender``
|
||||
is an ``address payable``.
|
||||
|
||||
* Conversions between ``bytesX`` and ``uintY`` of different size are now
|
||||
disallowed due to ``bytesX`` padding on the right and ``uintY`` padding on
|
||||
the left which may cause unexpected conversion results. The size must now be
|
||||
adjusted within the type before the conversion. For example, you can convert
|
||||
a ``bytes4`` (4 bytes) to a ``uint64`` (8 bytes) by first converting the
|
||||
``bytes4`` variable to ``bytes8`` and then to ``uint64``. You get the
|
||||
opposite padding when converting through ``uint32``.
|
||||
|
||||
* Using ``msg.value`` in non-payable functions (or introducing it via a
|
||||
modifier) is disallowed as a security feature. Turn the function into
|
||||
``payable`` or create a new internal function for the program logic that
|
||||
uses ``msg.value``.
|
||||
|
||||
* For clarity reasons, the command line interface now requires ``-`` if the
|
||||
standard input is used as source.
|
||||
|
||||
Deprecated Elements
|
||||
===================
|
||||
|
||||
This section lists changes that deprecate prior features or syntax. Note that
|
||||
many of these changes were already enabled in the experimental mode
|
||||
``v0.5.0``.
|
||||
|
||||
Command Line and JSON Interfaces
|
||||
--------------------------------
|
||||
|
||||
* The command line option ``--formal`` (used to generate Why3 output for
|
||||
further formal verification) was deprecated and is now removed. A new
|
||||
formal verification module, the SMTChecker, is enabled via ``pragma
|
||||
experimental SMTChecker;``.
|
||||
|
||||
* The command line option ``--julia`` was renamed to ``--yul`` due to the
|
||||
renaming of the intermediate language ``Julia`` to ``Yul``.
|
||||
|
||||
* The ``--clone-bin`` and ``--combined-json clone-bin`` command line options
|
||||
were removed.
|
||||
|
||||
* Remappings with empty prefix are disallowed.
|
||||
|
||||
* The JSON AST fields ``constant`` and ``payable`` were removed. The
|
||||
information is now present in the ``stateMutability`` field.
|
||||
|
||||
* The JSON AST field ``isConstructor`` of the ``FunctionDefinition``
|
||||
node was replaced by a field called ``kind`` which can have the
|
||||
value ``"constructor"``, ``"fallback"`` or ``"function"``.
|
||||
|
||||
* In unlinked binary hex files, library address placeholders are now
|
||||
the first 36 hex characters of the keccak256 hash of the fully qualified
|
||||
library name, surrounded by ``$...$``. Previously,
|
||||
just the fully qualified library name was used.
|
||||
This recudes the chances of collisions, especially when long paths are used.
|
||||
Binary files now also contain a list of mappings from these placeholders
|
||||
to the fully qualified names.
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
* Constructors must now be defined using the ``constructor`` keyword.
|
||||
|
||||
* Calling base constructors without parentheses is now disallowed.
|
||||
|
||||
* Specifying base constructor arguments multiple times in the same inheritance
|
||||
hierarchy is now disallowed.
|
||||
|
||||
* Calling a constructor with arguments but with wrong argument count is now
|
||||
disallowed. If you only want to specify an inheritance relation without
|
||||
giving arguments, do not provide parentheses at all.
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
* Function ``callcode`` is now disallowed (in favor of ``delegatecall``). It
|
||||
is still possible to use it via inline assembly.
|
||||
|
||||
* ``suicide`` is now disallowed (in favor of ``selfdestruct``).
|
||||
|
||||
* ``sha3`` is now disallowed (in favor of ``keccak256``).
|
||||
|
||||
* ``throw`` is now disallowed (in favor of ``revert``, ``require`` and
|
||||
``assert``).
|
||||
|
||||
Conversions
|
||||
-----------
|
||||
|
||||
* Explicit and implicit conversions from decimal literals to ``bytesXX`` types
|
||||
is now disallowed.
|
||||
|
||||
* Explicit and implicit conversions from hex literals to ``bytesXX`` types
|
||||
of different size is now disallowed.
|
||||
|
||||
Literals and Suffixes
|
||||
---------------------
|
||||
|
||||
* The unit denomination ``years`` is now disallowed due to complications and
|
||||
confusions about leap years.
|
||||
|
||||
* Trailing dots that are not followed by a number are now disallowed.
|
||||
|
||||
* Combining hex numbers with unit denominations (e.g. ``0x1e wei``) is now
|
||||
disallowed.
|
||||
|
||||
* The prefix ``0X`` for hex numbers is disallowed, only ``0x`` is possible.
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
* Declaring empty structs is now disallowed for clarity.
|
||||
|
||||
* The ``var`` keyword is now disallowed to favor explicitness.
|
||||
|
||||
* Assignments between tuples with different number of components is now
|
||||
disallowed.
|
||||
|
||||
* Values for constants that are not compile-time constants are disallowed.
|
||||
|
||||
* Multi-variable declarations with mismatching number of values are now
|
||||
disallowed.
|
||||
|
||||
* Uninitialized storage variables are now disallowed.
|
||||
|
||||
* Empty tuple components are now disallowed.
|
||||
|
||||
* Detecting cyclic dependencies in variables and structs is limited in
|
||||
recursion to 256.
|
||||
|
||||
* Fixed-size arrays with a length of zero are now disallowed.
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
* Using ``constant`` as function state mutability modifier is now disallowed.
|
||||
|
||||
* Boolean expressions cannot use arithmetic operations.
|
||||
|
||||
* The unary ``+`` operator is now disallowed.
|
||||
|
||||
* Literals cannot anymore be used with ``abi.encodePacked`` without prior
|
||||
conversion to an explicit type.
|
||||
|
||||
* Empty return statements for functions with one or more return values are now
|
||||
disallowed.
|
||||
|
||||
* The "loose assembly" syntax is now disallowed entirely, that is, jump labels,
|
||||
jumps and non-functional instructions cannot be used anymore. Use the new
|
||||
``while``, ``switch`` and ``if`` constructs instead.
|
||||
|
||||
* Functions without implementation cannot use modifiers anymore.
|
||||
|
||||
* Function types with named return values are now disallowed.
|
||||
|
||||
* Single statement variable declarations inside if/while/for bodies that are
|
||||
not blocks are now disallowed.
|
||||
|
||||
* New keywords: ``calldata`` and ``constructor``.
|
||||
|
||||
* New reserved keywords: ``alias``, ``apply``, ``auto``, ``copyof``,
|
||||
``define``, ``immutable``, ``implements``, ``macro``, ``mutable``,
|
||||
``override``, ``partial``, ``promise``, ``reference``, ``sealed``,
|
||||
``sizeof``, ``supports``, ``typedef`` and ``unchecked``.
|
||||
|
||||
.. _interoperability:
|
||||
|
||||
Interoperability With Older Contracts
|
||||
=====================================
|
||||
|
||||
It is still possible to interface with contracts written for Solidity versions prior to
|
||||
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:
|
||||
|
||||
::
|
||||
|
||||
// This will not compile with the current version of the compiler
|
||||
pragma solidity ^0.4.25;
|
||||
contract OldContract {
|
||||
function someOldFunction(uint8 a) {
|
||||
//...
|
||||
}
|
||||
function anotherOldFunction() constant returns (bool) {
|
||||
//...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
|
||||
This will no longer compile with Solidity v0.5.0. However, you can define a compatible interface for it:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
interface OldContract {
|
||||
function someOldFunction(uint8 a) external;
|
||||
function anotherOldFunction() external returns (bool);
|
||||
}
|
||||
|
||||
Note that we did not declare ``anotherOldFunction`` to be ``view``, despite it being declared ``constant`` in the original
|
||||
contract. This is due to the fact that starting with Solidity v0.5.0 ``staticcall`` is used to call ``view`` functions.
|
||||
Prior to v0.5.0 the ``constant`` keyword was not enforced, so calling a function declared ``constant`` with ``staticcall``
|
||||
may still revert, since the ``constant`` function may still attempt to modify storage. Consequently, when defining an
|
||||
interface for older contracts, you should only use ``view`` in place of ``constant`` in case you are absolutely sure that
|
||||
the function will work with ``staticcall``.
|
||||
|
||||
Given the interface defined above, you can now easily use the already deployed pre-0.5.0 contract:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
interface OldContract {
|
||||
function someOldFunction(uint8 a) external;
|
||||
function anotherOldFunction() external returns (bool);
|
||||
}
|
||||
|
||||
contract NewContract {
|
||||
function doSomething(OldContract a) public returns (bool) {
|
||||
a.someOldFunction(0x42);
|
||||
return a.anotherOldFunction();
|
||||
}
|
||||
}
|
||||
|
||||
Similarly, pre-0.5.0 libraries can be used by defining the functions of the library without implementation and
|
||||
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):
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
library OldLibrary {
|
||||
function someFunction(uint8 a) public returns(bool);
|
||||
}
|
||||
|
||||
contract NewContract {
|
||||
function f(uint8 a) public returns (bool) {
|
||||
return OldLibrary.someFunction(a);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
The following example shows a contract and its updated version for Solidity
|
||||
v0.5.0 with some of the changes listed in this section.
|
||||
|
||||
Old version:
|
||||
|
||||
::
|
||||
|
||||
// This will not compile
|
||||
pragma solidity ^0.4.25;
|
||||
|
||||
contract OtherContract {
|
||||
uint x;
|
||||
function f(uint y) external {
|
||||
x = y;
|
||||
}
|
||||
function() payable external {}
|
||||
}
|
||||
|
||||
contract Old {
|
||||
OtherContract other;
|
||||
uint myNumber;
|
||||
|
||||
// Function mutability not provided, not an error.
|
||||
function someInteger() internal returns (uint) { return 2; }
|
||||
|
||||
// Function visibility not provided, not an error.
|
||||
// Function mutability not provided, not an error.
|
||||
function f(uint x) returns (bytes) {
|
||||
// Var is fine in this version.
|
||||
var z = someInteger();
|
||||
x += z;
|
||||
// Throw is fine in this version.
|
||||
if (x > 100)
|
||||
throw;
|
||||
bytes b = new bytes(x);
|
||||
y = -3 >> 1;
|
||||
// y == -1 (wrong, should be -2)
|
||||
do {
|
||||
x += 1;
|
||||
if (x > 10) continue;
|
||||
// 'Continue' causes an infinite loop.
|
||||
} while (x < 11);
|
||||
// Call returns only a Bool.
|
||||
bool success = address(other).call("f");
|
||||
if (!success)
|
||||
revert();
|
||||
else {
|
||||
// Local variables could be declared after their use.
|
||||
int y;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// No need for an explicit data location for 'arr'
|
||||
function g(uint[] arr, bytes8 x, OtherContract otherContract) public {
|
||||
otherContract.transfer(1 ether);
|
||||
|
||||
// Since uint32 (4 bytes) is smaller than bytes8 (8 bytes),
|
||||
// the first 4 bytes of x will be lost. This might lead to
|
||||
// unexpected behavior since bytesX are right padded.
|
||||
uint32 y = uint32(x);
|
||||
myNumber += y + msg.value;
|
||||
}
|
||||
}
|
||||
|
||||
New version:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
contract OtherContract {
|
||||
uint x;
|
||||
function f(uint y) external {
|
||||
x = y;
|
||||
}
|
||||
function() payable external {}
|
||||
}
|
||||
|
||||
contract New {
|
||||
OtherContract other;
|
||||
uint myNumber;
|
||||
|
||||
// Function mutability must be specified.
|
||||
function someInteger() internal pure returns (uint) { return 2; }
|
||||
|
||||
// Function visibility must be specified.
|
||||
// Function mutability must be specified.
|
||||
function f(uint x) public returns (bytes memory) {
|
||||
// The type must now be explicitly given.
|
||||
uint z = someInteger();
|
||||
x += z;
|
||||
// Throw is now disallowed.
|
||||
require(x > 100);
|
||||
int y = -3 >> 1;
|
||||
// y == -2 (correct)
|
||||
do {
|
||||
x += 1;
|
||||
if (x > 10) continue;
|
||||
// 'Continue' jumps to the condition below.
|
||||
} while (x < 11);
|
||||
|
||||
// Call returns (bool, bytes).
|
||||
// Data location must be specified.
|
||||
(bool success, bytes memory data) = address(other).call("f");
|
||||
if (!success)
|
||||
revert();
|
||||
return data;
|
||||
}
|
||||
|
||||
using address_make_payable for address;
|
||||
// Data location for 'arr' must be specified
|
||||
function g(uint[] memory arr, bytes8 x, OtherContract otherContract, address unknownContract) public payable {
|
||||
// 'otherContract.transfer' is not provided.
|
||||
// Since the code of 'OtherContract' is known and has the fallback
|
||||
// function, address(otherContract) has type 'address payable'.
|
||||
address(otherContract).transfer(1 ether);
|
||||
|
||||
// 'unknownContract.transfer' is not provided.
|
||||
// 'address(unknownContract).transfer' is not provided
|
||||
// since 'address(unknownContract)' is not 'address payable'.
|
||||
// If the function takes an 'address' which you want to send
|
||||
// funds to, you can convert it to 'address payable' via 'uint160'.
|
||||
// Note: This is not recommended and the explicit type
|
||||
// 'address payable' should be used whenever possible.
|
||||
// To increase clarity, we suggest the use of a library for
|
||||
// the conversion (provided after the contract in this example).
|
||||
address payable addr = unknownContract.make_payable();
|
||||
require(addr.send(1 ether));
|
||||
|
||||
// Since uint32 (4 bytes) is smaller than bytes8 (8 bytes),
|
||||
// the conversion is not allowed.
|
||||
// We need to convert to a common size first:
|
||||
bytes4 x4 = bytes4(x); // Padding happens on the right
|
||||
uint32 y = uint32(x4); // Conversion is consistent
|
||||
// 'msg.value' cannot be used in a 'non-payable' function.
|
||||
// We need to make the function payable
|
||||
myNumber += y + msg.value;
|
||||
}
|
||||
}
|
||||
|
||||
// We can define a library for explicitly converting ``address``
|
||||
// to ``address payable`` as a workaround.
|
||||
library address_make_payable {
|
||||
function make_payable(address x) internal pure returns (address payable) {
|
||||
return address(uint160(x));
|
||||
}
|
||||
}
|
14
docs/_static/css/custom.css
vendored
Normal file
14
docs/_static/css/custom.css
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
pre {
|
||||
white-space: pre-wrap; /* css-3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.wy-table-responsive table td, .wy-table-responsive table th {
|
||||
white-space: normal;
|
||||
}
|
||||
.rst-content table.docutils td {
|
||||
vertical-align: top;
|
||||
}
|
6
docs/_templates/layout.html
vendored
Normal file
6
docs/_templates/layout.html
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{% extends "!layout.html" %}
|
||||
|
||||
{% block menu %}
|
||||
{{ super() }}
|
||||
<a href="genindex.html">Keyword Index</a>
|
||||
{% endblock %}
|
@ -2,20 +2,20 @@
|
||||
|
||||
.. _ABI:
|
||||
|
||||
******************************************
|
||||
Application Binary Interface Specification
|
||||
******************************************
|
||||
**************************
|
||||
Contract ABI Specification
|
||||
**************************
|
||||
|
||||
Basic Design
|
||||
============
|
||||
|
||||
The Application Binary Interface is the standard way to interact with contracts in the Ethereum ecosystem, both
|
||||
The Contract Application Binary Interface (ABI) is the standard way to interact with contracts in the Ethereum ecosystem, both
|
||||
from outside the blockchain and for contract-to-contract interaction. Data is encoded according to its type,
|
||||
as described in this specification. The encoding is not self describing and thus requires a schema in order to decode.
|
||||
|
||||
We assume the interface functions of a contract are strongly typed, known at compilation time and static. No introspection mechanism will be provided. We assume that all contracts will have the interface definitions of any contracts they call available at compile-time.
|
||||
We assume the interface functions of a contract are strongly typed, known at compilation time and static. We assume that all contracts will have the interface definitions of any contracts they call available at compile-time.
|
||||
|
||||
This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. Should these cases become important they can be adequately handled as facilities built within the Ethereum ecosystem.
|
||||
This specification does not address contracts whose interface is dynamic or otherwise known only at run-time.
|
||||
|
||||
.. _abi_function_selector:
|
||||
|
||||
@ -23,12 +23,12 @@ Function Selector
|
||||
=================
|
||||
|
||||
The first four bytes of the call data for a function call specifies the function to be called. It is the
|
||||
first (left, high-order in big-endian) four bytes of the Keccak (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype, i.e.
|
||||
first (left, high-order in big-endian) four bytes of the Keccak-256 (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype without data location specifier, i.e.
|
||||
the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used.
|
||||
|
||||
.. note::
|
||||
The return type of a function is not part of this signature. In :ref:`Solidity's function overloading <overload-function>` return types are not considered. The reason is to keep function call resolution context-independent.
|
||||
The JSON description of the ABI however contains both inputs and outputs. See (the :ref:`JSON ABI <abi_json>`)
|
||||
The :ref:`JSON description of the ABI<abi_json>` however contains both inputs and outputs.
|
||||
|
||||
Argument Encoding
|
||||
=================
|
||||
@ -72,27 +72,48 @@ The following non-fixed-size types exist:
|
||||
|
||||
- ``<type>[]``: a variable-length array of elements of the given type.
|
||||
|
||||
Types can be combined to a tuple by enclosing a finite non-negative number
|
||||
of them inside parentheses, separated by commas:
|
||||
Types can be combined to a tuple by enclosing them inside parentheses, separated by commas:
|
||||
|
||||
- ``(T1,T2,...,Tn)``: tuple consisting of the types ``T1``, ..., ``Tn``, ``n >= 0``
|
||||
|
||||
It is possible to form tuples of tuples, arrays of tuples and so on. It is also possible to form zero-tuples (where ``n == 0``).
|
||||
|
||||
.. note::
|
||||
Solidity supports all the types presented above with the same names with the exception of tuples. The ABI tuple type is utilised for encoding Solidity ``structs``.
|
||||
Mapping Solidity to ABI types
|
||||
-----------------------------
|
||||
|
||||
Formal Specification of the Encoding
|
||||
====================================
|
||||
Solidity supports all the types presented above with the same names with the
|
||||
exception of tuples. On the other hand, some Solidity types are not supported
|
||||
by the ABI. The following table shows on the left column Solidity types that
|
||||
are not part of the ABI, and on the right column the ABI types that represent
|
||||
them.
|
||||
|
||||
We will now formally specify the encoding, such that it will have the following
|
||||
properties, which are especially useful if some arguments are nested arrays:
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
| Solidity | ABI |
|
||||
+===============================+=============================================================================+
|
||||
|:ref:`address payable<address>`|``address`` |
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|:ref:`contract<contracts>` |``address`` |
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|:ref:`enum<enums>` |smallest ``uint`` type that is large enough to hold all values |
|
||||
| | |
|
||||
| |For example, an ``enum`` of 255 values or less is mapped to ``uint8`` and |
|
||||
| |an ``enum`` of 256 values is mapped to ``uint16``. |
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|:ref:`struct<structs>` |``tuple`` |
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|
||||
Properties:
|
||||
Design Criteria for the Encoding
|
||||
================================
|
||||
|
||||
The encoding is designed to have the following properties, which are especially useful if some arguments are nested arrays:
|
||||
|
||||
1. The number of reads necessary to access a value is at most the depth of the value inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a previous version of the ABI, the number of reads scaled linearly with the total number of dynamic parameters in the worst case.
|
||||
|
||||
2. The data of a variable or array element is not interleaved with other data and it is relocatable, i.e. it only uses relative "addresses"
|
||||
2. The data of a variable or array element is not interleaved with other data and it is relocatable, i.e. it only uses relative "addresses".
|
||||
|
||||
|
||||
Formal Specification of the Encoding
|
||||
====================================
|
||||
|
||||
We distinguish static and dynamic types. Static types are encoded in-place and dynamic types are encoded at a separately allocated location after the current block.
|
||||
|
||||
@ -152,8 +173,8 @@ on the type of ``X`` being
|
||||
- ``bytes``, of length ``k`` (which is assumed to be of type ``uint256``):
|
||||
|
||||
``enc(X) = enc(k) pad_right(X)``, i.e. the number of bytes is encoded as a
|
||||
``uint256`` followed by the actual value of ``X`` as a byte sequence, followed by
|
||||
the minimum number of zero-bytes such that ``len(enc(X))`` is a multiple of 32.
|
||||
``uint256`` followed by the actual value of ``X`` as a byte sequence, followed by
|
||||
the minimum number of zero-bytes such that ``len(enc(X))`` is a multiple of 32.
|
||||
|
||||
- ``string``:
|
||||
|
||||
@ -191,12 +212,12 @@ Given the contract:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
contract Foo {
|
||||
function bar(bytes3[2]) public pure {}
|
||||
function bar(bytes3[2] memory) public pure {}
|
||||
function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; }
|
||||
function sam(bytes, bool, uint[]) public pure {}
|
||||
function sam(bytes memory, bool, uint[] memory) public pure {}
|
||||
}
|
||||
|
||||
|
||||
@ -206,7 +227,9 @@ Thus for our ``Foo`` example if we wanted to call ``baz`` with the parameters ``
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000045``: the first parameter, a uint32 value ``69`` padded to 32 bytes
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000001``: the second parameter - boolean ``true``, padded to 32 bytes
|
||||
|
||||
In total::
|
||||
In total:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001
|
||||
|
||||
@ -218,7 +241,9 @@ If we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass
|
||||
- ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first parameter, a ``bytes3`` value ``"abc"`` (left-aligned).
|
||||
- ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first parameter, a ``bytes3`` value ``"def"`` (left-aligned).
|
||||
|
||||
In total::
|
||||
In total:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
@ -235,7 +260,9 @@ If we wanted to call ``sam`` with the arguments ``"dave"``, ``true`` and ``[1,2,
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002``: the second entry of the third parameter.
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003``: the third entry of the third parameter.
|
||||
|
||||
In total::
|
||||
In total:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003
|
||||
|
||||
@ -265,7 +292,7 @@ Finally, we encode the data part of the second dynamic argument, ``"Hello, world
|
||||
|
||||
All together, the encoding is (newline after function selector and each 32-bytes for clarity):
|
||||
|
||||
::
|
||||
.. code-block:: none
|
||||
|
||||
0x8be65246
|
||||
0000000000000000000000000000000000000000000000000000000000000123
|
||||
@ -278,6 +305,108 @@ All together, the encoding is (newline after function selector and each 32-bytes
|
||||
000000000000000000000000000000000000000000000000000000000000000d
|
||||
48656c6c6f2c20776f726c642100000000000000000000000000000000000000
|
||||
|
||||
Let us apply the same principle to encode the data for a function with a signature ``g(uint[][],string[])`` with values ``([[1, 2], [3]], ["one", "two", "three"])`` but start from the most atomic parts of the encoding:
|
||||
|
||||
First we encode the length and data of the first embedded dynamic array ``[1, 2]`` of the first root array ``[[1, 2], [3]]``:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first array, 2; the elements themselves are ``1`` and ``2``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (first element)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (second element)
|
||||
|
||||
Then we encode the length and data of the second embedded dynamic array ``[3]`` of the first root array ``[[1, 2], [3]]``:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (number of elements in the second array, 1; the element is ``3``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (first element)
|
||||
|
||||
Then we need to find the offsets ``a`` and ``b`` for their respective dynamic arrays ``[1, 2]`` and ``[3]``. To calculate the offsets we can take a look at the encoded data of the first root array ``[[1, 2], [3]]`` enumerating each line in the encoding:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
0 - a - offset of [1, 2]
|
||||
1 - b - offset of [3]
|
||||
2 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [1, 2]
|
||||
3 - 0000000000000000000000000000000000000000000000000000000000000001 - encoding of 1
|
||||
4 - 0000000000000000000000000000000000000000000000000000000000000002 - encoding of 2
|
||||
5 - 0000000000000000000000000000000000000000000000000000000000000001 - count for [3]
|
||||
6 - 0000000000000000000000000000000000000000000000000000000000000003 - encoding of 3
|
||||
|
||||
Offset ``a`` points to the start of the content of the array ``[1, 2]`` which is line 2 (64 bytes); thus ``a = 0x0000000000000000000000000000000000000000000000000000000000000040``.
|
||||
|
||||
Offset ``b`` points to the start of the content of the array ``[3]`` which is line 5 (160 bytes); thus ``b = 0x00000000000000000000000000000000000000000000000000000000000000a0``.
|
||||
|
||||
|
||||
Then we encode the embedded strings of the second root array:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"one"``)
|
||||
- ``0x6f6e650000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"one"``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"two"``)
|
||||
- ``0x74776f0000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"two"``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000005`` (number of characters in word ``"three"``)
|
||||
- ``0x7468726565000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"three"``)
|
||||
|
||||
In parallel to the first root array, since strings are dynamic elements we need to find their offsets ``c``, ``d`` and ``e``:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
0 - c - offset for "one"
|
||||
1 - d - offset for "two"
|
||||
2 - e - offset for "three"
|
||||
3 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "one"
|
||||
4 - 6f6e650000000000000000000000000000000000000000000000000000000000 - encoding of "one"
|
||||
5 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "two"
|
||||
6 - 74776f0000000000000000000000000000000000000000000000000000000000 - encoding of "two"
|
||||
7 - 0000000000000000000000000000000000000000000000000000000000000005 - count for "three"
|
||||
8 - 7468726565000000000000000000000000000000000000000000000000000000 - encoding of "three"
|
||||
|
||||
Offset ``c`` points to the start of the content of the string ``"one"`` which is line 3 (96 bytes); thus ``c = 0x0000000000000000000000000000000000000000000000000000000000000060``.
|
||||
|
||||
Offset ``d`` points to the start of the content of the string ``"two"`` which is line 5 (160 bytes); thus ``d = 0x00000000000000000000000000000000000000000000000000000000000000a0``.
|
||||
|
||||
Offset ``e`` points to the start of the content of the string ``"three"`` which is line 7 (224 bytes); thus ``e = 0x00000000000000000000000000000000000000000000000000000000000000e0``.
|
||||
|
||||
|
||||
Note that the encodings of the embedded elements of the root arrays are not dependent on each other and have the same encodings for a function with a signature ``g(string[],uint[][])``.
|
||||
|
||||
Then we encode the length of the first root array:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first root array, 2; the elements themselves are ``[1, 2]`` and ``[3]``)
|
||||
|
||||
Then we encode the length of the second root array:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of strings in the second root array, 3; the strings themselves are ``"one"``, ``"two"`` and ``"three"``)
|
||||
|
||||
Finally we find the offsets ``f`` and ``g`` for their respective root dynamic arrays ``[[1, 2], [3]]`` and ``["one", "two", "three"]``, and assemble parts in the correct order:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
0x2289b18c - function signature
|
||||
0 - f - offset of [[1, 2], [3]]
|
||||
1 - g - offset of ["one", "two", "three"]
|
||||
2 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [[1, 2], [3]]
|
||||
3 - 0000000000000000000000000000000000000000000000000000000000000040 - offset of [1, 2]
|
||||
4 - 00000000000000000000000000000000000000000000000000000000000000a0 - offset of [3]
|
||||
5 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [1, 2]
|
||||
6 - 0000000000000000000000000000000000000000000000000000000000000001 - encoding of 1
|
||||
7 - 0000000000000000000000000000000000000000000000000000000000000002 - encoding of 2
|
||||
8 - 0000000000000000000000000000000000000000000000000000000000000001 - count for [3]
|
||||
9 - 0000000000000000000000000000000000000000000000000000000000000003 - encoding of 3
|
||||
10 - 0000000000000000000000000000000000000000000000000000000000000003 - count for ["one", "two", "three"]
|
||||
11 - 0000000000000000000000000000000000000000000000000000000000000060 - offset for "one"
|
||||
12 - 00000000000000000000000000000000000000000000000000000000000000a0 - offset for "two"
|
||||
13 - 00000000000000000000000000000000000000000000000000000000000000e0 - offset for "three"
|
||||
14 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "one"
|
||||
15 - 6f6e650000000000000000000000000000000000000000000000000000000000 - encoding of "one"
|
||||
16 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "two"
|
||||
17 - 74776f0000000000000000000000000000000000000000000000000000000000 - encoding of "two"
|
||||
18 - 0000000000000000000000000000000000000000000000000000000000000005 - count for "three"
|
||||
19 - 7468726565000000000000000000000000000000000000000000000000000000 - encoding of "three"
|
||||
|
||||
Offset ``f`` points to the start of the content of the array ``[[1, 2], [3]]`` which is line 2 (64 bytes); thus ``f = 0x0000000000000000000000000000000000000000000000000000000000000040``.
|
||||
|
||||
Offset ``g`` points to the start of the content of the array ``["one", "two", "three"]`` which is line 10 (320 bytes); thus ``g = 0x0000000000000000000000000000000000000000000000000000000000000140``.
|
||||
|
||||
.. _abi_events:
|
||||
|
||||
Events
|
||||
======
|
||||
|
||||
@ -292,7 +421,7 @@ In effect, a log entry using this ABI is described as:
|
||||
- ``topics[n]``: ``EVENT_INDEXED_ARGS[n - 1]`` (``EVENT_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are indexed);
|
||||
- ``data``: ``abi_serialise(EVENT_NON_INDEXED_ARGS)`` (``EVENT_NON_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are not indexed, ``abi_serialise`` is the ABI serialisation function used for returning a series of typed values from a function, as described above).
|
||||
|
||||
For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value.
|
||||
For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the packed encoded value (see :ref:`abi_packed_mode`), rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value.
|
||||
|
||||
.. _abi_json:
|
||||
|
||||
@ -311,15 +440,19 @@ A function description is a JSON object with the fields:
|
||||
* ``components``: used for tuple types (more below).
|
||||
|
||||
- ``outputs``: an array of objects similar to ``inputs``, can be omitted if function doesn't return anything;
|
||||
- ``payable``: ``true`` if function accepts ether, defaults to ``false``;
|
||||
- ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state <pure-functions>`), ``view`` (:ref:`specified to not modify the blockchain state <view-functions>`), ``nonpayable`` and ``payable`` (same as ``payable`` above).
|
||||
- ``constant``: ``true`` if function is either ``pure`` or ``view``
|
||||
- ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state <pure-functions>`), ``view`` (:ref:`specified to not modify the blockchain state <view-functions>`), ``nonpayable`` (function does not accept Ether) and ``payable`` (function accepts Ether);
|
||||
- ``payable``: ``true`` if function accepts Ether, ``false`` otherwise;
|
||||
- ``constant``: ``true`` if function is either ``pure`` or ``view``, ``false`` otherwise.
|
||||
|
||||
``type`` can be omitted, defaulting to ``"function"``.
|
||||
``type`` can be omitted, defaulting to ``"function"``, likewise ``payable`` and ``constant`` can be omitted, both defaulting to ``false``.
|
||||
|
||||
Constructor and fallback function never have ``name`` or ``outputs``. Fallback function doesn't have ``inputs`` either.
|
||||
|
||||
Sending non-zero ether to non-payable function will throw. Don't do it.
|
||||
.. warning::
|
||||
The fields ``constant`` and ``payable`` are deprecated and will be removed in the future. Instead, the ``stateMutability`` field can be used to determine the same properties.
|
||||
|
||||
.. note::
|
||||
Sending non-zero Ether to non-payable function will revert the transaction.
|
||||
|
||||
An event description is a JSON object with fairly similar fields:
|
||||
|
||||
@ -338,19 +471,19 @@ For example,
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
contract Test {
|
||||
function Test() public { b = 0x12345678901234567890123456789012; }
|
||||
constructor() public { b = hex"12345678901234567890123456789012"; }
|
||||
event Event(uint indexed a, bytes32 b);
|
||||
event Event2(uint indexed a, bytes32 b);
|
||||
function foo(uint a) public { Event(a, b); }
|
||||
function foo(uint a) public { emit Event(a, b); }
|
||||
bytes32 b;
|
||||
}
|
||||
|
||||
would result in the JSON:
|
||||
|
||||
.. code:: json
|
||||
.. code-block:: json
|
||||
|
||||
[{
|
||||
"type":"event",
|
||||
@ -385,19 +518,19 @@ As an example, the code
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.19;
|
||||
pragma solidity >=0.4.19 <0.6.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Test {
|
||||
struct S { uint a; uint[] b; T[] c; }
|
||||
struct T { uint x; uint y; }
|
||||
function f(S s, T t, uint a) public { }
|
||||
function g() public returns (S s, T t, uint a) {}
|
||||
function f(S memory s, T memory t, uint a) public;
|
||||
function g() public returns (S memory s, T memory t, uint a);
|
||||
}
|
||||
|
||||
would result in the JSON:
|
||||
|
||||
.. code:: json
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
@ -457,19 +590,31 @@ would result in the JSON:
|
||||
|
||||
.. _abi_packed_mode:
|
||||
|
||||
Strict Encoding Mode
|
||||
====================
|
||||
|
||||
Strict encoding mode is the mode that leads to exactly the same encoding as defined in the formal specification above.
|
||||
This means offsets have to be as small as possible while still not creating overlaps in the data areas and thus no gaps are
|
||||
allowed.
|
||||
|
||||
Usually, ABI decoders are written in a straigthforward way just following offset pointers, but some decoders
|
||||
might enforce strict mode. The Solidity ABI decoder currently does not enforce strict mode, but the encoder
|
||||
always creates data in strict mode.
|
||||
|
||||
Non-standard Packed Mode
|
||||
========================
|
||||
|
||||
Solidity supports a non-standard packed mode where:
|
||||
Through ``abi.encodePacked()``, Solidity supports a non-standard packed mode where:
|
||||
|
||||
- no :ref:`function selector <abi_function_selector>` is encoded,
|
||||
- types shorter than 32 bytes are neither zero padded nor sign extended and
|
||||
- dynamic types are encoded in-place and without the length.
|
||||
|
||||
As an example encoding ``int1, bytes1, uint16, string`` with values ``-1, 0x42, 0x2424, "Hello, world!"`` results in ::
|
||||
As an example encoding ``int8, bytes1, uint16, string`` with values ``-1, 0x42, 0x2424, "Hello, world!"`` results in:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
0xff42242448656c6c6f2c20776f726c6421
|
||||
^^ int1(-1)
|
||||
^^ int8(-1)
|
||||
^^ bytes1(0x42)
|
||||
^^^^ uint16(0x2424)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field
|
||||
@ -478,3 +623,8 @@ More specifically, each statically-sized type takes as many bytes as its range h
|
||||
and dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded without
|
||||
their length field. This means that the encoding is ambiguous as soon as there are two
|
||||
dynamically-sized elements.
|
||||
|
||||
If padding is needed, explicit type conversions can be used: ``abi.encodePacked(uint16(0x12)) == hex"0012"``.
|
||||
|
||||
Since packed encoding is not used when calling functions, there is no special support
|
||||
for prepending a function selector.
|
||||
|
@ -4,57 +4,82 @@ Solidity Assembly
|
||||
|
||||
.. index:: ! assembly, ! asm, ! evmasm
|
||||
|
||||
Solidity defines an assembly language that can also be used without Solidity.
|
||||
This assembly language can also be used as "inline assembly" inside Solidity
|
||||
source code. We start with describing how to use inline assembly and how it
|
||||
differs from standalone assembly and then specify assembly itself.
|
||||
Solidity defines an assembly language that you can use without Solidity and also
|
||||
as "inline assembly" inside Solidity source code. This guide starts with describing
|
||||
how to use inline assembly, how it differs from standalone assembly, and
|
||||
specifies assembly itself.
|
||||
|
||||
.. _inline-assembly:
|
||||
|
||||
Inline Assembly
|
||||
===============
|
||||
|
||||
For more fine-grained control especially in order to enhance the language by writing libraries,
|
||||
it is possible to interleave Solidity statements with inline assembly in a language close
|
||||
to the one of the virtual machine. Due to the fact that the EVM is a stack machine, it is
|
||||
often hard to address the correct stack slot and provide arguments to opcodes at the correct
|
||||
point on the stack. Solidity's inline assembly tries to facilitate that and other issues
|
||||
arising when writing manual assembly by the following features:
|
||||
You can interleave Solidity statements with inline assembly in a language close
|
||||
to the one of the virtual machine. This gives you more fine-grained control,
|
||||
especially when you are enhancing the language by writing libraries.
|
||||
|
||||
* functional-style opcodes: ``mul(1, add(2, 3))`` instead of ``push1 3 push1 2 add push1 1 mul``
|
||||
As the EVM is a stack machine, it is often hard to address the correct stack slot
|
||||
and provide arguments to opcodes at the correct point on the stack. Solidity's inline
|
||||
assembly helps you do this, and with other issues that arise when writing manual assembly.
|
||||
|
||||
Inline assembly has the following features:
|
||||
|
||||
* functional-style opcodes: ``mul(1, add(2, 3))``
|
||||
* assembly-local variables: ``let x := add(2, 3) let y := mload(0x40) x := add(x, y)``
|
||||
* access to external variables: ``function f(uint x) public { assembly { x := sub(x, 1) } }``
|
||||
* labels: ``let x := 10 repeat: x := sub(x, 1) jumpi(repeat, eq(x, 0))``
|
||||
* loops: ``for { let i := 0 } lt(i, x) { i := add(i, 1) } { y := mul(2, y) }``
|
||||
* if statements: ``if slt(x, 0) { x := sub(0, x) }``
|
||||
* switch statements: ``switch x case 0 { y := mul(x, 2) } default { y := 0 }``
|
||||
* function calls: ``function f(x) -> y { switch x case 0 { y := 1 } default { y := mul(x, f(sub(x, 1))) } }``
|
||||
|
||||
We now want to describe the inline assembly language in detail.
|
||||
|
||||
.. warning::
|
||||
Inline assembly is a way to access the Ethereum Virtual Machine
|
||||
at a low level. This discards several important safety
|
||||
features of Solidity.
|
||||
at a low level. This bypasses several important safety
|
||||
features and checks of Solidity. You should only use it for
|
||||
tasks that need it, and only if you are confident with using it.
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
Assembly parses comments, literals and identifiers in the same way as Solidity, so you can use the
|
||||
usual ``//`` and ``/* */`` comments. Inline assembly is marked by ``assembly { ... }`` and inside
|
||||
these curly braces, you can use the following (see the later sections for more details):
|
||||
|
||||
- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
|
||||
- opcodes in functional style, e.g. ``add(1, mlod(0))``
|
||||
- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of empty (0) is assigned)
|
||||
- identifiers (assembly-local variables and externals if used as inline assembly), e.g. ``add(3, x)``, ``sstore(x_slot, 2)``
|
||||
- assignments, e.g. ``x := add(y, 3)``
|
||||
- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }``
|
||||
|
||||
The following features are only available for standalone assembly:
|
||||
|
||||
- direct stack control via ``dup1``, ``swap1``, ...
|
||||
- direct stack assignments (in "instruction style"), e.g. ``3 =: x``
|
||||
- labels, e.g. ``name:``
|
||||
- jump opcodes
|
||||
|
||||
.. note::
|
||||
TODO: Write about how scoping rules of inline assembly are a bit different
|
||||
and the complications that arise when for example using internal functions
|
||||
of libraries. Furthermore, write about the symbols defined by the compiler.
|
||||
Standalone assembly is supported for backwards compatibility but is not documented
|
||||
here anymore.
|
||||
|
||||
At the end of the ``assembly { ... }`` block, the stack must be balanced,
|
||||
unless you require it otherwise. If it is not balanced, the compiler generates
|
||||
a warning.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
The following example provides library code to access the code of another contract and
|
||||
load it into a ``bytes`` variable. This is not possible at all with "plain Solidity" and the
|
||||
idea is that assembly libraries will be used to enhance the language in such ways.
|
||||
load it into a ``bytes`` variable. This is not possible with "plain Solidity" and the
|
||||
idea is that assembly libraries will be used to enhance the Solidity language.
|
||||
|
||||
.. code::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
library GetCode {
|
||||
function at(address _addr) public view returns (bytes o_code) {
|
||||
function at(address _addr) public view returns (bytes memory o_code) {
|
||||
assembly {
|
||||
// retrieve the size of the code, this needs assembly
|
||||
let size := extcodesize(_addr)
|
||||
@ -71,19 +96,17 @@ idea is that assembly libraries will be used to enhance the language in such way
|
||||
}
|
||||
}
|
||||
|
||||
Inline assembly could also be beneficial in cases where the optimizer fails to produce
|
||||
efficient code. Please be aware that assembly is much more difficult to write because
|
||||
the compiler does not perform checks, so you should use it for complex things only if
|
||||
you really know what you are doing.
|
||||
Inline assembly is also beneficial in cases where the optimizer fails to produce
|
||||
efficient code, for example:
|
||||
|
||||
.. code::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
library VectorSum {
|
||||
// This function is less efficient because the optimizer currently fails to
|
||||
// remove the bounds checks in array access.
|
||||
function sumSolidity(uint[] _data) public view returns (uint o_sum) {
|
||||
function sumSolidity(uint[] memory _data) public pure returns (uint o_sum) {
|
||||
for (uint i = 0; i < _data.length; ++i)
|
||||
o_sum += _data[i];
|
||||
}
|
||||
@ -91,7 +114,7 @@ you really know what you are doing.
|
||||
// We know that we only access the array in bounds, so we can avoid the check.
|
||||
// 0x20 needs to be added to an array because the first slot contains the
|
||||
// array length.
|
||||
function sumAsm(uint[] _data) public view returns (uint o_sum) {
|
||||
function sumAsm(uint[] memory _data) public pure returns (uint o_sum) {
|
||||
for (uint i = 0; i < _data.length; ++i) {
|
||||
assembly {
|
||||
o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
|
||||
@ -100,7 +123,7 @@ you really know what you are doing.
|
||||
}
|
||||
|
||||
// Same as above, but accomplish the entire code within inline assembly.
|
||||
function sumPureAsm(uint[] _data) public view returns (uint o_sum) {
|
||||
function sumPureAsm(uint[] memory _data) public pure returns (uint o_sum) {
|
||||
assembly {
|
||||
// Load the length (first 32 bytes)
|
||||
let len := mload(_data)
|
||||
@ -115,7 +138,7 @@ you really know what you are doing.
|
||||
|
||||
// Iterate until the bound is not met.
|
||||
for
|
||||
{ let end := add(data, len) }
|
||||
{ let end := add(data, mul(len, 0x20)) }
|
||||
lt(data, end)
|
||||
{ data := add(data, 0x20) }
|
||||
{
|
||||
@ -126,22 +149,7 @@ you really know what you are doing.
|
||||
}
|
||||
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
Assembly parses comments, literals and identifiers exactly as Solidity, so you can use the
|
||||
usual ``//`` and ``/* */`` comments. Inline assembly is marked by ``assembly { ... }`` and inside
|
||||
these curly braces, the following can be used (see the later sections for more details)
|
||||
|
||||
- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
|
||||
- opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below
|
||||
- opcodes in functional style, e.g. ``add(1, mlod(0))``
|
||||
- labels, e.g. ``name:``
|
||||
- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of empty (0) is assigned)
|
||||
- identifiers (labels or assembly-local variables and externals if used as inline assembly), e.g. ``jump(name)``, ``3 x add``
|
||||
- assignments (in "instruction style"), e.g. ``3 =: x``
|
||||
- assignments in functional style, e.g. ``x := add(y, 3)``
|
||||
- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }``
|
||||
.. _opcodes:
|
||||
|
||||
Opcodes
|
||||
-------
|
||||
@ -157,7 +165,7 @@ Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Hom
|
||||
Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception.
|
||||
|
||||
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
|
||||
(excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
|
||||
but not including position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
|
||||
|
||||
The opcodes ``pushi`` and ``jumpdest`` cannot be used directly.
|
||||
|
||||
@ -212,16 +220,14 @@ In the grammar, opcodes are represented as pre-defined identifiers.
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| sar(x, y) | | C | arithmetic shift right y by x bits |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics |
|
||||
| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetic |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics |
|
||||
| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetic |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| keccak256(p, n) | | F | keccak(mem[p...(p+n))) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| sha3(p, n) | | F | keccak(mem[p...(p+n))) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| jump(label) | `-` | F | jump to label / code position |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| jumpi(label, cond) | `-` | F | jump to label if cond is nonzero |
|
||||
@ -230,13 +236,13 @@ In the grammar, opcodes are represented as pre-defined identifiers.
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| pop(x) | `-` | F | remove the element pushed by x |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| dup1 ... dup16 | | F | copy ith stack slot to the top (counting from top) |
|
||||
| dup1 ... dup16 | | F | copy nth stack slot to the top (counting from top) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| swap1 ... swap16 | `*` | F | swap topmost and ith stack slot below it |
|
||||
| swap1 ... swap16 | `*` | F | swap topmost and nth stack slot below it |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mload(p) | | F | mem[p..(p+32)) |
|
||||
| mload(p) | | F | mem[p...(p+32)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mstore(p, v) | `-` | F | mem[p..(p+32)) := v |
|
||||
| mstore(p, v) | `-` | F | mem[p...(p+32)) := v |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
@ -274,16 +280,20 @@ In the grammar, opcodes are represented as pre-defined identifiers.
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| create(v, p, s) | | F | create new contract with code mem[p..(p+s)) and send v wei |
|
||||
| extcodehash(a) | | C | code hash of address a |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| create(v, p, n) | | F | create new contract with code mem[p...(p+n)) and send v wei |
|
||||
| | | | and return the new address |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| create2(v, n, p, s) | | C | create new contract with code mem[p..(p+s)) at address |
|
||||
| | | | keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v |
|
||||
| | | | wei and return the new address |
|
||||
| create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address |
|
||||
| | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) |
|
||||
| | | | and send v wei and return the new address, where ``0xff`` is a |
|
||||
| | | | 8 byte value, ``this`` is the current contract's address |
|
||||
| | | | as a 20 byte value and ``s`` is a big-endian 256-bit value |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| call(g, a, v, in, | | F | call contract at address a with input mem[in..(in+insize)) |
|
||||
| call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) |
|
||||
| insize, out, outsize) | | | providing g gas and v wei and output area |
|
||||
| | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
|
||||
| | | | mem[out...(out+outsize)) returning 0 on error (eg. out of gas) |
|
||||
| | | | and 1 on success |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay |
|
||||
@ -295,23 +305,23 @@ In the grammar, opcodes are represented as pre-defined identifiers.
|
||||
| staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do |
|
||||
| insize, out, outsize) | | | not allow state modifications |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| return(p, s) | `-` | F | end execution, return data mem[p..(p+s)) |
|
||||
| return(p, s) | `-` | F | end execution, return data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p..(p+s)) |
|
||||
| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| invalid | `-` | F | end execution with invalid instruction |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log0(p, s) | `-` | F | log without topics and data mem[p..(p+s)) |
|
||||
| log0(p, s) | `-` | F | log without topics and data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p..(p+s)) |
|
||||
| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p..(p+s)) |
|
||||
| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p..(p+s)) |
|
||||
| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p...(p+s)) |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) |
|
||||
| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p...(p+s)) |
|
||||
| t4) | | | |
|
||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||
| origin | | F | transaction sender |
|
||||
@ -337,75 +347,66 @@ Literals
|
||||
You can use integer constants by typing them in decimal or hexadecimal notation and an
|
||||
appropriate ``PUSHi`` instruction will automatically be generated. The following creates code
|
||||
to add 2 and 3 resulting in 5 and then computes the bitwise and with the string "abc".
|
||||
The final value is assigned to a local variable called ``x``.
|
||||
Strings are stored left-aligned and cannot be longer than 32 bytes.
|
||||
|
||||
.. code::
|
||||
|
||||
assembly { 2 3 add "abc" and }
|
||||
assembly { let x := and("abc", add(3, 2)) }
|
||||
|
||||
|
||||
Functional Style
|
||||
-----------------
|
||||
|
||||
You can type opcode after opcode in the same way they will end up in bytecode. For example
|
||||
adding ``3`` to the contents in memory at position ``0x80`` would be
|
||||
For a sequence of opcodes, it is often hard to see what the actual
|
||||
arguments for certain opcodes are. In the following example,
|
||||
``3`` is added to the contents in memory at position ``0x80``.
|
||||
|
||||
.. code::
|
||||
|
||||
3 0x80 mload add 0x80 mstore
|
||||
|
||||
As it is often hard to see what the actual arguments for certain opcodes are,
|
||||
Solidity inline assembly also provides a "functional style" notation where the same code
|
||||
would be written as follows
|
||||
Solidity inline assembly has a "functional style" notation where the same code
|
||||
would be written as follows:
|
||||
|
||||
.. code::
|
||||
|
||||
mstore(0x80, add(mload(0x80), 3))
|
||||
|
||||
Functional style expressions cannot use instructional style internally, i.e.
|
||||
``1 2 mstore(0x80, add)`` is not valid assembly, it has to be written as
|
||||
``mstore(0x80, add(2, 1))``. For opcodes that do not take arguments, the
|
||||
parentheses can be omitted.
|
||||
If you read the code from right to left, you end up with exactly the same
|
||||
sequence of constants and opcodes, but it is much clearer where the
|
||||
values end up.
|
||||
|
||||
Note that the order of arguments is reversed in functional-style as opposed to the instruction-style
|
||||
way. If you use functional-style, the first argument will end up on the stack top.
|
||||
If you care about the exact stack layout, just note that the
|
||||
syntactically first argument for a function or opcode will be put at the
|
||||
top of the stack.
|
||||
|
||||
Access to External Variables, Functions and Libraries
|
||||
-----------------------------------------------------
|
||||
|
||||
Access to External Variables and Functions
|
||||
------------------------------------------
|
||||
|
||||
Solidity variables and other identifiers can be accessed by simply using their name.
|
||||
For memory variables, this will push the address and not the value onto the
|
||||
stack. Storage variables are different: Values in storage might not occupy a
|
||||
full storage slot, so their "address" is composed of a slot and a byte-offset
|
||||
You can access Solidity variables and other identifiers by using their name.
|
||||
For variables stored in the memory data location, this pushes the address, and not the value
|
||||
onto the stack. Variables stored in the storage data location are different, as they might not
|
||||
occupy a full storage slot, so their "address" is composed of a slot and a byte-offset
|
||||
inside that slot. To retrieve the slot pointed to by the variable ``x``, you
|
||||
used ``x_slot`` and to retrieve the byte-offset you used ``x_offset``.
|
||||
use ``x_slot``, and to retrieve the byte-offset you use ``x_offset``.
|
||||
|
||||
In assignments (see below), we can even use local Solidity variables to assign to.
|
||||
|
||||
Functions external to inline assembly can also be accessed: The assembly will
|
||||
push their entry label (with virtual function resolution applied). The calling semantics
|
||||
in solidity are:
|
||||
|
||||
- the caller pushes ``return label``, ``arg1``, ``arg2``, ..., ``argn``
|
||||
- the call returns with ``ret1``, ``ret2``, ..., ``retm``
|
||||
|
||||
This feature is still a bit cumbersome to use, because the stack offset essentially
|
||||
changes during the call, and thus references to local variables will be wrong.
|
||||
Local Solidity variables are available for assignments, for example:
|
||||
|
||||
.. code::
|
||||
|
||||
pragma solidity ^0.4.11;
|
||||
pragma solidity >=0.4.11 <0.6.0;
|
||||
|
||||
contract C {
|
||||
uint b;
|
||||
function f(uint x) public returns (uint r) {
|
||||
function f(uint x) public view returns (uint r) {
|
||||
assembly {
|
||||
r := mul(x, sload(b_slot)) // ignore the offset, we know it is zero
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
.. warning::
|
||||
If you access variables of a type that spans less than 256 bits
|
||||
(for example ``uint64``, ``address``, ``bytes16`` or ``byte``),
|
||||
you cannot make any assumptions about bits not part of the
|
||||
@ -418,57 +419,8 @@ changes during the call, and thus references to local variables will be wrong.
|
||||
Labels
|
||||
------
|
||||
|
||||
.. note::
|
||||
Labels are deprecated. Please use functions, loops, if or switch statements instead.
|
||||
|
||||
Another problem in EVM assembly is that ``jump`` and ``jumpi`` use absolute addresses
|
||||
which can change easily. Solidity inline assembly provides labels to make the use of
|
||||
jumps easier. Note that labels are a low-level feature and it is possible to write
|
||||
efficient assembly without labels, just using assembly functions, loops, if and switch instructions
|
||||
(see below). The following code computes an element in the Fibonacci series.
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
let n := calldataload(4)
|
||||
let a := 1
|
||||
let b := a
|
||||
loop:
|
||||
jumpi(loopend, eq(n, 0))
|
||||
a add swap1
|
||||
n := sub(n, 1)
|
||||
jump(loop)
|
||||
loopend:
|
||||
mstore(0, a)
|
||||
return(0, 0x20)
|
||||
}
|
||||
|
||||
Please note that automatically accessing stack variables can only work if the
|
||||
assembler knows the current stack height. This fails to work if the jump source
|
||||
and target have different stack heights. It is still fine to use such jumps, but
|
||||
you should just not access any stack variables (even assembly variables) in that case.
|
||||
|
||||
Furthermore, the stack height analyser goes through the code opcode by opcode
|
||||
(and not according to control flow), so in the following case, the assembler
|
||||
will have a wrong impression about the stack height at label ``two``:
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
let x := 8
|
||||
jump(two)
|
||||
one:
|
||||
// Here the stack height is 2 (because we pushed x and 7),
|
||||
// but the assembler thinks it is 1 because it reads
|
||||
// from top to bottom.
|
||||
// Accessing the stack variable x here will lead to errors.
|
||||
x := 9
|
||||
jump(three)
|
||||
two:
|
||||
7 // push something onto the stack
|
||||
jump(one)
|
||||
three:
|
||||
}
|
||||
Support for labels has been removed in version 0.5.0 of Solidity.
|
||||
Please use functions, loops, if or switch statements instead.
|
||||
|
||||
Declaring Assembly-Local Variables
|
||||
----------------------------------
|
||||
@ -482,7 +434,7 @@ be just ``0``, but it can also be a complex functional-style expression.
|
||||
|
||||
.. code::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
contract C {
|
||||
function f(uint x) public view returns (uint b) {
|
||||
@ -506,26 +458,19 @@ Assignments are possible to assembly-local variables and to function-local
|
||||
variables. Take care that when you assign to variables that point to
|
||||
memory or storage, you will only change the pointer and not the data.
|
||||
|
||||
There are two kinds of assignments: functional-style and instruction-style.
|
||||
For functional-style assignments (``variable := value``), you need to provide a value in a
|
||||
functional-style expression that results in exactly one stack value
|
||||
and for instruction-style (``=: variable``), the value is just taken from the stack top.
|
||||
For both ways, the colon points to the name of the variable. The assignment
|
||||
is performed by replacing the variable's value on the stack by the new value.
|
||||
Variables can only be assigned expressions that result in exactly one value.
|
||||
If you want to assign the values returned from a function that has
|
||||
multiple return parameters, you have to provide multiple variables.
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
let v := 0 // functional-style assignment as part of variable declaration
|
||||
let v := 0
|
||||
let g := add(v, 2)
|
||||
sload(10)
|
||||
=: v // instruction style assignment, puts the result of sload(10) into v
|
||||
function f() -> a, b { }
|
||||
let c, d := f()
|
||||
}
|
||||
|
||||
.. note::
|
||||
Instruction-style assignment is deprecated.
|
||||
|
||||
|
||||
If
|
||||
--
|
||||
|
||||
@ -642,12 +587,9 @@ Things to Avoid
|
||||
Inline assembly might have a quite high-level look, but it actually is extremely
|
||||
low-level. Function calls, loops, ifs and switches are converted by simple
|
||||
rewriting rules and after that, the only thing the assembler does for you is re-arranging
|
||||
functional-style opcodes, managing jump labels, counting stack height for
|
||||
functional-style opcodes, counting stack height for
|
||||
variable access and removing stack slots for assembly-local variables when the end
|
||||
of their block is reached. Especially for those two last cases, it is important
|
||||
to know that the assembler only counts stack height from top to bottom, not
|
||||
necessarily following control flow. Furthermore, operations like swap will only
|
||||
swap the contents of the stack but not the location of variables.
|
||||
of their block is reached.
|
||||
|
||||
Conventions in Solidity
|
||||
-----------------------
|
||||
@ -662,20 +604,31 @@ first.
|
||||
|
||||
Solidity manages memory in a very simple way: There is a "free memory pointer"
|
||||
at position ``0x40`` in memory. If you want to allocate memory, just use the memory
|
||||
from that point on and update the pointer accordingly.
|
||||
starting from where this pointer points at and update it accordingly.
|
||||
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 that can be used for allocating memory::
|
||||
|
||||
function allocate(length) -> pos {
|
||||
pos := mload(0x40)
|
||||
mstore(0x40, add(pos, length))
|
||||
}
|
||||
|
||||
The first 64 bytes of memory can be used as "scratch space" for short-term
|
||||
allocation. The 32 bytes after the free memory pointer (i.e. starting at ``0x60``)
|
||||
is meant to be zero permanently and is used as the initial value for
|
||||
empty dynamic memory arrays.
|
||||
This means that the allocatable memory starts at ``0x80``, which is the initial value
|
||||
of the free memory pointer.
|
||||
|
||||
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (yes, this is
|
||||
even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory
|
||||
arrays are pointers to memory arrays. The length of a dynamic array is stored at the
|
||||
first slot of the array and then only the array elements follow.
|
||||
first slot of the array and followed by the array elements.
|
||||
|
||||
.. warning::
|
||||
Statically-sized memory arrays do not have a length field, but it will be added soon
|
||||
Statically-sized memory arrays do not have a length field, but it might be added later
|
||||
to allow better convertibility between statically- and dynamically-sized arrays, so
|
||||
please do not rely on that.
|
||||
|
||||
@ -710,7 +663,7 @@ Scoping: An identifier that is declared (label, variable, function, assembly)
|
||||
is only visible in the block where it was declared (including nested blocks
|
||||
inside the current block). It is not legal to access local variables across
|
||||
function borders, even if they would be in scope. Shadowing is not allowed.
|
||||
Local variables cannot be accessed before they were declared, but labels,
|
||||
Local variables cannot be accessed before they were declared, but
|
||||
functions and assemblies can. Assemblies are special blocks that are used
|
||||
for e.g. returning runtime code or creating contracts. No identifier from an
|
||||
outer assembly is visible in a sub-assembly.
|
||||
@ -721,7 +674,7 @@ Whenever a local variable is referenced, the code generator needs
|
||||
to know its current relative position in the stack and thus it needs to
|
||||
keep track of the current so-called stack height. Since all local variables
|
||||
are removed at the end of a block, the stack height before and after the block
|
||||
should be the same. If this is not the case, a warning is issued.
|
||||
should be the same. If this is not the case, compilation fails.
|
||||
|
||||
Using ``switch``, ``for`` and functions, it should be possible to write
|
||||
complex code without using ``jump`` or ``jumpi`` manually. This makes it much
|
||||
@ -738,7 +691,7 @@ Example:
|
||||
We will follow an example compilation from Solidity to assembly.
|
||||
We consider the runtime bytecode of the following Solidity program::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
contract C {
|
||||
function f(uint x) public pure returns (uint y) {
|
||||
@ -751,7 +704,7 @@ We consider the runtime bytecode of the following Solidity program::
|
||||
The following assembly will be generated::
|
||||
|
||||
{
|
||||
mstore(0x40, 0x60) // store the "free memory pointer"
|
||||
mstore(0x40, 0x80) // store the "free memory pointer"
|
||||
// function dispatcher
|
||||
switch div(calldataload(0), exp(2, 226))
|
||||
case 0xb3de648b {
|
||||
|
@ -17,12 +17,13 @@
|
||||
"check": {"ast-compact-json-path": "$..[?(@.nodeType === 'EventDefinition')]..[?(@.nodeType === 'UserDefinedTypeName' && @.typeDescriptions.typeString.startsWith('struct'))]"}
|
||||
},
|
||||
{
|
||||
"name": "PublicLibFunctionsDoNotReturnNestedArrays",
|
||||
"summary": "Calls to public library functions (internal functions are safe) that return nested arrays return only zeroes.",
|
||||
"description": "The compiler does not complain about public library functions (internal functions are safe) returning nested arrays, but it also does not return it correctly. Thus, the function caller receives only zeroes.",
|
||||
"introduced": "0.4.11",
|
||||
"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.",
|
||||
"introduced": "0.1.4",
|
||||
"fixed": "0.4.22",
|
||||
"severity": "low"
|
||||
"severity": "medium",
|
||||
"check": {"regex-source": "returns[^;{]*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\]\\s*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\][^{;]*[;{]"}
|
||||
},
|
||||
{
|
||||
"name": "OneOfTwoConstructorsSkipped",
|
||||
@ -32,15 +33,6 @@
|
||||
"fixed": "0.4.23",
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"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.",
|
||||
"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][^\\]]*\\][^{;]*[;{]"}
|
||||
},
|
||||
{
|
||||
"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.",
|
||||
|
@ -57,15 +57,14 @@ conditions
|
||||
means that the optimizer has to be switched on to enable the bug.
|
||||
If no conditions are given, assume that the bug is present.
|
||||
check
|
||||
This field contains different checks that can be used to determine
|
||||
whether a smart contract
|
||||
This field contains different checks that report whether the smart contract
|
||||
contains the bug or not. The first type of check are Javascript regular
|
||||
expressions that are to be matched against the source code ("source-regex").
|
||||
If there is no match, then the bug is very likely
|
||||
expressions that are to be matched against the source code ("source-regex")
|
||||
if the bug is present. If there is no match, then the bug is very likely
|
||||
not present. If there is a match, the bug might be present. For improved
|
||||
accuracy, the checks should be applied to the source code after stripping
|
||||
comments.
|
||||
The second type of check are patterns to be applied to the compact AST of
|
||||
The second type of check are patterns to be checked on the compact AST of
|
||||
the Solidity program ("ast-compact-json-path"). The specified search query
|
||||
is a `JsonPath <https://github.com/json-path/JsonPath>`_ expression.
|
||||
If at least one path of the Solidity AST matches the query, the bug is
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,11 +13,11 @@ Withdrawal from Contracts
|
||||
The recommended method of sending funds after an effect
|
||||
is using the withdrawal pattern. Although the most intuitive
|
||||
method of sending Ether, as a result of an effect, is a
|
||||
direct ``send`` call, this is not recommended as it
|
||||
direct ``transfer`` call, this is not recommended as it
|
||||
introduces a potential security risk. You may read
|
||||
more about this on the :ref:`security_considerations` page.
|
||||
|
||||
This is an example of the withdrawal pattern in practice in
|
||||
The following is an example of the withdrawal pattern in practice in
|
||||
a contract where the goal is to send the most money to the
|
||||
contract in order to become the "richest", inspired by
|
||||
`King of the Ether <https://www.kingoftheether.com/>`_.
|
||||
@ -28,7 +28,7 @@ become the new richest.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.11;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
contract WithdrawalContract {
|
||||
address public richest;
|
||||
@ -36,7 +36,7 @@ become the new richest.
|
||||
|
||||
mapping (address => uint) pendingWithdrawals;
|
||||
|
||||
function WithdrawalContract() public payable {
|
||||
constructor() public payable {
|
||||
richest = msg.sender;
|
||||
mostSent = msg.value;
|
||||
}
|
||||
@ -65,13 +65,13 @@ This is as opposed to the more intuitive sending pattern:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.11;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
contract SendContract {
|
||||
address public richest;
|
||||
address payable public richest;
|
||||
uint public mostSent;
|
||||
|
||||
function SendContract() public payable {
|
||||
constructor() public payable {
|
||||
richest = msg.sender;
|
||||
mostSent = msg.value;
|
||||
}
|
||||
@ -93,7 +93,7 @@ Notice that, in this example, an attacker could trap the
|
||||
contract into an unusable state by causing ``richest`` to be
|
||||
the address of a contract that has a fallback function
|
||||
which fails (e.g. by using ``revert()`` or by just
|
||||
consuming more than the 2300 gas stipend). That way,
|
||||
consuming more than the 2300 gas stipend transferred to them). That way,
|
||||
whenever ``transfer`` is called to deliver funds to the
|
||||
"poisoned" contract, it will fail and thus also ``becomeRichest``
|
||||
will fail, with the contract being stuck forever.
|
||||
@ -130,7 +130,7 @@ restrictions highly readable.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.22;
|
||||
pragma solidity >=0.4.22 <0.6.0;
|
||||
|
||||
contract AccessRestriction {
|
||||
// These will be assigned at the construction
|
||||
@ -198,7 +198,7 @@ restrictions highly readable.
|
||||
);
|
||||
_;
|
||||
if (msg.value > _amount)
|
||||
msg.sender.send(msg.value - _amount);
|
||||
msg.sender.transfer(msg.value - _amount);
|
||||
}
|
||||
|
||||
function forceOwnerChange(address _newOwner)
|
||||
@ -282,7 +282,7 @@ function finishes.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.22;
|
||||
pragma solidity >=0.4.22 <0.6.0;
|
||||
|
||||
contract StateMachine {
|
||||
enum Stages {
|
||||
|
@ -24,9 +24,11 @@ import re
|
||||
def setup(sphinx):
|
||||
thisdir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, thisdir + '/utils')
|
||||
from SolidityLexer import SolidityLexer
|
||||
from pygments_lexer_solidity import SolidityLexer
|
||||
sphinx.add_lexer('Solidity', SolidityLexer())
|
||||
|
||||
sphinx.add_stylesheet('css/custom.css')
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
@ -112,7 +114,7 @@ highlight_language = 'Solidity'
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
@ -141,7 +143,7 @@ html_theme = 'default'
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = []
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,9 +16,11 @@ In particular, we need help in the following areas:
|
||||
<https://gitter.im/ethereum/solidity>`_
|
||||
* Fixing and responding to `Solidity's GitHub issues
|
||||
<https://github.com/ethereum/solidity/issues>`_, especially those tagged as
|
||||
`up-for-grabs <https://github.com/ethereum/solidity/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs>`_ which are
|
||||
`good first issue <https://github.com/ethereum/solidity/labels/good%20first%20issue>`_ which are
|
||||
meant as introductory issues for external contributors.
|
||||
|
||||
Please note that this project is released with a `Contributor Code of Conduct <https://raw.githubusercontent.com/ethereum/solidity/develop/CODE_OF_CONDUCT.md>`_. By participating in this project - in the issues, pull requests, or Gitter channels - you agree to abide by its terms.
|
||||
|
||||
How to Report Issues
|
||||
====================
|
||||
|
||||
@ -45,15 +47,18 @@ in addition to *what* you did (unless it is a tiny change).
|
||||
|
||||
If you need to pull in any changes from ``develop`` after making your fork (for
|
||||
example, to resolve potential merge conflicts), please avoid using ``git merge``
|
||||
and instead, ``git rebase`` your branch.
|
||||
and instead, ``git rebase`` your branch. This will help us review your change
|
||||
more easily.
|
||||
|
||||
Additionally, if you are writing a new feature, please ensure you write appropriate
|
||||
Boost test cases and place them under ``test/``.
|
||||
Additionally, if you are writing a new feature, please ensure you add appropriate
|
||||
test cases under ``test/`` (see below).
|
||||
|
||||
However, if you are making a larger change, please consult with the `Solidity Development Gitter channel
|
||||
<https://gitter.im/ethereum/solidity-dev>`_ (different from the one mentioned above, this on is
|
||||
<https://gitter.im/ethereum/solidity-dev>`_ (different from the one mentioned above, this one is
|
||||
focused on compiler and language development instead of language use) first.
|
||||
|
||||
New features and bugfixes should be added to the ``Changelog.md`` file: please
|
||||
follow the style of previous entries, when applicable.
|
||||
|
||||
Finally, please make sure you respect the `coding style
|
||||
<https://raw.githubusercontent.com/ethereum/solidity/develop/CODING_STYLE.md>`_
|
||||
@ -65,36 +70,52 @@ Thank you for your help!
|
||||
Running the compiler tests
|
||||
==========================
|
||||
|
||||
Solidity includes different types of tests. They are included in the application
|
||||
called ``soltest``. Some of them require the ``cpp-ethereum`` client in testing mode,
|
||||
There is a script at ``scripts/tests.sh`` which executes most of the tests and
|
||||
runs ``aleth`` automatically if it is in the path, but does not download it,
|
||||
so it most likely will not work right away. Please read on for the details.
|
||||
|
||||
Solidity includes different types of tests. Most of them are bundled in the application
|
||||
called ``soltest``. Some of them require the ``aleth`` client in testing mode,
|
||||
some others require ``libz3`` to be installed.
|
||||
|
||||
``soltest`` reads test contracts that are annotated with expected results
|
||||
stored in ``./test/libsolidity/syntaxTests``. In order for soltest to find these
|
||||
tests the root test directory has to be specified using the ``--testpath`` command
|
||||
line option, e.g. ``./build/test/soltest -- --testpath ./test``.
|
||||
To run a basic set of tests that neither require ``aleth`` nor ``libz3``, run
|
||||
``./scripts/soltest.sh --no-ipc --no-smt``. This script will run ``build/test/soltest``
|
||||
internally.
|
||||
|
||||
To disable the z3 tests, use ``./build/test/soltest -- --no-smt --testpath ./test`` and
|
||||
to run a subset of the tests that do not require ``cpp-ethereum``, use
|
||||
``./build/test/soltest -- --no-ipc --testpath ./test``.
|
||||
.. note ::
|
||||
|
||||
For all other tests, you need to install `cpp-ethereum <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth>`_ and run it in testing mode: ``eth --test -d /tmp/testeth``.
|
||||
Those working in a Windows environment wanting to run the above basic sets without aleth or libz3 in Git Bash, you would have to do: ``./build/test/RelWithDebInfo/soltest.exe -- --no-ipc --no-smt``.
|
||||
If you're running this in plain Command Prompt, use ``.\build\test\RelWithDebInfo\soltest.exe -- --no-ipc --no-smt``.
|
||||
|
||||
Then you run the actual tests: ``./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``.
|
||||
The option ``--no-smt`` disables the tests that require ``libz3`` and
|
||||
``--no-ipc`` disables those that require ``aleth``.
|
||||
|
||||
If you want to run the ipc tests (those test the semantics of the generated code),
|
||||
you need to install `aleth <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/aleth_2018-06-20_artful>`_ and run it in testing mode: ``aleth --test -d /tmp/testeth`` (make sure to rename it).
|
||||
|
||||
Then you run the actual tests: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``.
|
||||
|
||||
To run a subset of tests, filters can be used:
|
||||
``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``,
|
||||
``./scripts/soltest.sh -t TestSuite/TestName --ipcpath /tmp/testeth/geth.ipc``,
|
||||
where ``TestName`` can be a wildcard ``*``.
|
||||
|
||||
Alternatively, there is a testing script at ``scripts/test.sh`` which executes all tests and runs
|
||||
``cpp-ethereum`` automatically if it is in the path (but does not download it).
|
||||
The script ``scripts/tests.sh`` also runs commandline tests and compilation tests
|
||||
in addition to those found in ``soltest``.
|
||||
|
||||
Travis CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target.
|
||||
The CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target.
|
||||
|
||||
.. note ::
|
||||
|
||||
Some versions of ``aleth`` cannot be used for testing. We suggest using the same version that is used by the Solidity continuous integration tests.
|
||||
Currently the CI uses ``d661ac4fec0aeffbedcdc195f67f5ded0c798278`` of ``aleth``.
|
||||
|
||||
Writing and running syntax tests
|
||||
--------------------------------
|
||||
|
||||
As mentioned above, syntax tests are stored in individual contracts. These files must contain annotations, stating the expected result(s) of the respective test.
|
||||
Syntax tests check that the compiler generates the correct error messages for invalid code
|
||||
and properly accepts valid code.
|
||||
They are stored in individual files inside ``tests/libsolidity/syntaxTests``.
|
||||
These files must contain annotations, stating the expected result(s) of the respective test.
|
||||
The test suite will compile and check them against the given expectations.
|
||||
|
||||
Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol``
|
||||
@ -106,15 +127,17 @@ Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol``
|
||||
uint128 variable;
|
||||
}
|
||||
// ----
|
||||
// DeclarationError: Identifier already declared.
|
||||
// DeclarationError: (36-52): Identifier already declared.
|
||||
|
||||
A syntax test must contain at least the contract under test itself, followed by the seperator ``----``. The additional comments above are used to describe the
|
||||
expected compiler errors or warnings. This section can be empty in case that the contract should compile without any errors or warnings.
|
||||
A syntax test must contain at least the contract under test itself, followed by the separator ``// ----``. The following comments are used to describe the
|
||||
expected compiler errors or warnings. The number range denotes the location in the source where the error occurred.
|
||||
In case the contract should compile without any errors or warning, the section after the separator has to be empty
|
||||
and the separator can be left out completely.
|
||||
|
||||
In the above example, the state variable ``variable`` was declared twice, which is not allowed. This will result in a ``DeclarationError`` stating that the identifer was already declared.
|
||||
In the above example, the state variable ``variable`` was declared twice, which is not allowed. This will result in a ``DeclarationError`` stating that the identifier was already declared.
|
||||
|
||||
The tool that is being used for those tests is called ``isoltest`` and can be found under ``./test/tools/``. It is an interactive tool which allows
|
||||
editing of failing contracts using your prefered text editor. Let's try to break this test by removing the second declaration of ``variable``:
|
||||
editing of failing contracts using your preferred text editor. Let's try to break this test by removing the second declaration of ``variable``:
|
||||
|
||||
::
|
||||
|
||||
@ -122,7 +145,7 @@ editing of failing contracts using your prefered text editor. Let's try to break
|
||||
uint256 variable;
|
||||
}
|
||||
// ----
|
||||
// DeclarationError: Identifier already declared.
|
||||
// DeclarationError: (36-52): Identifier already declared.
|
||||
|
||||
Running ``./test/isoltest`` again will result in a test failure:
|
||||
|
||||
@ -135,16 +158,16 @@ Running ``./test/isoltest`` again will result in a test failure:
|
||||
}
|
||||
|
||||
Expected result:
|
||||
DeclarationError: Identifier already declared.
|
||||
DeclarationError: (36-52): Identifier already declared.
|
||||
Obtained result:
|
||||
Success
|
||||
|
||||
|
||||
which prints the expected result next to the obtained result, but also provides a way to change edit / update / skip the current contract or to even quit.
|
||||
``isoltest`` offers several options for failing tests:
|
||||
``isoltest`` prints the expected result next to the obtained result, but also provides a way to change edit / update / skip the current contract or to even quit.
|
||||
It offers several options for failing tests:
|
||||
|
||||
- edit: ``isoltest`` will try to open the editor that was specified before using ``isoltest --editor /path/to/editor``. If no path was set, this will result in a runtime error. In case an editor was specified, this will open it such that the contract can be adjusted.
|
||||
- update: Updates the contract under test. This will either remove the annotation which contains the exception not met or will add missing expectations. The test will then be run again.
|
||||
- edit: ``isoltest`` tries to open the contract in an editor so you can adjust it. It either uses the editor given on the command line (as ``isoltest --editor /path/to/editor``), in the environment variable ``EDITOR`` or just ``/usr/bin/editor`` (in this order).
|
||||
- update: Updates the contract under test. This either removes the annotation which contains the exception not met or adds missing expectations. The test will then be run again.
|
||||
- skip: Skips the execution of this particular test.
|
||||
- quit: Quits ``isoltest``.
|
||||
|
||||
@ -167,22 +190,23 @@ and re-run the test. It will now pass again:
|
||||
|
||||
.. note::
|
||||
|
||||
Please choose a name for the contract file, that is self-explainatory in the sense of what is been tested, e.g. ``double_variable_declaration.sol``.
|
||||
Do not put more than one contract into a single file. ``isoltest`` is currently not able to recognize them individually.
|
||||
Please choose a name for the contract file that explains what it tests, e.g. ``double_variable_declaration.sol``.
|
||||
Do not put more than one contract into a single file, unless you are testing inheritance or cross-contract calls.
|
||||
Each file should test one aspect of your new feature.
|
||||
|
||||
|
||||
Running the Fuzzer via AFL
|
||||
==========================
|
||||
|
||||
Fuzzing is a technique that runs programs on more or less random inputs to find exceptional execution
|
||||
states (segmentation faults, exceptions, etc). Modern fuzzers are clever and do a directed search
|
||||
states (segmentation faults, exceptions, etc). Modern fuzzers are clever and run a directed search
|
||||
inside the input. We have a specialized binary called ``solfuzzer`` which takes source code as input
|
||||
and fails whenever it encounters an internal compiler error, segmentation fault or similar, but
|
||||
does not fail if e.g. the code contains an error. This way, internal problems in the compiler
|
||||
can be found by fuzzing tools.
|
||||
does not fail if e.g., the code contains an error. This way, fuzzing tools can find internal problems in the compiler.
|
||||
|
||||
We mainly use `AFL <http://lcamtuf.coredump.cx/afl/>`_ for fuzzing. You need to download and
|
||||
build AFL manually. Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler:
|
||||
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:
|
||||
|
||||
::
|
||||
|
||||
@ -192,7 +216,49 @@ build AFL manually. Next, build Solidity (or just the ``solfuzzer`` binary) with
|
||||
cmake .. -DCMAKE_C_COMPILER=path/to/afl-gcc -DCMAKE_CXX_COMPILER=path/to/afl-g++
|
||||
make solfuzzer
|
||||
|
||||
Next, you need some example source files. This will make it much easer for the fuzzer
|
||||
At this stage you should be able to see a message similar to the following:
|
||||
|
||||
::
|
||||
|
||||
Scanning dependencies of target solfuzzer
|
||||
[ 98%] Building CXX object test/tools/CMakeFiles/solfuzzer.dir/fuzzer.cpp.o
|
||||
afl-cc 2.52b by <lcamtuf@google.com>
|
||||
afl-as 2.52b by <lcamtuf@google.com>
|
||||
[+] Instrumented 1949 locations (64-bit, non-hardened mode, ratio 100%).
|
||||
[100%] Linking CXX executable solfuzzer
|
||||
|
||||
If the instrumentation messages did not appear, try switching the cmake flags pointing to AFL's clang binaries:
|
||||
|
||||
::
|
||||
|
||||
# if previously failed
|
||||
make clean
|
||||
cmake .. -DCMAKE_C_COMPILER=path/to/afl-clang -DCMAKE_CXX_COMPILER=path/to/afl-clang++
|
||||
make solfuzzer
|
||||
|
||||
Otherwise, upon execution the fuzzer halts with an error saying binary is not instrumented:
|
||||
|
||||
::
|
||||
|
||||
afl-fuzz 2.52b by <lcamtuf@google.com>
|
||||
... (truncated messages)
|
||||
[*] Validating target binary...
|
||||
|
||||
[-] Looks like the target binary is not instrumented! The fuzzer depends on
|
||||
compile-time instrumentation to isolate interesting test cases while
|
||||
mutating the input data. For more information, and for tips on how to
|
||||
instrument binaries, please see /usr/share/doc/afl-doc/docs/README.
|
||||
|
||||
When source code is not available, you may be able to leverage QEMU
|
||||
mode support. Consult the README for tips on how to enable this.
|
||||
(It is also possible to use afl-fuzz as a traditional, "dumb" fuzzer.
|
||||
For that, you can use the -n option - but expect much worse results.)
|
||||
|
||||
[-] PROGRAM ABORT : No instrumentation detected
|
||||
Location : check_binary(), afl-fuzz.c:6920
|
||||
|
||||
|
||||
Next, you need some example source files. This makes it much easier for the fuzzer
|
||||
to find errors. You can either copy some files from the syntax tests or extract test files
|
||||
from the documentation or the other tests:
|
||||
|
||||
@ -207,8 +273,8 @@ from the documentation or the other tests:
|
||||
|
||||
The AFL documentation states that the corpus (the initial input files) should not be
|
||||
too large. The files themselves should not be larger than 1 kB and there should be
|
||||
at most one input file per functionality, so better start with a small number of
|
||||
input files. There is also a tool called ``afl-cmin`` that can trim input files
|
||||
at most one input file per functionality, so better start with a small number of.
|
||||
There is also a tool called ``afl-cmin`` that can trim input files
|
||||
that result in similar behaviour of the binary.
|
||||
|
||||
Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB):
|
||||
@ -217,20 +283,20 @@ Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB):
|
||||
|
||||
afl-fuzz -m 60 -i /tmp/test_cases -o /tmp/fuzzer_reports -- /path/to/solfuzzer
|
||||
|
||||
The fuzzer will create source files that lead to failures in ``/tmp/fuzzer_reports``.
|
||||
The fuzzer creates source files that lead to failures in ``/tmp/fuzzer_reports``.
|
||||
Often it finds many similar source files that produce the same error. You can
|
||||
use the tool ``scripts/uniqueErrors.sh`` to filter out the unique errors.
|
||||
|
||||
Whiskers
|
||||
========
|
||||
|
||||
*Whiskers* is a templating system similar to `Mustache <https://mustache.github.io>`_. It is used by the
|
||||
*Whiskers* is a string templating system similar to `Mustache <https://mustache.github.io>`_. It is used by the
|
||||
compiler in various places to aid readability, and thus maintainability and verifiability, of the code.
|
||||
|
||||
The syntax comes with a substantial difference to Mustache: the template markers ``{{`` and ``}}`` are
|
||||
The syntax comes with a substantial difference to Mustache. The template markers ``{{`` and ``}}`` are
|
||||
replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`inline-assembly`
|
||||
(The symbols ``<`` and ``>`` are invalid in inline assembly, while ``{`` and ``}`` are used to delimit blocks).
|
||||
Another limitation is that lists are only resolved one depth and they will not recurse. This may change in the future.
|
||||
Another limitation is that lists are only resolved one depth and they do not recurse. This may change in the future.
|
||||
|
||||
A rough specification is the following:
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
Expressions and Control Structures
|
||||
##################################
|
||||
|
||||
.. index:: ! parameter, parameter;input, parameter;output
|
||||
.. index:: ! parameter, parameter;input, parameter;output, parameter;multiple
|
||||
|
||||
Input Parameters and Output Parameters
|
||||
======================================
|
||||
@ -14,20 +14,26 @@ parameters as output.
|
||||
Input Parameters
|
||||
----------------
|
||||
|
||||
The input parameters are declared the same way as variables are. As an
|
||||
exception, unused parameters can omit the variable name.
|
||||
The input parameters are declared the same way as variables are.
|
||||
The name of unused parameters can be omitted.
|
||||
For example, suppose we want our contract to
|
||||
accept one kind of external calls with two integers, we would write
|
||||
something like::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
contract Simple {
|
||||
function taker(uint _a, uint _b) public pure {
|
||||
// do something with _a and _b.
|
||||
uint sum;
|
||||
function taker(uint _a, uint _b) public {
|
||||
sum = _a + _b;
|
||||
}
|
||||
}
|
||||
|
||||
Input parameters can be used just as any other local variable
|
||||
can be used, they can also be assigned to.
|
||||
|
||||
.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct
|
||||
|
||||
Output Parameters
|
||||
-----------------
|
||||
|
||||
@ -36,10 +42,10 @@ The output parameters can be declared with the same syntax after the
|
||||
the sum and the product of the two given integers, then we would
|
||||
write::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
contract Simple {
|
||||
function arithmetics(uint _a, uint _b)
|
||||
function arithmetic(uint _a, uint _b)
|
||||
public
|
||||
pure
|
||||
returns (uint o_sum, uint o_product)
|
||||
@ -50,24 +56,29 @@ write::
|
||||
}
|
||||
|
||||
The names of output parameters can be omitted.
|
||||
The output values can also be specified using ``return`` statements.
|
||||
The ``return`` statements are also capable of returning multiple
|
||||
values, see :ref:`multi-return`.
|
||||
Return parameters are initialized to zero; if they are not explicitly
|
||||
set, they stay to be zero.
|
||||
The return values can be specified using ``return`` statements,
|
||||
which are also capable of :ref:`returning multiple values<multi-return>`.
|
||||
Return parameters can be used as any other local variable and they
|
||||
are zero-initialized; if they are not explicitly
|
||||
set, they stay zero.
|
||||
|
||||
Input parameters and output parameters can be used as expressions in
|
||||
the function body. There, they are also usable in the left-hand side
|
||||
of assignment.
|
||||
|
||||
.. note::
|
||||
You cannot return some types from non-internal functions, notably
|
||||
multi-dimensional dynamic arrays and structs. If you enable the
|
||||
new experimental ``ABIEncoderV2`` feature by adding ``pragma experimental
|
||||
ABIEncoderV2;`` to your source file then more types are available, but
|
||||
``mapping`` types are still limited to inside a single contract and you
|
||||
cannot transfer them.
|
||||
|
||||
.. index:: if, else, while, do/while, for, break, continue, return, switch, goto
|
||||
|
||||
Control Structures
|
||||
===================
|
||||
|
||||
Most of the control structures from JavaScript are available in Solidity
|
||||
except for ``switch`` and ``goto``. So
|
||||
there is: ``if``, ``else``, ``while``, ``do``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with
|
||||
Most of the control structures known from curly-braces languages are available in Solidity:
|
||||
|
||||
There is: ``if``, ``else``, ``while``, ``do``, ``for``, ``break``, ``continue``, ``return``, with
|
||||
the usual semantics known from C or JavaScript.
|
||||
|
||||
Parentheses can *not* be omitted for conditionals, but curly brances can be omitted
|
||||
@ -99,10 +110,10 @@ Internal Function Calls
|
||||
Functions of the current contract can be called directly ("internally"), also recursively, as seen in
|
||||
this nonsensical example::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
contract C {
|
||||
function g(uint a) public pure returns (uint ret) { return f(); }
|
||||
function g(uint a) public pure returns (uint ret) { return a + f(); }
|
||||
function f() internal pure returns (uint ret) { return g(7) + f(); }
|
||||
}
|
||||
|
||||
@ -111,22 +122,31 @@ the effect that the current memory is not cleared, i.e. passing memory reference
|
||||
to internally-called functions is very efficient. Only functions of the same
|
||||
contract can be called internally.
|
||||
|
||||
You should still avoid excessive recursion, as every internal function call
|
||||
uses up at least one stack slot and there are at most 1024 slots available.
|
||||
|
||||
External Function Calls
|
||||
-----------------------
|
||||
|
||||
The expressions ``this.g(8);`` and ``c.g(2);`` (where ``c`` is a contract
|
||||
instance) are also valid function calls, but this time, the function
|
||||
will be called "externally", via a message call and not directly via jumps.
|
||||
Please note that function calls on ``this`` cannot be used in the constructor, as the
|
||||
actual contract has not been created yet.
|
||||
Please note that function calls on ``this`` cannot be used in the constructor,
|
||||
as the actual contract has not been created yet.
|
||||
|
||||
Functions of other contracts have to be called externally. For an external call,
|
||||
all function arguments have to be copied to memory.
|
||||
|
||||
When calling functions of other contracts, the amount of Wei sent with the call and
|
||||
the gas can be specified with special options ``.value()`` and ``.gas()``, respectively::
|
||||
.. note::
|
||||
A function call from one contract to another does not create its own transaction,
|
||||
it is a message call as part of the overall transaction.
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
When calling functions of other contracts, you can specify the amount of Wei or gas sent with the call with the special options ``.value()`` and ``.gas()``, respectively. Any Wei you send to the contract is added to the total balance of the contract:
|
||||
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract InfoFeed {
|
||||
function info() public payable returns (uint ret) { return 42; }
|
||||
@ -134,23 +154,15 @@ the gas can be specified with special options ``.value()`` and ``.gas()``, respe
|
||||
|
||||
contract Consumer {
|
||||
InfoFeed feed;
|
||||
function setFeed(address addr) public { feed = InfoFeed(addr); }
|
||||
function setFeed(InfoFeed addr) public { feed = addr; }
|
||||
function callFeed() public { feed.info.value(10).gas(800)(); }
|
||||
}
|
||||
|
||||
The modifier ``payable`` has to be used for ``info``, because otherwise, the `.value()`
|
||||
option would not be available.
|
||||
You need to use the modifier ``payable`` with the ``info`` function because
|
||||
otherwise, the ``.value()`` option would not be available.
|
||||
|
||||
Note that the expression ``InfoFeed(addr)`` performs an explicit type conversion stating
|
||||
that "we know that the type of the contract at the given address is ``InfoFeed``" and
|
||||
this does not execute a constructor. Explicit type conversions have to be
|
||||
handled with extreme caution. Never call a function on a contract where you
|
||||
are not sure about its type.
|
||||
|
||||
We could also have used ``function setFeed(InfoFeed _feed) { feed = _feed; }`` directly.
|
||||
Be careful about the fact that ``feed.info.value(10).gas(800)``
|
||||
only (locally) sets the value and amount of gas sent with the function call and only the
|
||||
parentheses at the end perform the actual call.
|
||||
.. warning::
|
||||
Be careful that ``feed.info.value(10).gas(800)`` only locally sets the ``value`` and amount of ``gas`` sent with the function call, and the parentheses at the end perform the actual call. So in this case, the function is not called.
|
||||
|
||||
Function calls cause exceptions if the called contract does not exist (in the
|
||||
sense that the account does not contain code) or if the called contract itself
|
||||
@ -158,8 +170,8 @@ throws an exception or goes out of gas.
|
||||
|
||||
.. warning::
|
||||
Any interaction with another contract imposes a potential danger, especially
|
||||
if the source code of the contract is not known in advance. The current
|
||||
contract hands over control to the called contract and that may potentially
|
||||
if the source code of the contract is not known in advance. The
|
||||
current contract hands over control to the called contract and that may potentially
|
||||
do just about anything. Even if the called contract inherits from a known parent contract,
|
||||
the inheriting contract is only required to have a correct interface. The
|
||||
implementation of the contract, however, can be completely arbitrary and thus,
|
||||
@ -174,24 +186,26 @@ throws an exception or goes out of gas.
|
||||
Named Calls and Anonymous Function Parameters
|
||||
---------------------------------------------
|
||||
|
||||
Function call arguments can also be given by name, in any order,
|
||||
Function call arguments can be given by name, in any order,
|
||||
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.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract C {
|
||||
function f(uint key, uint value) public {
|
||||
// ...
|
||||
mapping(uint => uint) data;
|
||||
|
||||
function f() public {
|
||||
set({value: 2, key: 3});
|
||||
}
|
||||
|
||||
function g() public {
|
||||
// named arguments
|
||||
f({value: 2, key: 3});
|
||||
function set(uint key, uint value) public {
|
||||
data[key] = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Omitted Function Parameter Names
|
||||
@ -202,7 +216,7 @@ Those parameters will still be present on the stack, but they are inaccessible.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
contract C {
|
||||
// omitted name for parameter
|
||||
@ -219,17 +233,17 @@ Those parameters will still be present on the stack, but they are inaccessible.
|
||||
Creating Contracts via ``new``
|
||||
==============================
|
||||
|
||||
A contract can create a new contract using the ``new`` keyword. The full
|
||||
code of the contract being created has to be known in advance, so recursive
|
||||
creation-dependencies are not possible.
|
||||
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.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
contract D {
|
||||
uint x;
|
||||
function D(uint a) public payable {
|
||||
uint public x;
|
||||
constructor(uint a) public payable {
|
||||
x = a;
|
||||
}
|
||||
}
|
||||
@ -239,15 +253,17 @@ creation-dependencies are not possible.
|
||||
|
||||
function createD(uint arg) public {
|
||||
D newD = new D(arg);
|
||||
newD.x();
|
||||
}
|
||||
|
||||
function createAndEndowD(uint arg, uint amount) public payable {
|
||||
// Send ether along with the creation
|
||||
D newD = (new D).value(amount)(arg);
|
||||
newD.x();
|
||||
}
|
||||
}
|
||||
|
||||
As seen in the example, it is possible to forward Ether while creating
|
||||
As seen in the example, it is possible to send Ether while creating
|
||||
an instance of ``D`` using the ``.value()`` option, but it is not possible
|
||||
to limit the amount of gas.
|
||||
If the creation fails (due to out-of-stack, not enough balance or other problems),
|
||||
@ -272,12 +288,15 @@ Assignment
|
||||
Destructuring Assignments and Returning Multiple Values
|
||||
-------------------------------------------------------
|
||||
|
||||
Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time.
|
||||
These can then either be assigned to newly declared variables or to pre-existing variables (or LValues in general):
|
||||
Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose number is a constant at compile-time. Those tuples can be used to return multiple values at the same time.
|
||||
These can then either be assigned to newly declared variables 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.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.4.23 <0.5.0;
|
||||
pragma solidity >0.4.23 <0.6.0;
|
||||
|
||||
contract C {
|
||||
uint[] data;
|
||||
@ -287,29 +306,33 @@ These can then either be assigned to newly declared variables or to pre-existing
|
||||
}
|
||||
|
||||
function g() public {
|
||||
// Variables declared with type and assigned from the returned tuple.
|
||||
(uint x, bool b, uint y) = f();
|
||||
// Variables declared with type and assigned from the returned tuple,
|
||||
// not all elements have to be specified (but the number must match).
|
||||
(uint x, , uint y) = f();
|
||||
// Common trick to swap values -- does not work for non-value storage types.
|
||||
(x, y) = (y, x);
|
||||
// Components can be left out (also for variable declarations).
|
||||
(data.length,,) = f(); // Sets the length to 7
|
||||
// Components can only be left out at the left-hand-side of assignments, with
|
||||
// one exception:
|
||||
(x,) = (1,);
|
||||
// (1,) is the only way to specify a 1-component tuple, because (1) is
|
||||
// equivalent to 1.
|
||||
(data.length, , ) = f(); // Sets the length to 7
|
||||
}
|
||||
}
|
||||
|
||||
It is not possible to mix variable declarations and non-declaration assignments,
|
||||
i.e. the following is not valid: ``(x, uint y) = (1, 2);``
|
||||
|
||||
.. note::
|
||||
Prior to version 0.4.24 it was possible to assign to tuples of smaller size, either
|
||||
Prior to version 0.5.0 it was possible to assign to tuples of smaller size, either
|
||||
filling up on the left or on the right side (which ever was empty). This is
|
||||
now deprecated, both sides have to have the same number of components.
|
||||
now disallowed, so both sides have to have the same number of components.
|
||||
|
||||
.. warning::
|
||||
Be careful when assigning to multiple variables at the same time when
|
||||
reference types are involved, because it could lead to unexpected
|
||||
copying behaviour.
|
||||
|
||||
Complications for Arrays and Structs
|
||||
------------------------------------
|
||||
|
||||
The semantics of assignment are a bit more complicated for non-value types like arrays and structs.
|
||||
The semantics of assignments are a bit more complicated for non-value types like arrays and structs.
|
||||
Assigning *to* a state variable always creates an independent copy. On the other hand, assigning to a local variable creates an independent copy only for elementary types, i.e. static types that fit into 32 bytes. If structs or arrays (including ``bytes`` and ``string``) are assigned from a state variable to a local variable, the local variable holds a reference to the original state variable. A second assignment to the local variable does not modify the state but only changes the reference. Assignments to members (or elements) of the local variable *do* change the state.
|
||||
|
||||
.. index:: ! scoping, declarations, default value
|
||||
@ -325,101 +348,31 @@ is ``false``. The default value for the ``uint`` or ``int`` types is ``0``. For
|
||||
element will be initialized to the default value corresponding to its type. Finally, for dynamically-sized arrays, ``bytes``
|
||||
and ``string``, the default value is an empty array or string.
|
||||
|
||||
A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared
|
||||
(this will change soon, see below).
|
||||
This happens because Solidity inherits its scoping rules from JavaScript.
|
||||
This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block.
|
||||
As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``:
|
||||
|
||||
::
|
||||
|
||||
// This will not compile
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract ScopingErrors {
|
||||
function scoping() public {
|
||||
uint i = 0;
|
||||
|
||||
while (i++ < 1) {
|
||||
uint same1 = 0;
|
||||
}
|
||||
|
||||
while (i++ < 2) {
|
||||
uint same1 = 0;// Illegal, second declaration of same1
|
||||
}
|
||||
}
|
||||
|
||||
function minimalScoping() public {
|
||||
{
|
||||
uint same2 = 0;
|
||||
}
|
||||
|
||||
{
|
||||
uint same2 = 0;// Illegal, second declaration of same2
|
||||
}
|
||||
}
|
||||
|
||||
function forLoopScoping() public {
|
||||
for (uint same3 = 0; same3 < 1; same3++) {
|
||||
}
|
||||
|
||||
for (uint same3 = 0; same3 < 1; same3++) {// Illegal, second declaration of same3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value.
|
||||
As a result, the following code is legal, despite being poorly written:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
function foo() public pure returns (uint) {
|
||||
// baz is implicitly initialized as 0
|
||||
uint bar = 5;
|
||||
if (true) {
|
||||
bar += baz;
|
||||
} else {
|
||||
uint baz = 10;// never executes
|
||||
}
|
||||
return bar;// returns 5
|
||||
}
|
||||
}
|
||||
|
||||
Scoping starting from Version 0.5.0
|
||||
-----------------------------------
|
||||
|
||||
Starting from version 0.5.0, Solidity will change to the more widespread scoping rules of C99
|
||||
Scoping in Solidity follows the widespread scoping rules of C99
|
||||
(and many other languages): Variables are visible from the point right after their declaration
|
||||
until the end of a ``{ }``-block. As an exception to this rule, variables declared in the
|
||||
until the end of the smallest ``{ }``-block that contains the declaration. As an exception to this rule, variables declared in the
|
||||
initialization part of a for-loop are only visible until the end of the for-loop.
|
||||
|
||||
Variables and other items declared outside of a code block, for example functions, contracts,
|
||||
user-defined types, etc., do not change their scoping behaviour. This means you can
|
||||
user-defined types, etc., are visible even before they were declared. This means you can
|
||||
use state variables before they are declared and call functions recursively.
|
||||
|
||||
These rules are already introduced now as an experimental feature.
|
||||
|
||||
As a consequence, the following examples will compile without warnings, since
|
||||
the two variables have the same name but disjoint scopes. In non-0.5.0-mode,
|
||||
they have the same scope (the function ``minimalScoping``) and thus it does
|
||||
not compile there.
|
||||
the two variables have the same name but disjoint scopes.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma experimental "v0.5.0";
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
contract C {
|
||||
function minimalScoping() pure public {
|
||||
{
|
||||
uint same2 = 0;
|
||||
uint same;
|
||||
same = 1;
|
||||
}
|
||||
|
||||
{
|
||||
uint same2 = 0;
|
||||
uint same;
|
||||
same = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -430,8 +383,8 @@ In any case, you will get a warning about the outer variable being shadowed.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma experimental "v0.5.0";
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
// This will report a warning
|
||||
contract C {
|
||||
function f() pure public returns (uint) {
|
||||
uint x = 1;
|
||||
@ -443,7 +396,26 @@ In any case, you will get a warning about the outer variable being shadowed.
|
||||
}
|
||||
}
|
||||
|
||||
.. index:: ! exception, ! throw, ! assert, ! require, ! revert
|
||||
.. warning::
|
||||
Before version 0.5.0 Solidity followed the same scoping rules as JavaScript, that is, a variable declared anywhere within a function would be in scope
|
||||
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.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
// This will not compile
|
||||
contract C {
|
||||
function f() pure public returns (uint) {
|
||||
x = 2;
|
||||
uint x;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
.. index:: ! exception, ! throw, ! assert, ! require, ! revert, ! errors
|
||||
|
||||
.. _assert-and-require:
|
||||
|
||||
Error handling: Assert, Require, Revert and Exceptions
|
||||
======================================================
|
||||
@ -458,17 +430,17 @@ If used properly, analysis tools can evaluate your contract to identify the cond
|
||||
There are two other ways to trigger exceptions: The ``revert`` function can be used to flag an error and
|
||||
revert the current call. It is possible to provide a string message containing details about the error
|
||||
that will be passed back to the caller.
|
||||
The deprecated keyword ``throw`` can also be used as an alternative to ``revert()`` (but only without error message).
|
||||
|
||||
.. note::
|
||||
From version 0.4.13 the ``throw`` keyword is deprecated and will be phased out in the future.
|
||||
There used to be a keyword called ``throw`` with the same semantics as ``revert()`` which
|
||||
was deprecated in version 0.4.13 and removed in version 0.5.0.
|
||||
|
||||
When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are rethrown) automatically. Exceptions to this rule are ``send``
|
||||
and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case
|
||||
and the low-level functions ``call``, ``delegatecall`` and ``staticcall`` -- those return ``false`` as their first return value in case
|
||||
of an exception instead of "bubbling up".
|
||||
|
||||
.. warning::
|
||||
The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
|
||||
The low-level functions ``call``, ``delegatecall`` and ``staticcall`` return ``true`` as their first return value if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
|
||||
|
||||
Catching exceptions is not yet possible.
|
||||
|
||||
@ -478,18 +450,18 @@ a message string for ``require``, but not for ``assert``.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.22;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
contract Sharer {
|
||||
function sendHalf(address addr) public payable returns (uint balance) {
|
||||
function sendHalf(address payable addr) public payable returns (uint balance) {
|
||||
require(msg.value % 2 == 0, "Even value required.");
|
||||
uint balanceBeforeTransfer = this.balance;
|
||||
uint balanceBeforeTransfer = address(this).balance;
|
||||
addr.transfer(msg.value / 2);
|
||||
// Since transfer throws an exception on failure and
|
||||
// cannot call back here, there should be no way for us to
|
||||
// still have half of the money.
|
||||
assert(this.balance == balanceBeforeTransfer - msg.value / 2);
|
||||
return this.balance;
|
||||
assert(address(this).balance == balanceBeforeTransfer - msg.value / 2);
|
||||
return address(this).balance;
|
||||
}
|
||||
}
|
||||
|
||||
@ -505,9 +477,8 @@ An ``assert``-style exception is generated in the following situations:
|
||||
|
||||
A ``require``-style exception is generated in the following situations:
|
||||
|
||||
#. Calling ``throw``.
|
||||
#. Calling ``require`` with an argument that evaluates to ``false``.
|
||||
#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
|
||||
#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall``, ``callcode`` or ``staticcall`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
|
||||
#. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly").
|
||||
#. If you perform an external function call targeting a contract that contains no code.
|
||||
#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function).
|
||||
@ -525,10 +496,10 @@ The following example shows how an error string can be used together with revert
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.22;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
contract VendingMachine {
|
||||
function buy(uint amount) payable {
|
||||
function buy(uint amount) public payable {
|
||||
if (amount > msg.value / 2 ether)
|
||||
revert("Not enough Ether provided.");
|
||||
// Alternative way to do it:
|
||||
|
@ -9,36 +9,11 @@ This list was originally compiled by `fivedogit <mailto:fivedogit@gmail.com>`_.
|
||||
Basic Questions
|
||||
***************
|
||||
|
||||
Is it possible to do something on a specific block number? (e.g. publish a contract or execute a transaction)
|
||||
=============================================================================================================
|
||||
|
||||
Transactions are not guaranteed to happen on the next block or any future
|
||||
specific block, since it is up to the miners to include transactions and not up
|
||||
to the submitter of the transaction. This applies to function calls/transactions and contract
|
||||
creation transactions.
|
||||
|
||||
If you want to schedule future calls of your contract, you can use the
|
||||
`alarm clock <http://www.ethereum-alarm-clock.com/>`_.
|
||||
|
||||
What is the transaction "payload"?
|
||||
==================================
|
||||
|
||||
This is just the bytecode "data" sent along with the request.
|
||||
|
||||
Is there a decompiler available?
|
||||
================================
|
||||
|
||||
There is no exact decompiler to Solidity, but
|
||||
`Porosity <https://github.com/comaeio/porosity>`_ is close.
|
||||
Because some information like variable names, comments, and
|
||||
source code formatting is lost in the compilation process,
|
||||
it is not possible to completely recover the original source code.
|
||||
|
||||
Bytecode can be disassembled to opcodes, a service that is provided by
|
||||
several blockchain explorers.
|
||||
|
||||
Contracts on the blockchain should have their original source
|
||||
code published if they are to be used by third parties.
|
||||
|
||||
Create a contract that can be killed and return funds
|
||||
=====================================================
|
||||
@ -63,39 +38,24 @@ has it (which includes `Remix <https://remix.ethereum.org/>`_), then
|
||||
``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my
|
||||
examples.
|
||||
|
||||
Can you return an array or a ``string`` from a solidity function call?
|
||||
======================================================================
|
||||
|
||||
Yes. See `array_receiver_and_returner.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/60_array_receiver_and_returner.sol>`_.
|
||||
|
||||
What is problematic, though, is returning any variably-sized data (e.g. a
|
||||
variably-sized array like ``uint[]``) from a fuction **called from within Solidity**.
|
||||
This is a limitation of the EVM and will be solved with the next protocol update.
|
||||
|
||||
Returning variably-sized data as part of an external transaction or call is fine.
|
||||
|
||||
Is it possible to in-line initialize an array like so: ``string[] myarray = ["a", "b"];``
|
||||
=========================================================================================
|
||||
|
||||
Yes. However it should be noted that this currently only works with statically sized memory arrays. You can even create an inline memory
|
||||
array in the return statement. Pretty cool, huh?
|
||||
array in the return statement.
|
||||
|
||||
Example::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
contract C {
|
||||
function f() public pure returns (uint8[5]) {
|
||||
function f() public pure returns (uint8[5] memory) {
|
||||
string[4] memory adaArr = ["This", "is", "an", "array"];
|
||||
return ([1, 2, 3, 4, 5]);
|
||||
adaArr[0] = "That";
|
||||
return [1, 2, 3, 4, 5];
|
||||
}
|
||||
}
|
||||
|
||||
Can a contract function return a ``struct``?
|
||||
============================================
|
||||
|
||||
Yes, but only in ``internal`` function calls.
|
||||
|
||||
If I return an ``enum``, I only get integer values in web3.js. How to get the named values?
|
||||
===========================================================================================
|
||||
|
||||
@ -111,7 +71,7 @@ should be noted that you must declare them as static memory arrays.
|
||||
|
||||
Examples::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract C {
|
||||
struct S {
|
||||
@ -133,20 +93,6 @@ How do structs work?
|
||||
|
||||
See `struct_and_for_loop_tester.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/65_struct_and_for_loop_tester.sol>`_.
|
||||
|
||||
How do for loops work?
|
||||
======================
|
||||
|
||||
Very similar to JavaScript. There is one point to watch out for, though:
|
||||
|
||||
If you use ``for (var i = 0; i < a.length; i ++) { a[i] = i; }``, then
|
||||
the type of ``i`` will be inferred only from ``0``, whose type is ``uint8``.
|
||||
This means that if ``a`` has more than ``255`` elements, your loop will
|
||||
not terminate because ``i`` can only hold values up to ``255``.
|
||||
|
||||
Better use ``for (uint i = 0; i < a.length...``
|
||||
|
||||
See `struct_and_for_loop_tester.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/65_struct_and_for_loop_tester.sol>`_.
|
||||
|
||||
What are some examples of basic string manipulation (``substring``, ``indexOf``, ``charAt``, etc)?
|
||||
==================================================================================================
|
||||
|
||||
@ -156,7 +102,7 @@ which will be extended in the future. In addition, Arachnid has written `solidit
|
||||
For now, if you want to modify a string (even when you only want to know its length),
|
||||
you should always convert it to a ``bytes`` first::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract C {
|
||||
string s;
|
||||
@ -174,7 +120,17 @@ you should always convert it to a ``bytes`` first::
|
||||
Can I concatenate two strings?
|
||||
==============================
|
||||
|
||||
You have to do it manually for now.
|
||||
Yes, you can use ``abi.encodePacked``::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
library ConcatHelper {
|
||||
function concat(bytes memory a, bytes memory b)
|
||||
internal pure returns (bytes memory) {
|
||||
return abi.encodePacked(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Why is the low-level function ``.call()`` less favorable than instantiating a contract with a variable (``ContractB b;``) and executing its functions (``b.doSomething();``)?
|
||||
=============================================================================================================================================================================
|
||||
@ -187,11 +143,6 @@ arguments for you.
|
||||
See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_ and
|
||||
`pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_.
|
||||
|
||||
Is unused gas automatically refunded?
|
||||
=====================================
|
||||
|
||||
Yes and it is immediate, i.e. done as part of the transaction.
|
||||
|
||||
When returning a value of say ``uint`` type, is it possible to return an ``undefined`` or "null"-like value?
|
||||
============================================================================================================
|
||||
|
||||
@ -203,7 +154,7 @@ situation.
|
||||
|
||||
If you do not want to throw, you can return a pair::
|
||||
|
||||
pragma solidity >0.4.23 <0.5.0;
|
||||
pragma solidity >0.4.23 <0.6.0;
|
||||
|
||||
contract C {
|
||||
uint[] counters;
|
||||
@ -221,9 +172,10 @@ If you do not want to throw, you can return a pair::
|
||||
function checkCounter(uint index) public view {
|
||||
(uint counter, bool error) = getCounter(index);
|
||||
if (error) {
|
||||
// ...
|
||||
// Handle the error
|
||||
} else {
|
||||
// ...
|
||||
// Do something with counter.
|
||||
require(counter > 7, "Invalid counter value");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -242,118 +194,6 @@ It gets added to the total balance of the contract, just like when you send ethe
|
||||
You can only send ether along to a function that has the ``payable`` modifier,
|
||||
otherwise an exception is thrown.
|
||||
|
||||
Is it possible to get a tx receipt for a transaction executed contract-to-contract?
|
||||
===================================================================================
|
||||
|
||||
No, a function call from one contract to another does not create its own transaction,
|
||||
you have to look in the overall transaction. This is also the reason why several
|
||||
block explorer do not show Ether sent between contracts correctly.
|
||||
|
||||
What is the ``memory`` keyword? What does it do?
|
||||
================================================
|
||||
|
||||
The Ethereum Virtual Machine has three areas where it can store items.
|
||||
|
||||
The first is "storage", where all the contract state variables reside.
|
||||
Every contract has its own storage and it is persistent between function calls
|
||||
and quite expensive to use.
|
||||
|
||||
The second is "memory", this is used to hold temporary values. It
|
||||
is erased between (external) function calls and is cheaper to use.
|
||||
|
||||
The third one is the stack, which is used to hold small local variables.
|
||||
It is almost free to use, but can only hold a limited amount of values.
|
||||
|
||||
For almost all types, you cannot specify where they should be stored, because
|
||||
they are copied everytime they are used.
|
||||
|
||||
The types where the so-called storage location is important are structs
|
||||
and arrays. If you e.g. pass such variables in function calls, their
|
||||
data is not copied if it can stay in memory or stay in storage.
|
||||
This means that you can modify their content in the called function
|
||||
and these modifications will still be visible in the caller.
|
||||
|
||||
There are defaults for the storage location depending on which type
|
||||
of variable it concerns:
|
||||
|
||||
* state variables are always in storage
|
||||
* function arguments are in memory by default
|
||||
* local variables of struct, array or mapping type reference storage by default
|
||||
* local variables of value type (i.e. neither array, nor struct nor mapping) are stored in the stack
|
||||
|
||||
Example::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
uint[] data1;
|
||||
uint[] data2;
|
||||
|
||||
function appendOne() public {
|
||||
append(data1);
|
||||
}
|
||||
|
||||
function appendTwo() public {
|
||||
append(data2);
|
||||
}
|
||||
|
||||
function append(uint[] storage d) internal {
|
||||
d.push(1);
|
||||
}
|
||||
}
|
||||
|
||||
The function ``append`` can work both on ``data1`` and ``data2`` and its modifications will be
|
||||
stored permanently. If you remove the ``storage`` keyword, the default
|
||||
is to use ``memory`` for function arguments. This has the effect that
|
||||
at the point where ``append(data1)`` or ``append(data2)`` is called, an
|
||||
independent copy of the state variable is created in memory and
|
||||
``append`` operates on this copy (which does not support ``.push`` - but that
|
||||
is another issue). The modifications to this independent copy do not
|
||||
carry back to ``data1`` or ``data2``.
|
||||
|
||||
A common mistake is to declare a local variable and assume that it will
|
||||
be created in memory, although it will be created in storage::
|
||||
|
||||
/// THIS CONTRACT CONTAINS AN ERROR
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
uint someVariable;
|
||||
uint[] data;
|
||||
|
||||
function f() public {
|
||||
uint[] x;
|
||||
x.push(2);
|
||||
data = x;
|
||||
}
|
||||
}
|
||||
|
||||
The type of the local variable ``x`` is ``uint[] storage``, but since
|
||||
storage is not dynamically allocated, it has to be assigned from
|
||||
a state variable before it can be used. So no space in storage will be
|
||||
allocated for ``x``, but instead it functions only as an alias for
|
||||
a pre-existing variable in storage.
|
||||
|
||||
What will happen is that the compiler interprets ``x`` as a storage
|
||||
pointer and will make it point to the storage slot ``0`` by default.
|
||||
This has the effect that ``someVariable`` (which resides at storage
|
||||
slot ``0``) is modified by ``x.push(2)``.
|
||||
|
||||
The correct way to do this is the following::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
uint someVariable;
|
||||
uint[] data;
|
||||
|
||||
function f() public {
|
||||
uint[] x = data;
|
||||
x.push(2);
|
||||
}
|
||||
}
|
||||
|
||||
******************
|
||||
Advanced Questions
|
||||
******************
|
||||
@ -375,13 +215,6 @@ The key point is that the calling contract needs to know about the function it i
|
||||
See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_
|
||||
and `pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_.
|
||||
|
||||
Get contract to do something when it is first mined
|
||||
===================================================
|
||||
|
||||
Use the constructor. Anything inside it will be executed when the contract is first mined.
|
||||
|
||||
See `replicator.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/50_replicator.sol>`_.
|
||||
|
||||
How do you create 2-dimensional arrays?
|
||||
=======================================
|
||||
|
||||
@ -414,7 +247,7 @@ This is a very interesting question. Suppose that we have a contract field set u
|
||||
User user2 = user1;
|
||||
}
|
||||
|
||||
In this case, the mapping of the struct being copied over into the userList is ignored as there is no "list of mapped keys".
|
||||
In this case, the mapping of the struct being copied over into ``user2`` is ignored as there is no "list of mapped keys".
|
||||
Therefore it is not possible to find out which values should be copied over.
|
||||
|
||||
How do I initialize a contract with only a specific amount of wei?
|
||||
@ -426,26 +259,20 @@ In the case of a ``contract A`` calling a new instance of ``contract B``, parent
|
||||
You will need to make sure that you have both contracts aware of each other's presence and that ``contract B`` has a ``payable`` constructor.
|
||||
In this example::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
contract B {
|
||||
function B() public payable {}
|
||||
constructor() public payable {}
|
||||
}
|
||||
|
||||
contract A {
|
||||
address child;
|
||||
B child;
|
||||
|
||||
function test() public {
|
||||
child = (new B).value(10)(); //construct a new B with 10 wei
|
||||
}
|
||||
}
|
||||
|
||||
Can a contract function accept a two-dimensional array?
|
||||
=======================================================
|
||||
|
||||
This is not yet implemented for external calls and dynamic arrays -
|
||||
you can only use one level of dynamic arrays.
|
||||
|
||||
What is the relationship between ``bytes32`` and ``string``? Why is it that ``bytes32 somevar = "stringliteral";`` works and what does the saved 32-byte hex value mean?
|
||||
========================================================================================================================================================================
|
||||
|
||||
@ -474,7 +301,7 @@ Can a contract pass an array (static size) or string or ``bytes`` (dynamic size)
|
||||
Sure. Take care that if you cross the memory / storage boundary,
|
||||
independent copies will be created::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
pragma solidity >=0.4.16 <0.6.0;
|
||||
|
||||
contract C {
|
||||
uint[20] x;
|
||||
@ -484,7 +311,7 @@ independent copies will be created::
|
||||
h(x);
|
||||
}
|
||||
|
||||
function g(uint[20] y) internal pure {
|
||||
function g(uint[20] memory y) internal pure {
|
||||
y[2] = 3;
|
||||
}
|
||||
|
||||
@ -494,10 +321,9 @@ independent copies will be created::
|
||||
}
|
||||
|
||||
The call to ``g(x)`` will not have an effect on ``x`` because it needs
|
||||
to create an independent copy of the storage value in memory
|
||||
(the default storage location is memory). On the other hand,
|
||||
``h(x)`` successfully modifies ``x`` because only a reference
|
||||
and not a copy is passed.
|
||||
to create an independent copy of the storage value in memory.
|
||||
On the other hand, ``h(x)`` successfully modifies ``x`` because only
|
||||
a reference and not a copy is passed.
|
||||
|
||||
Sometimes, when I try to change the length of an array with ex: ``arrayname.length = 7;`` I get a compiler error ``Value must be an lvalue``. Why?
|
||||
==================================================================================================================================================
|
||||
@ -512,15 +338,14 @@ contract level) with ``arrayname.length = <some new length>;``. If you get the
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.18 <0.6.0;
|
||||
|
||||
// This will not compile
|
||||
|
||||
pragma solidity ^0.4.18;
|
||||
|
||||
contract C {
|
||||
int8[] dynamicStorageArray;
|
||||
int8[5] fixedStorageArray;
|
||||
|
||||
function f() {
|
||||
function f() public {
|
||||
int8[] memory memArr; // Case 1
|
||||
memArr.length++; // illegal
|
||||
|
||||
@ -547,29 +372,7 @@ case in C or Java).
|
||||
Is it possible to return an array of strings (``string[]``) from a Solidity function?
|
||||
=====================================================================================
|
||||
|
||||
Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic array itself).
|
||||
|
||||
If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that?
|
||||
===========================================================================================================================
|
||||
|
||||
The automatic :ref:`getter function<getter-functions>` for a public state variable of array type only returns
|
||||
individual elements. If you want to return the complete array, you have to
|
||||
manually write a function to do that.
|
||||
|
||||
|
||||
What could have happened if an account has storage value(s) but no code? Example: http://test.ether.camp/account/5f740b3a43fbb99724ce93a879805f4dc89178b5
|
||||
==========================================================================================================================================================
|
||||
|
||||
The last thing a constructor does is returning the code of the contract.
|
||||
The gas costs for this depend on the length of the code and it might be
|
||||
that the supplied gas is not enough. This situation is the only one
|
||||
where an "out of gas" exception does not revert changes to the state,
|
||||
i.e. in this case the initialisation of the state variables.
|
||||
|
||||
https://github.com/ethereum/wiki/wiki/Subtleties
|
||||
|
||||
After a successful CREATE operation's sub-execution, if the operation returns x, 5 * len(x) gas is subtracted from the remaining gas before the contract is created. If the remaining gas is less than 5 * len(x), then no gas is subtracted, the code of the created contract becomes the empty string, but this is not treated as an exceptional condition - no reverts happen.
|
||||
|
||||
Only when ``pragma experimental "ABIEncoderV2";`` is used.
|
||||
|
||||
What does the following strange check do in the Custom Token contract?
|
||||
======================================================================
|
||||
@ -585,6 +388,25 @@ does not fit inside this range, it is truncated. These truncations can have
|
||||
above is necessary to avoid certain attacks.
|
||||
|
||||
|
||||
Why are explicit conversions between fixed-size bytes types and integer types failing?
|
||||
======================================================================================
|
||||
|
||||
Since version 0.5.0 explicit conversions between fixed-size byte arrays and integers are only allowed,
|
||||
if both types have the same size. This prevents unexpected behaviour when truncating or padding.
|
||||
Such conversions are still possible, but intermediate casts are required that make the desired
|
||||
truncation and padding convention explicit. See :ref:`types-conversion-elementary-types` for a full
|
||||
explanation and examples.
|
||||
|
||||
|
||||
Why can number literals not be converted to fixed-size bytes types?
|
||||
===================================================================
|
||||
|
||||
Since version 0.5.0 only hexadecimal number literals can be converted to fixed-size bytes
|
||||
types and only if the number of hex digits matches the size of the type. See :ref:`types-conversion-literals`
|
||||
for a full explanation and examples.
|
||||
|
||||
|
||||
|
||||
More Questions?
|
||||
===============
|
||||
|
||||
|
@ -50,6 +50,7 @@ TypeName = ElementaryTypeName
|
||||
| Mapping
|
||||
| ArrayTypeName
|
||||
| FunctionTypeName
|
||||
| ( 'address' 'payable' )
|
||||
|
||||
UserDefinedTypeName = Identifier ( '.' Identifier )*
|
||||
|
||||
@ -57,8 +58,8 @@ Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
|
||||
ArrayTypeName = TypeName '[' Expression? ']'
|
||||
FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )*
|
||||
( 'returns' FunctionTypeParameterList )?
|
||||
StorageLocation = 'memory' | 'storage'
|
||||
StateMutability = 'pure' | 'constant' | 'view' | 'payable'
|
||||
StorageLocation = 'memory' | 'storage' | 'calldata'
|
||||
StateMutability = 'pure' | 'view' | 'payable'
|
||||
|
||||
Block = '{' Statement* '}'
|
||||
Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |
|
||||
@ -78,8 +79,7 @@ Break = 'break'
|
||||
Return = 'return' Expression?
|
||||
Throw = 'throw'
|
||||
EmitStatement = 'emit' FunctionCall
|
||||
VariableDefinition = ('var' IdentifierList | VariableDeclaration | '(' VariableDeclaration? (',' VariableDeclaration? )* ')' ) ( '=' Expression )?
|
||||
IdentifierList = '(' ( Identifier? ',' )* Identifier? ')'
|
||||
VariableDefinition = (VariableDeclaration | '(' VariableDeclaration? (',' VariableDeclaration? )* ')' ) ( '=' Expression )?
|
||||
|
||||
// Precedence by order (see github.com/ethereum/solidity/pull/732)
|
||||
Expression
|
||||
@ -140,8 +140,7 @@ TupleExpression = '(' ( Expression? ( ',' Expression? )* )? ')'
|
||||
|
||||
ElementaryTypeNameExpression = ElementaryTypeName
|
||||
|
||||
ElementaryTypeName = 'address' | 'bool' | 'string' | 'var'
|
||||
| Int | Uint | Byte | Fixed | Ufixed
|
||||
ElementaryTypeName = 'address' | 'bool' | 'string' | Int | Uint | Byte | Fixed | Ufixed
|
||||
|
||||
Int = 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256'
|
||||
|
||||
@ -155,8 +154,9 @@ Ufixed = 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ )
|
||||
|
||||
InlineAssemblyBlock = '{' AssemblyItem* '}'
|
||||
|
||||
AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyLocalBinding | AssemblyAssignment | AssemblyLabel | NumberLiteral | StringLiteral | HexLiteral
|
||||
AssemblyLocalBinding = 'let' Identifier ':=' FunctionalAssemblyExpression
|
||||
AssemblyAssignment = ( Identifier ':=' FunctionalAssemblyExpression ) | ( '=:' Identifier )
|
||||
AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyVariableDeclaration | AssemblyAssignment | AssemblyLabel | NumberLiteral | StringLiteral | HexLiteral
|
||||
AssemblyExpression = Identifier | FunctionalAssemblyExpression | NumberLiteral | StringLiteral | HexLiteral
|
||||
AssemblyVariableDeclaration = 'let' Identifier ':=' AssemblyExpression
|
||||
AssemblyAssignment = ( Identifier ':=' AssemblyExpression ) | ( '=:' Identifier )
|
||||
AssemblyLabel = Identifier ':'
|
||||
FunctionalAssemblyExpression = Identifier '(' AssemblyItem? ( ',' AssemblyItem )* ')'
|
||||
|
122
docs/index.rst
122
docs/index.rst
@ -6,22 +6,25 @@ Solidity
|
||||
:alt: Solidity logo
|
||||
:align: center
|
||||
|
||||
Solidity is a contract-oriented, high-level language for implementing smart contracts.
|
||||
It was influenced by C++, Python and JavaScript
|
||||
and is designed to target the Ethereum Virtual Machine (EVM).
|
||||
Solidity is an object-oriented, high-level language for implementing smart
|
||||
contracts. Smart contracts are programs which govern the behaviour of accounts
|
||||
within the Ethereum state.
|
||||
|
||||
Solidity was influenced by C++, Python and JavaScript and is designed to target
|
||||
the Ethereum Virtual Machine (EVM).
|
||||
|
||||
Solidity is statically typed, supports inheritance, libraries and complex
|
||||
user-defined types among other features.
|
||||
|
||||
As you will see, it is possible to create contracts for voting,
|
||||
crowdfunding, blind auctions, multi-signature wallets and more.
|
||||
With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions,
|
||||
and multi-signature wallets.
|
||||
|
||||
.. note::
|
||||
The best way to try out Solidity right now is using
|
||||
`Remix <https://remix.ethereum.org/>`_
|
||||
(it can take a while to load, please be patient). Remix is a web browser
|
||||
based IDE that allows you to write Solidity smart contracts, then deploy
|
||||
and run the smart contracts.
|
||||
based IDE that allows you to write Solidity smart contracts, then deploy
|
||||
and run the smart contracts.
|
||||
|
||||
.. warning::
|
||||
Since software is written by humans, it can have bugs. Thus, also
|
||||
@ -34,105 +37,14 @@ crowdfunding, blind auctions, multi-signature wallets and more.
|
||||
Translations
|
||||
------------
|
||||
|
||||
This documentation is translated into several languages by community volunteers, but the English version stands as a reference.
|
||||
This documentation is translated into several languages by community volunteers
|
||||
with varying degrees of completeness and up-to-dateness. The English version stands as a reference.
|
||||
|
||||
* `Simplified Chinese <http://solidity-cn.readthedocs.io>`_ (in progress)
|
||||
* `Spanish <https://solidity-es.readthedocs.io>`_
|
||||
* `Russian <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_ (rather outdated)
|
||||
* `Korean <http://solidity-kr.readthedocs.io>`_ (in progress)
|
||||
|
||||
|
||||
Useful links
|
||||
------------
|
||||
|
||||
* `Ethereum <https://ethereum.org>`_
|
||||
|
||||
* `Changelog <https://github.com/ethereum/solidity/blob/develop/Changelog.md>`_
|
||||
|
||||
* `Story Backlog <https://www.pivotaltracker.com/n/projects/1189488>`_
|
||||
|
||||
* `Source Code <https://github.com/ethereum/solidity/>`_
|
||||
|
||||
* `Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_
|
||||
|
||||
* `Gitter Chat <https://gitter.im/ethereum/solidity/>`_
|
||||
|
||||
Available Solidity Integrations
|
||||
-------------------------------
|
||||
|
||||
* `Remix <https://remix.ethereum.org/>`_
|
||||
Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components.
|
||||
|
||||
* `IntelliJ IDEA plugin <https://plugins.jetbrains.com/plugin/9475-intellij-solidity>`_
|
||||
Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs)
|
||||
|
||||
* `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_
|
||||
Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler.
|
||||
|
||||
* `Package for SublimeText — Solidity language syntax <https://packagecontrol.io/packages/Ethereum/>`_
|
||||
Solidity syntax highlighting for SublimeText editor.
|
||||
|
||||
* `Etheratom <https://github.com/0mkara/etheratom>`_
|
||||
Plugin for the Atom editor that features syntax highlighting, compilation and a runtime environment (Backend node & VM compatible).
|
||||
|
||||
* `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_
|
||||
Plugin for the Atom editor that provides Solidity linting.
|
||||
|
||||
* `Atom Solium Linter <https://atom.io/packages/linter-solium>`_
|
||||
Configurable Solidty linter for Atom using Solium as a base.
|
||||
|
||||
* `Solium <https://github.com/duaraghav8/Solium/>`_
|
||||
Linter to identify and fix style and security issues in Solidity.
|
||||
|
||||
* `Solhint <https://github.com/protofire/solhint>`_
|
||||
Solidity linter that provides security, style guide and best practice rules for smart contract validation.
|
||||
|
||||
* `Visual Studio Code extension <http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_
|
||||
Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler.
|
||||
|
||||
* `Emacs Solidity <https://github.com/ethereum/emacs-solidity/>`_
|
||||
Plugin for the Emacs editor providing syntax highlighting and compilation error reporting.
|
||||
|
||||
* `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_
|
||||
Plugin for the Vim editor providing syntax highlighting.
|
||||
|
||||
* `Vim Syntastic <https://github.com/scrooloose/syntastic>`_
|
||||
Plugin for the Vim editor providing compile checking.
|
||||
|
||||
Discontinued:
|
||||
|
||||
* `Mix IDE <https://github.com/ethereum/mix/>`_
|
||||
Qt based IDE for designing, debugging and testing solidity smart contracts.
|
||||
|
||||
* `Ethereum Studio <https://live.ether.camp/>`_
|
||||
Specialized web IDE that also provides shell access to a complete Ethereum environment.
|
||||
|
||||
Solidity Tools
|
||||
--------------
|
||||
|
||||
* `Dapp <https://dapp.readthedocs.io>`_
|
||||
Build tool, package manager, and deployment assistant for Solidity.
|
||||
|
||||
* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_
|
||||
Try Solidity instantly with a command-line Solidity console.
|
||||
|
||||
* `solgraph <https://github.com/raineorshine/solgraph>`_
|
||||
Visualize Solidity control flow and highlight potential security vulnerabilities.
|
||||
|
||||
* `evmdis <https://github.com/Arachnid/evmdis>`_
|
||||
EVM Disassembler that performs static analysis on the bytecode to provide a higher level of abstraction than raw EVM operations.
|
||||
|
||||
* `Doxity <https://github.com/DigixGlobal/doxity>`_
|
||||
Documentation Generator for Solidity.
|
||||
|
||||
Third-Party Solidity Parsers and Grammars
|
||||
-----------------------------------------
|
||||
|
||||
* `solidity-parser <https://github.com/ConsenSys/solidity-parser>`_
|
||||
Solidity parser for JavaScript
|
||||
|
||||
* `Solidity Grammar for ANTLR 4 <https://github.com/federicobond/solidity-antlr4>`_
|
||||
Solidity grammar for the ANTLR 4 parser generator
|
||||
* `French <http://solidity-fr.readthedocs.io>`_ (in progress)
|
||||
|
||||
Language Documentation
|
||||
----------------------
|
||||
@ -142,11 +54,11 @@ in Solidity followed by the basics about :ref:`blockchains <blockchain-basics>`
|
||||
and the :ref:`Ethereum Virtual Machine <the-ethereum-virtual-machine>`.
|
||||
|
||||
The next section will explain several *features* of Solidity by giving
|
||||
useful :ref:`example contracts <voting>`
|
||||
useful :ref:`example contracts <voting>`.
|
||||
Remember that you can always try out the contracts
|
||||
`in your browser <https://remix.ethereum.org>`_!
|
||||
|
||||
The last and most extensive section will cover all aspects of Solidity in depth.
|
||||
The fourth and most extensive section will cover all aspects of Solidity in depth.
|
||||
|
||||
If you still have questions, you can try searching or asking on the
|
||||
`Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_
|
||||
@ -166,12 +78,14 @@ Contents
|
||||
solidity-by-example.rst
|
||||
solidity-in-depth.rst
|
||||
security-considerations.rst
|
||||
resources.rst
|
||||
using-the-compiler.rst
|
||||
metadata.rst
|
||||
abi-spec.rst
|
||||
julia.rst
|
||||
yul.rst
|
||||
style-guide.rst
|
||||
common-patterns.rst
|
||||
bugs.rst
|
||||
contributing.rst
|
||||
frequently-asked-questions.rst
|
||||
lll.rst
|
||||
|
@ -22,7 +22,7 @@ Remix
|
||||
|
||||
`Access Remix online <https://remix.ethereum.org/>`_, you don't need to install anything.
|
||||
If you want to use it without connection to the Internet, go to
|
||||
https://github.com/ethereum/browser-solidity/tree/gh-pages and download the .ZIP file as
|
||||
https://github.com/ethereum/remix-live/tree/gh-pages and download the ``.zip`` file as
|
||||
explained on that page.
|
||||
|
||||
Further options on this page detail installing commandline Solidity compiler software
|
||||
@ -35,22 +35,24 @@ npm / Node.js
|
||||
=============
|
||||
|
||||
Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The
|
||||
`solcjs` program has fewer features than all options further down this page. Our
|
||||
`solcjs` program has fewer features than the ways to access the compiler described
|
||||
further down this page. The
|
||||
:ref:`commandline-compiler` documentation assumes you are using
|
||||
the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will
|
||||
stop reading the documentation here and then continue to `solc-js <https://github.com/ethereum/solc-js>`_.
|
||||
the full-featured compiler, `solc`. The usage of `solcjs` is documented inside its own
|
||||
`repository <https://github.com/ethereum/solc-js>`_.
|
||||
|
||||
Note: The solc-js project is derived from the C++
|
||||
`solc` by using Emscripten. `solc-js` can be used in JavaScript projects directly (such as Remix).
|
||||
`solc` by using Emscripten which means that both use the same compiler source code.
|
||||
`solc-js` can be used in JavaScript projects directly (such as Remix).
|
||||
Please refer to the solc-js repository for instructions.
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
npm install -g solc
|
||||
|
||||
.. note::
|
||||
|
||||
The commandline is named `solcjs`.
|
||||
The commandline executable is named `solcjs`.
|
||||
|
||||
The comandline options of `solcjs` are not compatible with `solc` and tools (such as `geth`)
|
||||
expecting the behaviour of `solc` will not work with `solcjs`.
|
||||
@ -62,9 +64,9 @@ We provide up to date docker builds for the compiler. The ``stable``
|
||||
repository contains released versions while the ``nightly``
|
||||
repository contains potentially unstable changes in the develop branch.
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
docker run ethereum/solc:stable solc --version
|
||||
docker run ethereum/solc:stable --version
|
||||
|
||||
Currently, the docker image only contains the compiler executable,
|
||||
so you have to do some additional work to link in the source and
|
||||
@ -76,65 +78,66 @@ Binary Packages
|
||||
Binary packages of Solidity are available at
|
||||
`solidity/releases <https://github.com/ethereum/solidity/releases>`_.
|
||||
|
||||
We also have PPAs for Ubuntu. For the latest stable version.
|
||||
We also have PPAs for Ubuntu, you can get the latest stable
|
||||
version using the following commands:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
sudo add-apt-repository ppa:ethereum/ethereum
|
||||
sudo apt-get update
|
||||
sudo apt-get install solc
|
||||
|
||||
If you want to use the cutting edge developer version:
|
||||
The nightly version can be installed using these commands:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
sudo add-apt-repository ppa:ethereum/ethereum
|
||||
sudo add-apt-repository ppa:ethereum/ethereum-dev
|
||||
sudo apt-get update
|
||||
sudo apt-get install solc
|
||||
|
||||
|
||||
We are also releasing a `snap package <https://snapcraft.io/>`_, which is installable in all the `supported Linux distros <https://snapcraft.io/docs/core/install>`_. To install the latest stable version of solc:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
sudo snap install solc
|
||||
|
||||
Or if you want to help testing the unstable solc with the most recent changes from the development branch:
|
||||
If you want to help testing the latest development version of Solidity
|
||||
with the most recent changes, please use the following:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
sudo snap install solc --edge
|
||||
|
||||
Arch Linux also has packages, albeit limited to the latest development version:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
pacman -S solidity
|
||||
|
||||
Homebrew is missing pre-built bottles at the time of writing,
|
||||
following a Jenkins to TravisCI migration, but Homebrew
|
||||
should still work just fine as a means to build-from-source.
|
||||
We will re-add the pre-built bottles soon.
|
||||
We distribute the Solidity compiler through Homebrow
|
||||
as a build-from-source version. Pre-built bottles are
|
||||
currently not supported.
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
brew update
|
||||
brew upgrade
|
||||
brew tap ethereum/ethereum
|
||||
brew install solidity
|
||||
|
||||
If you need a specific version of Solidity you can install a
|
||||
If you need a specific version of Solidity you can install a
|
||||
Homebrew formula directly from Github.
|
||||
|
||||
View
|
||||
View
|
||||
`solidity.rb commits on Github <https://github.com/ethereum/homebrew-ethereum/commits/master/solidity.rb>`_.
|
||||
|
||||
Follow the history links until you have a raw file link of a
|
||||
Follow the history links until you have a raw file link of a
|
||||
specific commit of ``solidity.rb``.
|
||||
|
||||
Install it using ``brew``:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
brew unlink solidity
|
||||
# Install 0.4.8
|
||||
@ -142,7 +145,7 @@ Install it using ``brew``:
|
||||
|
||||
Gentoo Linux also provides a solidity package that can be installed using ``emerge``:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
emerge dev-lang/solidity
|
||||
|
||||
@ -151,29 +154,18 @@ Gentoo Linux also provides a solidity package that can be installed using ``emer
|
||||
Building from Source
|
||||
====================
|
||||
|
||||
Clone the Repository
|
||||
--------------------
|
||||
Prerequisites - Linux
|
||||
---------------------
|
||||
|
||||
To clone the source code, execute the following command:
|
||||
You need to install the following dependencies for Linux builds of Solidity:
|
||||
|
||||
.. code:: bash
|
||||
+-----------------------------------+-------------------------------------------------------+
|
||||
| Software | Notes |
|
||||
+===================================+=======================================================+
|
||||
| `Git for Linux`_ | Command-line tool for retrieving source from Github. |
|
||||
+-----------------------------------+-------------------------------------------------------+
|
||||
|
||||
git clone --recursive https://github.com/ethereum/solidity.git
|
||||
cd solidity
|
||||
|
||||
If you want to help developing Solidity,
|
||||
you should fork Solidity and add your personal fork as a second remote:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
cd solidity
|
||||
git remote add personal git@github.com:[username]/solidity.git
|
||||
|
||||
Solidity has git submodules. Ensure they are properly loaded:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
git submodule update --init --recursive
|
||||
.. _Git for Linux: https://git-scm.com/download/linux
|
||||
|
||||
Prerequisites - macOS
|
||||
---------------------
|
||||
@ -187,7 +179,7 @@ If you are installing Xcode for the first time, or have just installed a new
|
||||
version then you will need to agree to the license before you can do
|
||||
command-line builds:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
sudo xcodebuild -license accept
|
||||
|
||||
@ -201,7 +193,7 @@ if you ever want to start again from scratch.
|
||||
Prerequisites - Windows
|
||||
-----------------------
|
||||
|
||||
You will need to install the following dependencies for Windows builds of Solidity:
|
||||
You need to install the following dependencies for Windows builds of Solidity:
|
||||
|
||||
+-----------------------------------+-------------------------------------------------------+
|
||||
| Software | Notes |
|
||||
@ -236,21 +228,36 @@ in Visual Studio 2017 Build Tools or Visual Studio 2017:
|
||||
.. _Visual Studio 2017: https://www.visualstudio.com/vs/
|
||||
.. _Visual Studio 2017 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017
|
||||
|
||||
Clone the Repository
|
||||
--------------------
|
||||
|
||||
To clone the source code, execute the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone --recursive https://github.com/ethereum/solidity.git
|
||||
cd solidity
|
||||
|
||||
If you want to help developing Solidity,
|
||||
you should fork Solidity and add your personal fork as a second remote:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git remote add personal git@github.com:[username]/solidity.git
|
||||
|
||||
External Dependencies
|
||||
---------------------
|
||||
|
||||
We now have a "one button" script which installs all required external dependencies
|
||||
on macOS, Windows and on numerous Linux distros. This used to be a multi-step
|
||||
manual process, but is now a one-liner:
|
||||
We have a helper script which installs all required external dependencies
|
||||
on macOS, Windows and on numerous Linux distros.
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
./scripts/install_deps.sh
|
||||
|
||||
Or, on Windows:
|
||||
|
||||
.. code:: bat
|
||||
.. code-block:: bat
|
||||
|
||||
scripts\install_deps.bat
|
||||
|
||||
@ -261,9 +268,11 @@ Command-Line Build
|
||||
**Be sure to install External Dependencies (see above) before build.**
|
||||
|
||||
Solidity project uses CMake to configure the build.
|
||||
You might want to install ccache to speed up repeated builds.
|
||||
CMake will pick it up automatically.
|
||||
Building Solidity is quite similar on Linux, macOS and other Unices:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
@ -271,14 +280,14 @@ Building Solidity is quite similar on Linux, macOS and other Unices:
|
||||
|
||||
or even easier:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
#note: this will install binaries solc and soltest at usr/local/bin
|
||||
./scripts/build.sh
|
||||
|
||||
And even for Windows:
|
||||
And for Windows:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
@ -291,7 +300,7 @@ should result in Visual Studio firing up. We suggest building
|
||||
|
||||
Alternatively, you can build for Windows on the command-line, like so:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
cmake --build . --config RelWithDebInfo
|
||||
|
||||
@ -300,6 +309,29 @@ CMake options
|
||||
|
||||
If you are interested what CMake options are available run ``cmake .. -LH``.
|
||||
|
||||
.. _smt_solvers_build:
|
||||
|
||||
SMT Solvers
|
||||
-----------
|
||||
Solidity can be built against SMT solvers and will do so by default if
|
||||
they are found in the system. Each solver can be disabled by a `cmake` option.
|
||||
|
||||
*Note: In some cases, this can also be a potential workaround for build failures.*
|
||||
|
||||
|
||||
Inside the build folder you can disable them, since they are enabled by default:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# disables only Z3 SMT Solver.
|
||||
cmake .. -DUSE_Z3=OFF
|
||||
|
||||
# disables only CVC4 SMT Solver.
|
||||
cmake .. -DUSE_CVC4=OFF
|
||||
|
||||
# disables both Z3 and CVC4
|
||||
cmake .. -DUSE_CVC4=OFF -DUSE_Z3=OFF
|
||||
|
||||
The version string in detail
|
||||
============================
|
||||
|
||||
@ -308,7 +340,7 @@ The Solidity version string contains four parts:
|
||||
- the version number
|
||||
- pre-release tag, usually set to ``develop.YYYY.MM.DD`` or ``nightly.YYYY.MM.DD``
|
||||
- commit in the format of ``commit.GITHASH``
|
||||
- platform has arbitrary number of items, containing details about the platform and compiler
|
||||
- platform, which has an arbitrary number of items, containing details about the platform and compiler
|
||||
|
||||
If there are local modifications, the commit will be postfixed with ``.mod``.
|
||||
|
||||
|
@ -8,15 +8,16 @@ Introduction to Smart Contracts
|
||||
A Simple Smart Contract
|
||||
***********************
|
||||
|
||||
Let us begin with the most basic example. It is fine if you do not understand everything
|
||||
right now, we will go into more detail later.
|
||||
Let us begin with a basic example that sets the value of a variable and exposes
|
||||
it for other contracts to access. It is fine if you do not understand
|
||||
everything right now, we will go into more detail later.
|
||||
|
||||
Storage
|
||||
=======
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract SimpleStorage {
|
||||
uint storedData;
|
||||
@ -32,15 +33,15 @@ Storage
|
||||
|
||||
The first line simply tells that the source code is written for
|
||||
Solidity version 0.4.0 or anything newer that does not break functionality
|
||||
(up to, but not including, version 0.5.0). This is to ensure that the
|
||||
contract does not suddenly behave differently with a new compiler version. The keyword ``pragma`` is called that way because, in general,
|
||||
pragmas are instructions for the compiler about how to treat the
|
||||
(up to, but not including, version 0.6.0). This is to ensure that the
|
||||
contract is not compilable with a new (breaking) compiler version, where it could behave differently.
|
||||
So-called pragmas are common instructions for compilers about how to treat the
|
||||
source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_).
|
||||
|
||||
A contract in the sense of Solidity is a collection of code (its *functions*) and
|
||||
data (its *state*) that resides at a specific address on the Ethereum
|
||||
blockchain. The line ``uint storedData;`` declares a state variable called ``storedData`` of
|
||||
type ``uint`` (unsigned integer of 256 bits). You can think of it as a single slot
|
||||
type ``uint`` (*u*nsigned *int*eger of *256* bits). You can think of it as a single slot
|
||||
in a database that can be queried and altered by calling functions of the
|
||||
code that manages the database. In the case of Ethereum, this is always the owning
|
||||
contract. And in this case, the functions ``set`` and ``get`` can be used to modify
|
||||
@ -49,8 +50,8 @@ 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.
|
||||
|
||||
This contract does not do much yet (due to the infrastructure
|
||||
built by Ethereum) apart from allowing anyone to store a single number that is accessible by
|
||||
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
|
||||
anyone in the world without a (feasible) way to prevent you from publishing
|
||||
this number. Of course, anyone could just call ``set`` again with a different value
|
||||
and overwrite your number, but the number will still be stored in the history
|
||||
@ -62,7 +63,7 @@ so that only you can alter the number.
|
||||
the ASCII character set. It is possible to store UTF-8 encoded data in string variables.
|
||||
|
||||
.. warning::
|
||||
Be careful with using Unicode text as similarly looking (or even identical) characters can
|
||||
Be careful with using Unicode text, as similar looking (or even identical) characters can
|
||||
have different code points and as such will be encoded as a different byte array.
|
||||
|
||||
.. index:: ! subcurrency
|
||||
@ -72,39 +73,40 @@ Subcurrency Example
|
||||
|
||||
The following contract will implement the simplest form of a
|
||||
cryptocurrency. It is possible to generate coins out of thin air, but
|
||||
only the person that created the contract will be able to do that (it is trivial
|
||||
only the person that created the contract will be able to do that (it is easy
|
||||
to implement a different issuance scheme).
|
||||
Furthermore, anyone can send coins to each other without any need for
|
||||
registering with username and password - all you need is an Ethereum keypair.
|
||||
Furthermore, anyone can send coins to each other without a need for
|
||||
registering with username and password — all you need is an Ethereum keypair.
|
||||
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.21;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
contract Coin {
|
||||
// The keyword "public" makes those variables
|
||||
// readable from outside.
|
||||
// easily readable from outside.
|
||||
address public minter;
|
||||
mapping (address => uint) public balances;
|
||||
|
||||
// Events allow light clients to react on
|
||||
// Events allow light clients to react to
|
||||
// changes efficiently.
|
||||
event Sent(address from, address to, uint amount);
|
||||
|
||||
// This is the constructor whose code is
|
||||
// run only when the contract is created.
|
||||
function Coin() public {
|
||||
constructor() public {
|
||||
minter = msg.sender;
|
||||
}
|
||||
|
||||
function mint(address receiver, uint amount) public {
|
||||
if (msg.sender != minter) return;
|
||||
require(msg.sender == minter);
|
||||
require(amount < 1e60);
|
||||
balances[receiver] += amount;
|
||||
}
|
||||
|
||||
function send(address receiver, uint amount) public {
|
||||
if (balances[msg.sender] < amount) return;
|
||||
require(amount <= balances[msg.sender], "Insufficient balance.");
|
||||
balances[msg.sender] -= amount;
|
||||
balances[receiver] += amount;
|
||||
emit Sent(msg.sender, receiver, amount);
|
||||
@ -116,15 +118,15 @@ This contract introduces some new concepts, let us go through them one by one.
|
||||
The line ``address public minter;`` declares a state variable of type address
|
||||
that is publicly accessible. The ``address`` type is a 160-bit value
|
||||
that does not allow any arithmetic operations. It is suitable for
|
||||
storing addresses of contracts or keypairs belonging to external
|
||||
storing addresses of contracts or of keypairs belonging to external
|
||||
persons. The keyword ``public`` automatically generates a function that
|
||||
allows you to access the current value of the state variable
|
||||
from outside of the contract.
|
||||
Without this keyword, other contracts have no way to access the variable.
|
||||
The code of the function generated by the compiler is roughly equivalent
|
||||
to the following::
|
||||
to the following (ignore ``external`` and ``view`` for now)::
|
||||
|
||||
function minter() returns (address) { return minter; }
|
||||
function minter() external view returns (address) { return minter; }
|
||||
|
||||
Of course, adding a function exactly like that will not work
|
||||
because we would have a
|
||||
@ -137,17 +139,17 @@ The next line, ``mapping (address => uint) public balances;`` also
|
||||
creates a public state variable, but it is a more complex datatype.
|
||||
The type maps addresses to unsigned integers.
|
||||
Mappings can be seen as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_ which are
|
||||
virtually initialized such that every possible key exists and is mapped to a
|
||||
virtually initialized such that every possible key exists from the start and is mapped to a
|
||||
value whose byte-representation is all zeros. This analogy does not go
|
||||
too far, though, as it is neither possible to obtain a list of all keys of
|
||||
a mapping, nor a list of all values. So either keep in mind (or
|
||||
better, keep a list or use a more advanced data type) what you
|
||||
added to the mapping or use it in a context where this is not needed,
|
||||
like this one. The :ref:`getter function<getter-functions>` created by the ``public`` keyword
|
||||
added to the mapping or use it in a context where this is not needed.
|
||||
The :ref:`getter function<getter-functions>` created by the ``public`` keyword
|
||||
is a bit more complex in this case. It roughly looks like the
|
||||
following::
|
||||
|
||||
function balances(address _account) public view returns (uint) {
|
||||
function balances(address _account) external view returns (uint) {
|
||||
return balances[_account];
|
||||
}
|
||||
|
||||
@ -162,7 +164,9 @@ a so-called "event" which is emitted in the last line of the function
|
||||
listen for those events being emitted on the blockchain without much
|
||||
cost. As soon as it is emitted, the listener will also receive the
|
||||
arguments ``from``, ``to`` and ``amount``, which makes it easy to track
|
||||
transactions. In order to listen for this event, you would use ::
|
||||
transactions. In order to listen for this event, you would use the following
|
||||
JavaScript code (which assumes that ``Coin`` is a contract object created via
|
||||
web3.js or a similar module)::
|
||||
|
||||
Coin.Sent().watch({}, '', function(error, result) {
|
||||
if (!error) {
|
||||
@ -180,23 +184,34 @@ the user interface.
|
||||
|
||||
.. index:: coin
|
||||
|
||||
The special function ``Coin`` is the
|
||||
constructor which is run during creation of the contract and
|
||||
The constructor is a special function which is run during creation of the contract and
|
||||
cannot be called afterwards. It permanently stores the address of the person creating the
|
||||
contract: ``msg`` (together with ``tx`` and ``block``) is a magic global variable that
|
||||
contract: ``msg`` (together with ``tx`` and ``block``) is a special global variable that
|
||||
contains some properties which allow access to the blockchain. ``msg.sender`` is
|
||||
always the address where the current (external) function call came from.
|
||||
|
||||
Finally, the functions that will actually end up with the contract and can be called
|
||||
by users and contracts alike are ``mint`` and ``send``.
|
||||
If ``mint`` is called by anyone except the account that created the contract,
|
||||
nothing will happen. On the other hand, ``send`` can be used by anyone (who already
|
||||
has some of these coins) to send coins to anyone else. Note that if you use
|
||||
this contract to send coins to an address, you will not see anything when you
|
||||
look at that address on a blockchain explorer, because the fact that you sent
|
||||
coins and the changed balances are only stored in the data storage of this
|
||||
particular coin contract. By the use of events it is relatively easy to create
|
||||
a "blockchain explorer" that tracks transactions and balances of your new coin.
|
||||
nothing will happen. This is ensured by the special function ``require`` which
|
||||
causes all changes to be reverted if its argument evaluates to false.
|
||||
The second call to ``require`` ensures that there will not be too many coins,
|
||||
which could cause overflow errors later.
|
||||
|
||||
On the other hand, ``send`` can be used by anyone (who already
|
||||
has some of these coins) to send coins to anyone else. If you do not have
|
||||
enough coins to send, the ``require`` call will fail and also provide the
|
||||
user with an appropriate error message string.
|
||||
|
||||
.. note::
|
||||
If you use
|
||||
this contract to send coins to an address, you will not see anything when you
|
||||
look at that address on a blockchain explorer, because the fact that you sent
|
||||
coins and the changed balances are only stored in the data storage of this
|
||||
particular coin contract. By the use of events it is relatively easy to create
|
||||
a "blockchain explorer" that tracks transactions and balances of your new coin,
|
||||
but you have to inspect the coin contract address and not the addresses of the
|
||||
coin owners.
|
||||
|
||||
.. _blockchain-basics:
|
||||
|
||||
@ -206,7 +221,7 @@ Blockchain Basics
|
||||
|
||||
Blockchains as a concept are not too hard to understand for programmers. The reason is that
|
||||
most of the complications (mining, `hashing <https://en.wikipedia.org/wiki/Cryptographic_hash_function>`_, `elliptic-curve cryptography <https://en.wikipedia.org/wiki/Elliptic_curve_cryptography>`_, `peer-to-peer networks <https://en.wikipedia.org/wiki/Peer-to-peer>`_, etc.)
|
||||
are just there to provide a certain set of features and promises. Once you accept these
|
||||
are just there to provide a certain set of features and promises for the platform. Once you accept these
|
||||
features as given, you do not have to worry about the underlying technology - or do you have
|
||||
to know how Amazon's AWS works internally in order to use it?
|
||||
|
||||
@ -221,7 +236,7 @@ If you want to change something in the database, you have to create a so-called
|
||||
which has to be accepted by all others.
|
||||
The word transaction implies that the change you want to make (assume you want to change
|
||||
two values at the same time) is either not done at all or completely applied. Furthermore,
|
||||
while your transaction is applied to the database, no other transaction can alter it.
|
||||
while your transaction is being applied to the database, no other transaction can alter it.
|
||||
|
||||
As an example, imagine a table that lists the balances of all accounts in an
|
||||
electronic currency. If a transfer from one account to another is requested,
|
||||
@ -240,12 +255,13 @@ only the person holding the keys to the account can transfer money from it.
|
||||
Blocks
|
||||
======
|
||||
|
||||
One major obstacle to overcome is what, in Bitcoin terms, is called a "double-spend attack":
|
||||
What happens if two transactions exist in the network that both want to empty an account,
|
||||
a so-called conflict?
|
||||
One major obstacle to overcome is what (in Bitcoin terms) is called a "double-spend attack":
|
||||
What happens if two transactions exist in the network that both want to empty an account?
|
||||
Only one of the transactions can be valid, typically the one that is accepted first.
|
||||
The problem is that "first" is not an objective term in a peer-to-peer network.
|
||||
|
||||
The abstract answer to this is that you do not have to care. An order of the transactions
|
||||
will be selected for you, the transactions will be bundled into what is called a "block"
|
||||
The abstract answer to this is that you do not have to care. A globally accepted order of the transactions
|
||||
will be selected for you, solving the conflict. The transactions will be bundled into what is called a "block"
|
||||
and then they will be executed and distributed among all participating nodes.
|
||||
If two transactions contradict each other, the one that ends up being second will
|
||||
be rejected and not become part of the block.
|
||||
@ -256,10 +272,16 @@ Ethereum this is roughly every 17 seconds.
|
||||
|
||||
As part of the "order selection mechanism" (which is called "mining") it may happen that
|
||||
blocks are reverted from time to time, but only at the "tip" of the chain. The more
|
||||
blocks that are added on top, the less likely it is. So it might be that your transactions
|
||||
blocks are added on top of a particular block, the less likely this block will be reverted. So it might be that your transactions
|
||||
are reverted and even removed from the blockchain, but the longer you wait, the less
|
||||
likely it will be.
|
||||
|
||||
.. note::
|
||||
Transactions are not guaranteed to be included in the next block or any specific future block,
|
||||
since it is not up to the submitter of a transaction, but up to the miners to determine in which block the transaction is included.
|
||||
|
||||
If you want to schedule future calls of your contract, you can use
|
||||
the `alarm clock <http://www.ethereum-alarm-clock.com/>`_ or a similar oracle service.
|
||||
|
||||
.. _the-ethereum-virtual-machine:
|
||||
|
||||
@ -301,7 +323,7 @@ Every account has a persistent key-value store mapping 256-bit words to 256-bit
|
||||
words called **storage**.
|
||||
|
||||
Furthermore, every account has a **balance** in
|
||||
Ether (in "Wei" to be exact) which can be modified by sending transactions that
|
||||
Ether (in "Wei" to be exact, `1 ether` is `10**18 wei`) which can be modified by sending transactions that
|
||||
include Ether.
|
||||
|
||||
.. index:: ! transaction
|
||||
@ -310,19 +332,20 @@ Transactions
|
||||
============
|
||||
|
||||
A transaction is a message that is sent from one account to another
|
||||
account (which might be the same or the special zero-account, see below).
|
||||
It can include binary data (its payload) and Ether.
|
||||
account (which might be the same or empty, see below).
|
||||
It can include binary data (which is called "payload") and Ether.
|
||||
|
||||
If the target account contains code, that code is executed and
|
||||
the payload is provided as input data.
|
||||
|
||||
If the target account is the zero-account (the account with the
|
||||
address ``0``), the transaction creates a **new contract**.
|
||||
If the target account is not set (the transaction does not have
|
||||
a recipient or the recipient is set to ``null``), the transaction
|
||||
creates a **new contract**.
|
||||
As already mentioned, the address of that contract is not
|
||||
the zero address but an address derived from the sender and
|
||||
its number of transactions sent (the "nonce"). The payload
|
||||
of such a contract creation transaction is taken to be
|
||||
EVM bytecode and executed. The output of this execution is
|
||||
EVM bytecode and executed. The output data of this execution is
|
||||
permanently stored as the code of the contract.
|
||||
This means that in order to create a contract, you do not
|
||||
send the actual code of the contract, but in fact code that
|
||||
@ -341,14 +364,14 @@ Gas
|
||||
|
||||
Upon creation, each transaction is charged with a certain amount of **gas**,
|
||||
whose purpose is to limit the amount of work that is needed to execute
|
||||
the transaction and to pay for this execution. While the EVM executes the
|
||||
the transaction and to pay for this execution at the same time. While the EVM executes the
|
||||
transaction, the gas is gradually depleted according to specific rules.
|
||||
|
||||
The **gas price** is a value set by the creator of the transaction, who
|
||||
has to pay ``gas_price * gas`` up front from the sending account.
|
||||
If some gas is left after the execution, it is refunded in the same way.
|
||||
If some gas is left after the execution, it is refunded to the creator in the same way.
|
||||
|
||||
If the gas is used up at any point (i.e. it is negative),
|
||||
If the gas is used up at any point (i.e. it would be negative),
|
||||
an out-of-gas exception is triggered, which reverts all modifications
|
||||
made to the state in the current call frame.
|
||||
|
||||
@ -357,23 +380,27 @@ made to the state in the current call frame.
|
||||
Storage, Memory and the Stack
|
||||
=============================
|
||||
|
||||
Each account has a persistent memory area which is called **storage**.
|
||||
Storage is a key-value store that maps 256-bit words to 256-bit words.
|
||||
It is not possible to enumerate storage from within a contract
|
||||
and it is comparatively costly to read and even more so, to modify
|
||||
storage. A contract can neither read nor write to any storage apart
|
||||
from its own.
|
||||
The Ethereum Virtual Machine has three areas where it can store data-
|
||||
storage, memory and the stack, which are explained in the following
|
||||
paragraphs.
|
||||
|
||||
The second memory area is called **memory**, of which a contract obtains
|
||||
Each account has a data area called **storage**, which is persistent between function calls
|
||||
and transactions.
|
||||
Storage is a key-value store that maps 256-bit words to 256-bit words.
|
||||
It is not possible to enumerate storage from within a contract and it is
|
||||
comparatively costly to read, and even more to modify storage.
|
||||
A contract can neither read nor write to any storage apart from its own.
|
||||
|
||||
The second data area is called **memory**, of which a contract obtains
|
||||
a freshly cleared instance for each message call. Memory is linear and can be
|
||||
addressed at byte level, but reads are limited to a width of 256 bits, while writes
|
||||
can be either 8 bits or 256 bits wide. Memory is expanded by a word (256-bit), when
|
||||
accessing (either reading or writing) a previously untouched memory word (ie. any offset
|
||||
accessing (either reading or writing) a previously untouched memory word (i.e. any offset
|
||||
within a word). At the time of expansion, the cost in gas must be paid. Memory is more
|
||||
costly the larger it grows (it scales quadratically).
|
||||
|
||||
The EVM is not a register machine but a stack machine, so all
|
||||
computations are performed on an area called the **stack**. It has a maximum size of
|
||||
computations are performed on an data area called the **stack**. It has a maximum size of
|
||||
1024 elements and contains words of 256 bits. Access to the stack is
|
||||
limited to the top end in the following way:
|
||||
It is possible to copy one of
|
||||
@ -381,7 +408,8 @@ the topmost 16 elements to the top of the stack or swap the
|
||||
topmost element with one of the 16 elements below it.
|
||||
All other operations take the topmost two (or one, or more, depending on
|
||||
the operation) elements from the stack and push the result onto the stack.
|
||||
Of course it is possible to move stack elements to storage or memory,
|
||||
Of course it is possible to move stack elements to storage or memory
|
||||
in order to get deeper access to the stack,
|
||||
but it is not possible to just access arbitrary elements deeper in the stack
|
||||
without first removing the top of the stack.
|
||||
|
||||
@ -391,13 +419,17 @@ Instruction Set
|
||||
===============
|
||||
|
||||
The instruction set of the EVM is kept minimal in order to avoid
|
||||
incorrect implementations which could cause consensus problems.
|
||||
All instructions operate on the basic data type, 256-bit words.
|
||||
incorrect or inconsistent implementations which could cause consensus problems.
|
||||
All instructions operate on the basic data type, 256-bit words or on slices of memory
|
||||
(or other byte arrays).
|
||||
The usual arithmetic, bit, logical and comparison operations are present.
|
||||
Conditional and unconditional jumps are possible. Furthermore,
|
||||
contracts can access relevant properties of the current block
|
||||
like its number and timestamp.
|
||||
|
||||
For a complete list, please see the :ref:`list of opcodes <opcodes>` as part of the inline
|
||||
assembly documentation.
|
||||
|
||||
.. index:: ! message call, function;call
|
||||
|
||||
Message Calls
|
||||
@ -412,7 +444,7 @@ a top-level message call which in turn can create further message calls.
|
||||
A contract can decide how much of its remaining **gas** should be sent
|
||||
with the inner message call and how much it wants to retain.
|
||||
If an out-of-gas exception happens in the inner call (or any
|
||||
other exception), this will be signalled by an error value put onto the stack.
|
||||
other exception), this will be signaled by an error value put onto the stack.
|
||||
In this case, only the gas sent together with the call is used up.
|
||||
In Solidity, the calling contract causes a manual exception by default in
|
||||
such situations, so that exceptions "bubble up" the call stack.
|
||||
@ -422,9 +454,12 @@ will receive a freshly cleared instance of memory and has access to the
|
||||
call payload - which will be provided in a separate area called the **calldata**.
|
||||
After it has finished execution, it can return data which will be stored at
|
||||
a location in the caller's memory preallocated by the caller.
|
||||
All such calls are fully synchronous.
|
||||
|
||||
Calls are **limited** to a depth of 1024, which means that for more complex
|
||||
operations, loops should be preferred over recursive calls.
|
||||
operations, loops should be preferred over recursive calls. Furthermore,
|
||||
only 63/64th of the gas can be forwarded in a message call, which causes a
|
||||
depth limit of a little less than 1000 in practice.
|
||||
|
||||
.. index:: delegatecall, callcode, library
|
||||
|
||||
@ -442,7 +477,7 @@ refer to the calling contract, only the code is taken from the called address.
|
||||
|
||||
This makes it possible to implement the "library" feature in Solidity:
|
||||
Reusable library code that can be applied to a contract's storage, e.g. in
|
||||
order to implement a complex data structure.
|
||||
order to implement a complex data structure.
|
||||
|
||||
.. index:: log
|
||||
|
||||
@ -451,13 +486,13 @@ Logs
|
||||
|
||||
It is possible to store data in a specially indexed data structure
|
||||
that maps all the way up to the block level. This feature called **logs**
|
||||
is used by Solidity in order to implement **events**.
|
||||
is used by Solidity in order to implement :ref:`events <events>`.
|
||||
Contracts cannot access log data after it has been created, but they
|
||||
can be efficiently accessed from outside the blockchain.
|
||||
Since some part of the log data is stored in `bloom filters <https://en.wikipedia.org/wiki/Bloom_filter>`_, it is
|
||||
possible to search for this data in an efficient and cryptographically
|
||||
secure way, so network peers that do not download the whole blockchain
|
||||
("light clients") can still find these logs.
|
||||
(so-called "light clients") can still find these logs.
|
||||
|
||||
.. index:: contract creation
|
||||
|
||||
@ -465,26 +500,22 @@ Create
|
||||
======
|
||||
|
||||
Contracts can even create other contracts using a special opcode (i.e.
|
||||
they do not simply call the zero address). The only difference between
|
||||
they do not simply call the zero address as a transaction would). The only difference between
|
||||
these **create calls** and normal message calls is that the payload data is
|
||||
executed and the result stored as code and the caller / creator
|
||||
receives the address of the new contract on the stack.
|
||||
|
||||
.. index:: selfdestruct
|
||||
.. index:: selfdestruct, self-destruct, deactivate
|
||||
|
||||
Self-destruct
|
||||
=============
|
||||
Deactivate and Self-destruct
|
||||
============================
|
||||
|
||||
The only possibility that code is removed from the blockchain is
|
||||
when a contract at that address performs the ``selfdestruct`` operation.
|
||||
The remaining Ether stored at that address is sent to a designated
|
||||
target and then the storage and code is removed from the state.
|
||||
The only way to remove code from the blockchain is when a contract at that address performs the ``selfdestruct`` operation. The remaining Ether stored at that address is sent to a designated target and then the storage and code is removed from the state. Removing the contract in theory sounds like a good idea, but it is potentially dangerous, as if someone sends Ether to removed contracts, the Ether is forever lost.
|
||||
|
||||
.. warning:: Even if a contract's code does not contain a call to ``selfdestruct``,
|
||||
it can still perform that operation using ``delegatecall`` or ``callcode``.
|
||||
.. note::
|
||||
Even if a contract's code does not contain a call to ``selfdestruct``, it can still perform that operation using ``delegatecall`` or ``callcode``.
|
||||
|
||||
.. note:: The pruning of old contracts may or may not be implemented by Ethereum
|
||||
clients. Additionally, archive nodes could choose to keep the contract storage
|
||||
and code indefinitely.
|
||||
If you want to deactivate your contracts, you should instead **disable** them by changing some internal state which causes all functions to revert. This makes it impossible to use the contract, as it returns Ether immediately.
|
||||
|
||||
.. note:: Currently **external accounts** cannot be removed from the state.
|
||||
.. warning::
|
||||
Even if a contract is removed by "selfdestruct", it is still part of the history of the blockchain and probably retained by most Ethereum nodes. So using "selfdestruct" is not the same as deleting data from a hard disk.
|
||||
|
@ -2,15 +2,29 @@
|
||||
Layout of a Solidity Source File
|
||||
********************************
|
||||
|
||||
Source files can contain an arbitrary number of contract definitions, include directives
|
||||
and pragma directives.
|
||||
Source files can contain an arbitrary number of
|
||||
:ref:`contract definitions<contract_structure>`, import_ directives
|
||||
and :ref:`pragma directives<pragma>`.
|
||||
|
||||
.. index:: ! pragma
|
||||
|
||||
.. _pragma:
|
||||
|
||||
Pragmas
|
||||
=======
|
||||
|
||||
The ``pragma`` keyword can be used to enable certain compiler features
|
||||
or checks. A pragma directive is always local to a source file, so
|
||||
you have to add the pragma to all your files if you want enable it
|
||||
in all of your project. If you :ref:`import<import>` another file, the pragma
|
||||
from that file will not automatically apply to the importing file.
|
||||
|
||||
.. index:: ! pragma, version
|
||||
|
||||
.. _version_pragma:
|
||||
|
||||
Version Pragma
|
||||
==============
|
||||
--------------
|
||||
|
||||
Source files can (and should) be annotated with a so-called version pragma to reject
|
||||
being compiled with future compiler versions that might introduce incompatible
|
||||
@ -35,6 +49,53 @@ the exact version of the compiler, so that bugfix releases are still possible.
|
||||
It is possible to specify much more complex rules for the compiler version,
|
||||
the expression follows those used by `npm <https://docs.npmjs.com/misc/semver>`_.
|
||||
|
||||
.. note::
|
||||
Using the version pragma will *not* change the version of the compiler.
|
||||
It will also *not* enable or disable features of the compiler. It will just
|
||||
instruct the compiler to check whether its version matches the one
|
||||
required by the pragma. If it does not match, the compiler will issue
|
||||
an error.
|
||||
|
||||
.. index:: ! pragma, experimental
|
||||
|
||||
.. _experimental_pragma:
|
||||
|
||||
Experimental Pragma
|
||||
-------------------
|
||||
|
||||
The second pragma is the experimental pragma. It can be used to enable
|
||||
features of the compiler or language that are not yet enabled by default.
|
||||
The following experimental pragmas are currently supported:
|
||||
|
||||
|
||||
ABIEncoderV2
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The new ABI encoder is able to encode and decode arbitrarily nested
|
||||
arrays and structs. It produces less optimal code (the optimizer
|
||||
for this part of the code is still under development) and has not
|
||||
received as much testing as the old encoder. You can activate it
|
||||
using ``pragma experimental ABIEncoderV2;``.
|
||||
|
||||
.. _smt_checker:
|
||||
|
||||
SMTChecker
|
||||
~~~~~~~~~~
|
||||
|
||||
This component has to be enabled when the Solidity compiler is built
|
||||
and therefore it is not available in all Solidity binaries.
|
||||
The :ref:`build instructions<smt_solvers_build>` explain how to activate this option.
|
||||
It is activated for the Ubuntu PPA releases in most versions,
|
||||
but not for solc-js, the Docker images, Windows binaries or the
|
||||
statically-built Linux binaries.
|
||||
|
||||
If you use
|
||||
``pragma experimental SMTChecker;``, then you get additional
|
||||
safety warnings which are obtained by querying an SMT solver.
|
||||
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
|
||||
|
||||
.. _import:
|
||||
@ -56,18 +117,27 @@ At a global level, you can use import statements of the following form:
|
||||
|
||||
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 simple form is not recommended for use, because it pollutes the namespace in an
|
||||
unpredictable way: If you add new top-level items inside "filename", they will automatically
|
||||
appear in all files that import like this from "filename". It is better to import specific
|
||||
symbols explicitly.
|
||||
|
||||
The following example creates a new global symbol ``symbolName`` whose members are all
|
||||
the global symbols from ``"filename"``.
|
||||
|
||||
::
|
||||
|
||||
import * as symbolName from "filename";
|
||||
|
||||
...creates a new global symbol ``symbolName`` whose members are all the global symbols from ``"filename"``.
|
||||
If there is a naming collision, you can also rename symbols while importing.
|
||||
This code
|
||||
creates new global symbols ``alias`` and ``symbol2`` which reference ``symbol1`` and ``symbol2`` from inside ``"filename"``, respectively.
|
||||
|
||||
::
|
||||
|
||||
import {symbol1 as alias, symbol2} from "filename";
|
||||
|
||||
...creates new global symbols ``alias`` and ``symbol2`` which reference ``symbol1`` and ``symbol2`` from ``"filename"``, respectively.
|
||||
|
||||
|
||||
Another syntax is not part of ES6, but probably convenient:
|
||||
|
||||
@ -77,6 +147,10 @@ Another syntax is not part of ES6, but probably convenient:
|
||||
|
||||
which is equivalent to ``import * as symbolName from "filename";``.
|
||||
|
||||
.. note::
|
||||
If you use `import "filename.sol" as moduleName;`, you access a contract called `C`
|
||||
from inside `"filename.sol"` as `moduleName.C` and not by using `C` directly.
|
||||
|
||||
Paths
|
||||
-----
|
||||
|
||||
@ -93,47 +167,52 @@ It depends on the compiler (see below) how to actually resolve the paths.
|
||||
In general, the directory hierarchy does not need to strictly map onto your local
|
||||
filesystem, it can also map to resources discovered via e.g. ipfs, http or git.
|
||||
|
||||
.. 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.
|
||||
|
||||
Use in Actual Compilers
|
||||
-----------------------
|
||||
|
||||
When the compiler is invoked, it is not only possible to specify how to
|
||||
discover the first element of a path, but it is possible to specify path prefix
|
||||
remappings so that e.g. ``github.com/ethereum/dapp-bin/library`` is remapped to
|
||||
``/usr/local/dapp-bin/library`` and the compiler will read the files from there.
|
||||
If multiple remappings can be applied, the one with the longest key is tried first. This
|
||||
allows for a "fallback-remapping" with e.g. ``""`` maps to
|
||||
``"/usr/local/include/solidity"``. Furthermore, these remappings can
|
||||
depend on the context, which allows you to configure packages to
|
||||
import e.g. different versions of a library of the same name.
|
||||
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), these remappings are provided as
|
||||
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 (where target defaults to prefix in that
|
||||
``=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 completely backwards-compatible (as long
|
||||
as no filename contains = or :) and thus not a breaking change. All imports
|
||||
in files in or below the directory ``context`` that import a file that
|
||||
starts with ``prefix`` are redirected by replacing ``prefix`` by ``target``.
|
||||
their dependencies).
|
||||
|
||||
So as an example, if you clone
|
||||
``github.com/ethereum/dapp-bin/`` locally to ``/usr/local/dapp-bin``, you can use
|
||||
the following in your source file:
|
||||
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;
|
||||
|
||||
and then run the compiler as
|
||||
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 some module that uses a
|
||||
very old version of dapp-bin. That old version of dapp-bin is checked
|
||||
out at ``/usr/local/dapp-bin_old``, then you can use
|
||||
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
|
||||
|
||||
@ -141,28 +220,29 @@ out at ``/usr/local/dapp-bin_old``, then you can use
|
||||
module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \
|
||||
source.sol
|
||||
|
||||
so that all imports in ``module2`` point to the old version but imports
|
||||
in ``module1`` get the new version.
|
||||
This means that all imports in ``module2`` point to the old version but imports
|
||||
in ``module1`` point to the new version.
|
||||
|
||||
Note that 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, just add the
|
||||
remapping ``=/``.
|
||||
.. 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 will also automatically retrieve
|
||||
the file over the network:
|
||||
You can import the iterable mapping by e.g.
|
||||
``import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;``.
|
||||
`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.
|
||||
|
||||
Other source code providers may be added in the future.
|
||||
::
|
||||
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
|
||||
|
||||
Remix may add other source code providers in the future.
|
||||
|
||||
.. index:: ! comment, natspec
|
||||
|
||||
@ -180,6 +260,11 @@ Single-line comments (``//``) and multi-line comments (``/*...*/``) are possible
|
||||
multi-line comment.
|
||||
*/
|
||||
|
||||
.. note::
|
||||
A single-line comment is terminated by any unicode line terminator
|
||||
(LF, VF, FF, CR, NEL, LS or PS) in utf8 encoding. The terminator is still part of
|
||||
the source code after the comment, so if it is not an ascii symbol
|
||||
(these are NEL, LS and PS), it will lead to a parser error.
|
||||
|
||||
Additionally, there is another type of comment called a natspec comment,
|
||||
for which the documentation is not yet written. They are written with a
|
||||
@ -195,17 +280,17 @@ for the two input parameters and two returned values.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
/** @title Shape calculator. */
|
||||
contract shapeCalculator {
|
||||
contract ShapeCalculator {
|
||||
/** @dev Calculates a rectangle's surface and perimeter.
|
||||
* @param w Width of the rectangle.
|
||||
* @param h Height of the rectangle.
|
||||
* @return s The calculated surface.
|
||||
* @return p The calculated perimeter.
|
||||
*/
|
||||
function rectangle(uint w, uint h) returns (uint s, uint p) {
|
||||
function rectangle(uint w, uint h) public pure returns (uint s, uint p) {
|
||||
s = w * h;
|
||||
p = 2 * (w + h);
|
||||
}
|
||||
|
14
docs/lll.rst
Normal file
14
docs/lll.rst
Normal file
@ -0,0 +1,14 @@
|
||||
###
|
||||
LLL
|
||||
###
|
||||
|
||||
.. _lll:
|
||||
|
||||
LLL is a low-level language for the EVM with an s-expressions syntax.
|
||||
|
||||
The Solidity repository contains an LLL compiler, which shares the assembler subsystem with Solidity.
|
||||
However, apart from maintaining that it still compiles, no other improvements are made to it.
|
||||
|
||||
.. warning::
|
||||
|
||||
The LLL codebase is deprecated and will be removed from the Solidity repository in the future.
|
484
docs/make.bat
484
docs/make.bat
@ -1,242 +1,242 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. xml to make Docutils-native XML files
|
||||
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
|
||||
%SPHINXBUILD% 2> nul
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Solidity.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Solidity.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdf" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf
|
||||
cd %BUILDDIR%/..
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdfja" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf-ja
|
||||
cd %BUILDDIR%/..
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "xml" (
|
||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pseudoxml" (
|
||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. xml to make Docutils-native XML files
|
||||
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
|
||||
%SPHINXBUILD% 2> nul
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Solidity.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Solidity.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdf" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf
|
||||
cd %BUILDDIR%/..
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdfja" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf-ja
|
||||
cd %BUILDDIR%/..
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "xml" (
|
||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pseudoxml" (
|
||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
|
@ -4,28 +4,28 @@ Contract Metadata
|
||||
|
||||
.. index:: metadata, contract verification
|
||||
|
||||
The Solidity compiler automatically generates a JSON file, the
|
||||
contract metadata, that contains information about the current contract.
|
||||
It can be used to query the compiler version, the sources used, the ABI
|
||||
and NatSpec documentation in order to more safely interact with the contract
|
||||
and to verify its source code.
|
||||
The Solidity compiler automatically generates a JSON file, the contract
|
||||
metadata, that contains information about the current contract. You can use
|
||||
this file to query the compiler version, the sources used, the ABI and NatSpec
|
||||
documentation to more safely interact with the contract and verify its source
|
||||
code.
|
||||
|
||||
The compiler appends a Swarm hash of the metadata file to the end of the
|
||||
bytecode (for details, see below) of each contract, so that you can retrieve
|
||||
the file in an authenticated way without having to resort to a centralized
|
||||
data provider.
|
||||
|
||||
Of course, you have to publish the metadata file to Swarm (or some other service)
|
||||
so that others can access it. The file can be output by using ``solc --metadata``
|
||||
and the file will be called ``ContractName_meta.json``.
|
||||
It will contain Swarm references to the source code, so you have to upload
|
||||
all source files and the metadata file.
|
||||
You have to publish the metadata file to Swarm (or another service) so that
|
||||
others can access it. You create the file by using the ``solc --metadata``
|
||||
command that generates a file called ``ContractName_meta.json``. It contains
|
||||
Swarm references to the source code, so you have to upload all source files and
|
||||
the metadata file.
|
||||
|
||||
The metadata file has the following format. The example below is presented in a
|
||||
human-readable way. Properly formatted metadata should use quotes correctly,
|
||||
reduce whitespace to a minimum and sort the keys of all objects to arrive at a
|
||||
unique formatting.
|
||||
Comments are of course also not permitted and used here only for explanatory purposes.
|
||||
unique formatting. Comments are not permitted and used here only for
|
||||
explanatory purposes.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
@ -93,14 +93,15 @@ Comments are of course also not permitted and used here only for explanatory pur
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
Note the ABI definition above has no fixed order. It can change with compiler versions.
|
||||
.. warning::
|
||||
Since the bytecode of the resulting contract contains the metadata hash, any
|
||||
change to the metadata results in a change of the bytecode. This includes
|
||||
changes to a filename or path, and since the metadata includes a hash of all the
|
||||
sources used, a single whitespace change results in different metadata, and
|
||||
different bytecode.
|
||||
|
||||
.. note::
|
||||
Since the bytecode of the resulting contract contains the metadata hash, any change to
|
||||
the metadata will result in a change of the bytecode. Furthermore, since the metadata
|
||||
includes a hash of all the sources used, a single whitespace change in any of the source
|
||||
codes will result in a different metadata, and subsequently a different bytecode.
|
||||
Note the ABI definition above has no fixed order. It can change with compiler versions.
|
||||
|
||||
Encoding of the Metadata Hash in the Bytecode
|
||||
=============================================
|
||||
@ -117,21 +118,30 @@ to the end of the deployed bytecode::
|
||||
So in order to retrieve the data, the end of the deployed bytecode can be checked
|
||||
to match that pattern and use the Swarm hash to retrieve the file.
|
||||
|
||||
.. note::
|
||||
The compiler currently uses the "swarm version 0" hash of the metadata,
|
||||
but this might change in the future, so do not rely on this sequence
|
||||
to start with ``0xa1 0x65 'b' 'z' 'z' 'r' '0'``. We might also
|
||||
add additional data to this CBOR structure, so the
|
||||
best option is to use a proper CBOR parser.
|
||||
|
||||
|
||||
Usage for Automatic Interface Generation and NatSpec
|
||||
====================================================
|
||||
|
||||
The metadata is used in the following way: A component that wants to interact
|
||||
with a contract (e.g. Mist) retrieves the code of the contract, from that
|
||||
with a contract (e.g. Mist or any wallet) retrieves the code of the contract, from that
|
||||
the Swarm hash of a file which is then retrieved.
|
||||
That file is JSON-decoded into a structure like above.
|
||||
|
||||
The component can then use the ABI to automatically generate a rudimentary
|
||||
user interface for the contract.
|
||||
|
||||
Furthermore, Mist can use the userdoc to display a confirmation message to the user
|
||||
whenever they interact with the contract.
|
||||
Furthermore, the wallet can use the NatSpec user documentation to display a confirmation message to the user
|
||||
whenever they interact with the contract, together with requesting
|
||||
authorization for the transaction signature.
|
||||
|
||||
Additional information about Ethereum Natural Specification (NatSpec) can be found `here <https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format>`_.
|
||||
Additional information about Ethereum Natural Specification (NatSpec) can be found `here <https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format>`_.
|
||||
|
||||
Usage for Source Code Verification
|
||||
==================================
|
||||
|
@ -32,23 +32,33 @@ Statically-sized variables (everything except mapping and dynamically-sized arra
|
||||
``uint128, uint256, uint128``, as the former will only take up two slots of storage whereas the
|
||||
latter will take up three.
|
||||
|
||||
.. note::
|
||||
The layout of state variables in storage is considered to be part of the external interface
|
||||
of Solidity due to the fact that storage pointers can be passed to libraries. This means that
|
||||
any change to the rules outlined in this section is considered a breaking change
|
||||
of the language and due to its critical nature should be considered very carefully before
|
||||
being executed.
|
||||
|
||||
|
||||
The elements of structs and arrays are stored after each other, just as if they were given explicitly.
|
||||
|
||||
Mappings and Dynamic Arrays
|
||||
===========================
|
||||
|
||||
Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash
|
||||
computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
|
||||
|
||||
The mapping or the dynamic array itself
|
||||
occupies an (unfilled) slot in storage at some position ``p`` according to the above rule (or by
|
||||
recursively applying this rule for mappings to mappings or arrays of arrays). For a dynamic array, this slot stores the number of elements in the array (byte arrays and strings are an exception here, see below). For a mapping, the slot is unused (but it is needed so that two equal mappings after each other will use a different hash distribution).
|
||||
Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key
|
||||
The mapping or the dynamic array itself occupies a slot in storage at some position ``p``
|
||||
according to the above rule (or by recursively applying this rule for mappings of mappings or arrays of arrays). For dynamic arrays,
|
||||
this slot stores the number of elements in the array (byte arrays and strings are an exception, see :ref:`below <bytes-and-string>`).
|
||||
For mappings, the slot is unused (but it is needed so that two equal mappings after each other will use a different
|
||||
hash distribution). Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key
|
||||
``k`` is located at ``keccak256(k . p)`` where ``.`` is concatenation. If the value is again a
|
||||
non-elementary type, the positions are found by adding an offset of ``keccak256(k . p)``.
|
||||
|
||||
``bytes`` and ``string`` store their data in the same slot where also the length is stored if they are short. In particular: If the data is at most ``31`` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. If it is longer, the main slot stores ``length * 2 + 1`` and the data is stored as usual in ``keccak256(slot)``.
|
||||
|
||||
So for the following contract snippet::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract C {
|
||||
struct s { uint a; uint b; }
|
||||
@ -58,27 +68,43 @@ So for the following contract snippet::
|
||||
|
||||
The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``.
|
||||
|
||||
.. _bytes-and-string:
|
||||
|
||||
``bytes`` and ``string``
|
||||
------------------------
|
||||
|
||||
``bytes`` and ``string`` are encoded identically. For short byte arrays, they store their data in the same
|
||||
slot where the length is also stored. In particular: if the data is at most ``31`` bytes long, it is stored
|
||||
in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``.
|
||||
For byte arrays that store data which is ``32`` or more bytes long, the main slot stores ``length * 2 + 1`` and the data is
|
||||
stored as usual in ``keccak256(slot)``. This means that you can distinguish a short array from a long array
|
||||
by checking if the lowest bit is set: short (not set) and long (set).
|
||||
|
||||
.. note::
|
||||
Handling invalidly encoded slots is currently not supported but may be added in the future.
|
||||
|
||||
.. index: memory layout
|
||||
|
||||
****************
|
||||
Layout in Memory
|
||||
****************
|
||||
|
||||
Solidity reserves four 32 byte slots:
|
||||
Solidity reserves four 32-byte slots, with specific byte ranges (inclusive of endpoints) being used as follows:
|
||||
|
||||
- ``0x00`` - ``0x3f``: scratch space for hashing methods
|
||||
- ``0x40`` - ``0x5f``: currently allocated memory size (aka. free memory pointer)
|
||||
- ``0x60`` - ``0x7f``: zero slot
|
||||
- ``0x00`` - ``0x3f`` (64 bytes): scratch space for hashing methods
|
||||
- ``0x40`` - ``0x5f`` (32 bytes): currently allocated memory size (aka. free memory pointer)
|
||||
- ``0x60`` - ``0x7f`` (32 bytes): zero slot
|
||||
|
||||
Scratch space can be used between statements (ie. within inline assembly). The zero slot
|
||||
Scratch space can be used between statements (i.e. within inline assembly). The zero slot
|
||||
is used as initial value for dynamic memory arrays and should never be written to
|
||||
(the free memory pointer points to ``0x80`` initially).
|
||||
|
||||
Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future).
|
||||
|
||||
.. warning::
|
||||
There are some operations in Solidity that need a temporary memory area larger than 64 bytes and therefore will not fit into the scratch space. They will be placed where the free memory points to, but given their short lifecycle, the pointer is not updated. The memory may or may not be zeroed out. Because of this, one shouldn't expect the free memory to be zeroed out.
|
||||
There are some operations in Solidity that need a temporary memory area larger than 64 bytes and therefore will not fit into the scratch space. They will be placed where the free memory points to, but given their short lifetime, the pointer is not updated. The memory may or may not be zeroed out. Because of this, one shouldn't expect the free memory to point to zeroed out memory.
|
||||
|
||||
While it may seem like a good idea to use ``msize`` to arrive at a definitely zeroed out memory area, using such a pointer non-temporarily without updating the free memory pointer can have adverse results.
|
||||
|
||||
.. index: calldata layout
|
||||
|
||||
@ -86,10 +112,14 @@ Solidity always places new objects at the free memory pointer and memory is neve
|
||||
Layout of Call Data
|
||||
*******************
|
||||
|
||||
When a Solidity contract is deployed and when it is called from an
|
||||
account, the input data is assumed to be in the format in :ref:`the ABI
|
||||
specification <ABI>`. The ABI specification requires arguments to be padded to multiples of 32
|
||||
bytes. The internal function calls use a different convention.
|
||||
The input data for a function call is assumed to be in the format defined by the :ref:`ABI
|
||||
specification <ABI>`. Among others, the ABI specification requires arguments to be padded to multiples of 32
|
||||
bytes. The internal function calls use a different convention.
|
||||
|
||||
Arguments for the constructor of a contract are directly appended at the end of the
|
||||
contract's code, also in ABI encoding. The constructor will access them through a hard-coded offset, and
|
||||
not by using the ``codesize`` opcode, since this of course changes when appending
|
||||
data to the code.
|
||||
|
||||
|
||||
.. index: variable cleanup
|
||||
@ -143,35 +173,34 @@ Different types have different rules for cleaning up invalid values:
|
||||
.. index:: optimizer, common subexpression elimination, constant propagation
|
||||
|
||||
*************************
|
||||
Internals - The Optimizer
|
||||
Internals - The Optimiser
|
||||
*************************
|
||||
|
||||
The Solidity optimizer operates on assembly, so it can be and also is used by other languages. It splits the sequence of instructions into basic blocks at ``JUMPs`` and ``JUMPDESTs``. Inside these blocks, the instructions are analysed and every modification to the stack, to memory or storage is recorded as an expression which consists of an instruction and a list of arguments which are essentially pointers to other expressions. The main idea is now to find expressions that are always equal (on every input) and combine them into an expression class. The optimizer first tries to find each new expression in a list of already known expressions. If this does not work, the expression is simplified according to rules like ``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is done recursively, we can also apply the latter rule if the second factor is a more complex expression where we know that it will always evaluate 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 actually do not know what is stored at x after we wrote to y. On the other hand, if a 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.
|
||||
The Solidity optimiser operates on assembly so that other languages can use it. 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.
|
||||
|
||||
At the end of this process, we know which expressions have to be on the stack in 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 a ``JUMPI`` is found whose condition evaluates to a constant, it is transformed to an unconditional jump.
|
||||
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 completely re-generated. A dependency graph is created from the expressions on the stack at the end of the block and every operation that is not part of this graph is essentially dropped. Now code is generated 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) and finally, generates all values that are required to be on the stack in the correct place.
|
||||
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, and thus code like
|
||||
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
|
||||
|
||||
::
|
||||
|
||||
var x = 7;
|
||||
uint x = 7;
|
||||
data[7] = 9;
|
||||
if (data[x] != x + 2)
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
|
||||
is simplified to code which can also be compiled from
|
||||
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;
|
||||
|
||||
even though the instructions contained a jump in the beginning.
|
||||
|
||||
.. index:: source mappings
|
||||
|
||||
***************
|
||||
@ -190,7 +219,7 @@ important for static analysis tools that operate on bytecode level and
|
||||
for displaying the current position in the source code inside a debugger
|
||||
or for breakpoint handling.
|
||||
|
||||
Both kinds of source mappings use integer indentifiers to refer to source files.
|
||||
Both kinds of source mappings use integer identifiers to refer to source files.
|
||||
These are regular array indices into a list of source files usually called
|
||||
``"sourceList"``, which is part of the combined-json and the output of
|
||||
the json / npm compiler.
|
||||
@ -237,7 +266,6 @@ Tips and Tricks
|
||||
* Use shorter types for struct elements and sort them such that short types are grouped together. This can lower the gas costs as multiple ``SSTORE`` operations might be combined into a single (``SSTORE`` costs 5000 or 20000 gas, so this is what you want to optimise). Use the gas price estimator (with optimiser enabled) to check!
|
||||
* Make your state variables public - the compiler will create :ref:`getters <visibility-and-getters>` for you automatically.
|
||||
* If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`.
|
||||
* If your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``.
|
||||
* Initialize storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});``
|
||||
|
||||
.. note::
|
||||
@ -273,7 +301,7 @@ The following is the order of precedence for operators, listed in order of evalu
|
||||
+------------+-------------------------------------+--------------------------------------------+
|
||||
| *2* | Prefix increment and decrement | ``++``, ``--`` |
|
||||
+ +-------------------------------------+--------------------------------------------+
|
||||
| | Unary plus and minus | ``+``, ``-`` |
|
||||
| | Unary minus | ``-`` |
|
||||
+ +-------------------------------------+--------------------------------------------+
|
||||
| | Unary operations | ``delete`` |
|
||||
+ +-------------------------------------+--------------------------------------------+
|
||||
@ -317,45 +345,42 @@ The following is the order of precedence for operators, listed in order of evalu
|
||||
Global Variables
|
||||
================
|
||||
|
||||
- ``abi.encode(...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments
|
||||
- ``abi.encodePacked(...) returns (bytes)``: Performes :ref:`packed encoding <abi_packed_mode>` of the given arguments
|
||||
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments
|
||||
- ``abi.decode(bytes memory encodedData, (...)) returns (...)``: :ref:`ABI <ABI>`-decodes the provided data. The types are given in parentheses as second argument. Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))``
|
||||
- ``abi.encode(...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes the given arguments
|
||||
- ``abi.encodePacked(...) returns (bytes memory)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments
|
||||
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes the given arguments
|
||||
starting from the second and prepends the given four-byte selector
|
||||
- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(signature), ...)```
|
||||
- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``.
|
||||
- ``block.coinbase`` (``address``): current block miner's address
|
||||
- ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)```
|
||||
- ``block.coinbase`` (``address payable``): current block miner's address
|
||||
- ``block.difficulty`` (``uint``): current block difficulty
|
||||
- ``block.gaslimit`` (``uint``): current block gaslimit
|
||||
- ``block.number`` (``uint``): current block number
|
||||
- ``block.timestamp`` (``uint``): current block timestamp
|
||||
- ``gasleft() returns (uint256)``: remaining gas
|
||||
- ``msg.data`` (``bytes``): complete calldata
|
||||
- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()``
|
||||
- ``msg.sender`` (``address``): sender of the message (current call)
|
||||
- ``msg.sender`` (``address payable``): sender of the message (current call)
|
||||
- ``msg.value`` (``uint``): number of wei sent with the message
|
||||
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
|
||||
- ``tx.gasprice`` (``uint``): gas price of the transaction
|
||||
- ``tx.origin`` (``address``): sender of the transaction (full call chain)
|
||||
- ``tx.origin`` (``address payable``): sender of the transaction (full call chain)
|
||||
- ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error)
|
||||
- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component)
|
||||
- ``require(bool condition, string message)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component). Also provide error message.
|
||||
- ``require(bool condition, string memory message)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component). Also provide error message.
|
||||
- ``revert()``: abort execution and revert state changes
|
||||
- ``revert(string message)``: abort execution and revert state changes providing an explanatory string
|
||||
- ``revert(string memory message)``: abort execution and revert state changes providing an explanatory string
|
||||
- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks
|
||||
- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
||||
- ``sha3(...) returns (bytes32)``: an alias to ``keccak256``
|
||||
- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
||||
- ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
||||
- ``keccak256(bytes memory) returns (bytes32)``: compute the Keccak-256 hash of the input
|
||||
- ``sha256(bytes memory) returns (bytes32)``: compute the SHA-256 hash of the input
|
||||
- ``ripemd160(bytes memory) returns (bytes20)``: compute the RIPEMD-160 hash of the input
|
||||
- ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error
|
||||
- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
||||
- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
||||
- ``this`` (current contract's type): the current contract, explicitly convertible to ``address``
|
||||
- ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` or ``address payable``
|
||||
- ``super``: the contract one level higher in the inheritance hierarchy
|
||||
- ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address
|
||||
- ``suicide(address recipient)``: a deprecated alias to ``selfdestruct``
|
||||
- ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address
|
||||
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
|
||||
- ``<address>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure
|
||||
- ``<address>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
|
||||
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure
|
||||
- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
|
||||
|
||||
.. note::
|
||||
Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness,
|
||||
@ -374,6 +399,11 @@ Global Variables
|
||||
You can only access the hashes of the most recent 256 blocks, all other
|
||||
values will be zero.
|
||||
|
||||
.. note::
|
||||
In version 0.5.0, the following aliases were removed: ``suicide`` as alias for ``selfdestruct``,
|
||||
``msg.gas`` as alias for ``gasleft``, ``block.blockhash`` as alias for ``blockhash`` and
|
||||
``sha3`` as alias for ``keccak256``.
|
||||
|
||||
.. index:: visibility, public, private, external, internal
|
||||
|
||||
Function Visibility Specifiers
|
||||
@ -396,11 +426,10 @@ Function Visibility Specifiers
|
||||
Modifiers
|
||||
=========
|
||||
|
||||
- ``pure`` for functions: Disallows modification or access of state - this is not enforced yet.
|
||||
- ``view`` for functions: Disallows modification of state - this is not enforced yet.
|
||||
- ``pure`` for functions: Disallows modification or access of state.
|
||||
- ``view`` for functions: Disallows modification of state.
|
||||
- ``payable`` for functions: Allows them to receive Ether together with a call.
|
||||
- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
|
||||
- ``constant`` for functions: Same as ``view``.
|
||||
- ``anonymous`` for events: Does not store event signature as topic.
|
||||
- ``indexed`` for event parameters: Stores the parameter as topic.
|
||||
|
||||
@ -409,8 +438,11 @@ Reserved Keywords
|
||||
|
||||
These keywords are reserved in Solidity. They might become part of the syntax in the future:
|
||||
|
||||
``abstract``, ``after``, ``case``, ``catch``, ``default``, ``final``, ``in``, ``inline``, ``let``, ``match``, ``null``,
|
||||
``of``, ``relocatable``, ``static``, ``switch``, ``try``, ``type``, ``typeof``.
|
||||
``abstract``, ``after``, ``alias``, ``apply``, ``auto``, ``case``, ``catch``, ``copyof``, ``default``,
|
||||
``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
|
||||
``mutable``, ``null``, ``of``, ``override``, ``partial``, ``promise``, ``reference``, ``relocatable``,
|
||||
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``type``, ``typedef``, ``typeof``,
|
||||
``unchecked``.
|
||||
|
||||
Language Grammar
|
||||
================
|
||||
|
@ -1 +1,2 @@
|
||||
sphinx_rtd_theme>=0.3.1
|
||||
pygments-lexer-solidity>=0.3.1
|
||||
|
132
docs/resources.rst
Normal file
132
docs/resources.rst
Normal file
@ -0,0 +1,132 @@
|
||||
Resources
|
||||
---------
|
||||
|
||||
General
|
||||
~~~~~~~
|
||||
|
||||
* `Ethereum <https://ethereum.org>`_
|
||||
|
||||
* `Changelog <https://github.com/ethereum/solidity/blob/develop/Changelog.md>`_
|
||||
|
||||
* `Source Code <https://github.com/ethereum/solidity/>`_
|
||||
|
||||
* `Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_
|
||||
|
||||
* `Language Users Chat <https://gitter.im/ethereum/solidity/>`_
|
||||
|
||||
* `Compiler Developers Chat <https://gitter.im/ethereum/solidity-dev/>`_
|
||||
|
||||
Solidity Integrations
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Generic:
|
||||
|
||||
|
||||
* `EthFiddle <https://ethfiddle.com/>`_
|
||||
Solidity IDE in the Browser. Write and share your solidity code. Uses server-side components.
|
||||
|
||||
* `Remix <https://remix.ethereum.org/>`_
|
||||
Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components.
|
||||
|
||||
* `Solium <https://github.com/duaraghav8/Solium/>`_
|
||||
Linter to identify and fix style and security issues in Solidity.
|
||||
|
||||
* `Solhint <https://github.com/protofire/solhint>`_
|
||||
Solidity linter that provides security, style guide and best practice rules for smart contract validation.
|
||||
|
||||
* Atom:
|
||||
|
||||
* `Etheratom <https://github.com/0mkara/etheratom>`_
|
||||
Plugin for the Atom editor that features syntax highlighting, compilation and a runtime environment (Backend node & VM compatible).
|
||||
|
||||
* `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_
|
||||
Plugin for the Atom editor that provides Solidity linting.
|
||||
|
||||
* `Atom Solium Linter <https://atom.io/packages/linter-solium>`_
|
||||
Configurable Solidty linter for Atom using Solium as a base.
|
||||
|
||||
* Eclipse:
|
||||
|
||||
* `YAKINDU Solidity Tools <https://yakindu.github.io/solidity-ide/>`_
|
||||
Eclipse based IDE. Features context sensitive code completion and help, code navigation, syntax coloring, built in compiler, quick fixes and templates.
|
||||
|
||||
* Emacs:
|
||||
|
||||
* `Emacs Solidity <https://github.com/ethereum/emacs-solidity/>`_
|
||||
Plugin for the Emacs editor providing syntax highlighting and compilation error reporting.
|
||||
|
||||
* IntelliJ:
|
||||
|
||||
* `IntelliJ IDEA plugin <https://plugins.jetbrains.com/plugin/9475-intellij-solidity>`_
|
||||
Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs)
|
||||
|
||||
* Sublime:
|
||||
|
||||
* `Package for SublimeText - Solidity language syntax <https://packagecontrol.io/packages/Ethereum/>`_
|
||||
Solidity syntax highlighting for SublimeText editor.
|
||||
|
||||
* Vim:
|
||||
|
||||
* `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_
|
||||
Plugin for the Vim editor providing syntax highlighting.
|
||||
|
||||
* `Vim Syntastic <https://github.com/scrooloose/syntastic>`_
|
||||
Plugin for the Vim editor providing compile checking.
|
||||
|
||||
* Visual Studio Code:
|
||||
|
||||
* `Visual Studio Code extension <http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_
|
||||
Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler.
|
||||
|
||||
Discontinued:
|
||||
|
||||
* `Mix IDE <https://github.com/ethereum/mix/>`_
|
||||
Qt based IDE for designing, debugging and testing solidity smart contracts.
|
||||
|
||||
* `Ethereum Studio <https://live.ether.camp/>`_
|
||||
Specialized web IDE that also provides shell access to a complete Ethereum environment.
|
||||
|
||||
* `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_
|
||||
Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler.
|
||||
|
||||
Solidity Tools
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
* `Dapp <https://dapp.tools/dapp/>`_
|
||||
Build tool, package manager, and deployment assistant for Solidity.
|
||||
|
||||
* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_
|
||||
Try Solidity instantly with a command-line Solidity console.
|
||||
|
||||
* `solgraph <https://github.com/raineorshine/solgraph>`_
|
||||
Visualize Solidity control flow and highlight potential security vulnerabilities.
|
||||
|
||||
* `Doxity <https://github.com/DigixGlobal/doxity>`_
|
||||
Documentation Generator for Solidity.
|
||||
|
||||
* `evmdis <https://github.com/Arachnid/evmdis>`_
|
||||
EVM Disassembler that performs static analysis on the bytecode to provide a higher level of abstraction than raw EVM operations.
|
||||
|
||||
* `ABI to solidity interface converter <https://gist.github.com/chriseth/8f533d133fa0c15b0d6eaf3ec502c82b>`_
|
||||
A script for generating contract interfaces from the ABI of a smart contract.
|
||||
|
||||
* `Securify <https://securify.ch/>`_
|
||||
Fully automated online static analyzer for smart contracts, providing a security report based on vulnerability patterns.
|
||||
|
||||
* `Sūrya <https://github.com/ConsenSys/surya/>`_
|
||||
Utility tool for smart contract systems, offering a number of visual outputs and information about the contracts' structure. Also supports querying the function call graph.
|
||||
|
||||
* `EVM Lab <https://github.com/ethereum/evmlab/>`_
|
||||
Rich tool package to interact with the EVM. Includes a VM, Etherchain API, and a trace-viewer with gas cost display.
|
||||
|
||||
.. note::
|
||||
Information like variable names, comments, and source code formatting is lost in the compilation process and it is not possible to completely recover the original source code. Decompiling smart contracts to view the original source code might not be possible, or the end result that useful.
|
||||
|
||||
Third-Party Solidity Parsers and Grammars
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* `solidity-parser <https://github.com/ConsenSys/solidity-parser>`_
|
||||
Solidity parser for JavaScript
|
||||
|
||||
* `Solidity Grammar for ANTLR 4 <https://github.com/federicobond/solidity-antlr4>`_
|
||||
Solidity grammar for the ANTLR 4 parser generator
|
@ -55,7 +55,7 @@ complete contract):
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||
contract Fund {
|
||||
@ -78,7 +78,7 @@ as it uses ``call`` which forwards all remaining gas by default:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||
contract Fund {
|
||||
@ -86,7 +86,8 @@ as it uses ``call`` which forwards all remaining gas by default:
|
||||
mapping(address => uint) shares;
|
||||
/// Withdraw your share.
|
||||
function withdraw() public {
|
||||
if (msg.sender.call.value(shares[msg.sender])())
|
||||
(bool success,) = msg.sender.call.value(shares[msg.sender])("");
|
||||
if (success)
|
||||
shares[msg.sender] = 0;
|
||||
}
|
||||
}
|
||||
@ -96,14 +97,14 @@ outlined further below:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.11;
|
||||
pragma solidity >=0.4.11 <0.6.0;
|
||||
|
||||
contract Fund {
|
||||
/// Mapping of ether shares of the contract.
|
||||
mapping(address => uint) shares;
|
||||
/// Withdraw your share.
|
||||
function withdraw() public {
|
||||
var share = shares[msg.sender];
|
||||
uint share = shares[msg.sender];
|
||||
shares[msg.sender] = 0;
|
||||
msg.sender.transfer(share);
|
||||
}
|
||||
@ -135,15 +136,16 @@ Sending and Receiving Ether
|
||||
- If a contract receives Ether (without a function being called), the fallback function is executed.
|
||||
If it does not have a fallback function, the Ether will be rejected (by throwing an exception).
|
||||
During the execution of the fallback function, the contract can only rely
|
||||
on the "gas stipend" (2300 gas) being available to it at that time. This stipend is not enough to access storage in any way.
|
||||
on the "gas stipend" it is passed (2300 gas) being available to it at that time. This stipend is not enough to modify storage
|
||||
(do not take this for granted though, the stipend might change with future hard forks).
|
||||
To be sure that your contract can receive Ether in that way, check the gas requirements of the fallback function
|
||||
(for example in the "details" section in Remix).
|
||||
|
||||
- There is a way to forward more gas to the receiving contract using
|
||||
``addr.call.value(x)()``. This is essentially the same as ``addr.transfer(x)``,
|
||||
``addr.call.value(x)("")``. This is essentially the same as ``addr.transfer(x)``,
|
||||
only that it forwards all remaining gas and opens up the ability for the
|
||||
recipient to perform more expensive actions (and it only returns a failure code
|
||||
and does not automatically propagate the error). This might include calling back
|
||||
recipient to perform more expensive actions (and it returns a failure code
|
||||
instead of automatically propagating the error). This might include calling back
|
||||
into the sending contract or other state changes you might not have thought of.
|
||||
So it allows for great flexibility for honest users but also for malicious actors.
|
||||
|
||||
@ -171,7 +173,8 @@ before they interact with your contract.
|
||||
|
||||
Note that ``.send()`` does **not** throw an exception if the call stack is
|
||||
depleted but rather returns ``false`` in that case. The low-level functions
|
||||
``.call()``, ``.callcode()`` and ``.delegatecall()`` behave in the same way.
|
||||
``.call()``, ``.callcode()``, ``.delegatecall()`` and ``.staticcall()`` behave
|
||||
in the same way.
|
||||
|
||||
tx.origin
|
||||
=========
|
||||
@ -180,17 +183,17 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.11;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||
contract TxUserWallet {
|
||||
address owner;
|
||||
|
||||
function TxUserWallet() public {
|
||||
constructor() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function transferTo(address dest, uint amount) public {
|
||||
function transferTo(address payable dest, uint amount) public {
|
||||
require(tx.origin == owner);
|
||||
dest.transfer(amount);
|
||||
}
|
||||
@ -200,20 +203,20 @@ Now someone tricks you into sending ether to the address of this attack wallet:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.11;
|
||||
pragma solidity >0.4.99 <0.6.0;
|
||||
|
||||
interface TxUserWallet {
|
||||
function transferTo(address dest, uint amount) public;
|
||||
function transferTo(address payable dest, uint amount) external;
|
||||
}
|
||||
|
||||
contract TxAttackWallet {
|
||||
address owner;
|
||||
address payable owner;
|
||||
|
||||
function TxAttackWallet() public {
|
||||
constructor() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function() public {
|
||||
function() external {
|
||||
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
|
||||
}
|
||||
}
|
||||
@ -221,10 +224,29 @@ Now someone tricks you into sending ether to the address of this attack wallet:
|
||||
If your wallet had checked ``msg.sender`` for authorization, it would get the address of the attack wallet, instead of the owner address. But by checking ``tx.origin``, it gets the original address that kicked off the transaction, which is still the owner address. The attack wallet instantly drains all your funds.
|
||||
|
||||
|
||||
|
||||
Two's Complement / Underflows / Overflows
|
||||
=========================================
|
||||
|
||||
As in many programming languages, Solidity's integer types are not actually integers.
|
||||
They resemble integers when the values are small, but behave differently if the numbers are larger.
|
||||
For example, the following is true: ``uint8(255) + uint8(1) == 0``. This situation is called
|
||||
an *overflow*. It occurs when an operation is performed that requires a fixed size variable
|
||||
to store a number (or piece of data) that is outside the range of the variable's data type.
|
||||
An *underflow* is the converse situation: ``uint8(0) - uint8(1) == 255``.
|
||||
|
||||
In general, read about the limits of two's complement representation, which even has some
|
||||
more special edge cases for signed numbers.
|
||||
|
||||
Try to use ``require`` to limit the size of inputs to a reasonable range and use the
|
||||
:ref:`SMT checker<smt_checker>` to find potential overflows, or
|
||||
use a library like
|
||||
`SafeMath<https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol>`
|
||||
if you want all overflows to cause a revert.
|
||||
|
||||
Minor Details
|
||||
=============
|
||||
|
||||
- In ``for (var i = 0; i < arrayName.length; i++) { ... }``, the type of ``i`` will be ``uint8``, because this is the smallest type that is required to hold the value ``0``. If the array has more than 255 elements, the loop will not terminate.
|
||||
- Types that do not occupy the full 32 bytes might contain "dirty higher order bits".
|
||||
This is especially important if you access ``msg.data`` - it poses a malleability risk:
|
||||
You can craft transactions that call a function ``f(uint8 x)`` with a raw byte argument
|
||||
@ -245,12 +267,8 @@ implications, there might be another issue buried beneath it.
|
||||
Any compiler warning we issue can be silenced by slight changes to the
|
||||
code.
|
||||
|
||||
Also try to enable the "0.5.0" safety features as early as possible
|
||||
by adding ``pragma experimental "v0.5.0";``. Note that in this case,
|
||||
the word ``experimental`` does not mean that the safety features are in any
|
||||
way risky, it is just a way to enable some features that are
|
||||
not yet part of the latest version of Solidity due to backwards
|
||||
compatibility.
|
||||
Always use the latest version of the compiler to be notified about all recently
|
||||
introduced warnings.
|
||||
|
||||
Restrict the Amount of Ether
|
||||
============================
|
||||
@ -304,6 +322,12 @@ of "failsafe" mode, which, for example, disables most of the features, hands ove
|
||||
control to a fixed and trusted third party or just converts the contract into
|
||||
a simple "give me back my money" contract.
|
||||
|
||||
Ask for Peer Review
|
||||
===================
|
||||
|
||||
The more people examine a piece of code, the more issues are found.
|
||||
Asking people to review your code also helps as a cross-check to find out whether your code
|
||||
is easy to understand - a very important criterion for good smart contracts.
|
||||
|
||||
*******************
|
||||
Formal Verification
|
||||
|
@ -36,7 +36,7 @@ of votes.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.22;
|
||||
pragma solidity >=0.4.22 <0.6.0;
|
||||
|
||||
/// @title Voting with delegation.
|
||||
contract Ballot {
|
||||
@ -66,7 +66,7 @@ of votes.
|
||||
Proposal[] public proposals;
|
||||
|
||||
/// Create a new ballot to choose one of `proposalNames`.
|
||||
constructor(bytes32[] proposalNames) public {
|
||||
constructor(bytes32[] memory proposalNames) public {
|
||||
chairperson = msg.sender;
|
||||
voters[chairperson].weight = 1;
|
||||
|
||||
@ -90,7 +90,7 @@ of votes.
|
||||
// If the first argument of `require` evaluates
|
||||
// to `false`, execution terminates and all
|
||||
// changes to the state and to Ether balances
|
||||
// are reverted.
|
||||
// are reverted.
|
||||
// This used to consume all gas in old EVM versions, but
|
||||
// not anymore.
|
||||
// It is often a good idea to use `require` to check if
|
||||
@ -152,6 +152,7 @@ of votes.
|
||||
/// to proposal `proposals[proposal].name`.
|
||||
function vote(uint proposal) public {
|
||||
Voter storage sender = voters[msg.sender];
|
||||
require(sender.weight != 0, "Has no right to vote");
|
||||
require(!sender.voted, "Already voted.");
|
||||
sender.voted = true;
|
||||
sender.vote = proposal;
|
||||
@ -220,19 +221,19 @@ bid. If the highest bid is raised, the previously
|
||||
highest bidder gets her money back.
|
||||
After the end of the bidding period, the
|
||||
contract has to be called manually for the
|
||||
beneficiary to receive his money - contracts cannot
|
||||
beneficiary to receive their money - contracts cannot
|
||||
activate themselves.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.22;
|
||||
pragma solidity >=0.4.22 <0.6.0;
|
||||
|
||||
contract SimpleAuction {
|
||||
// Parameters of the auction. Times are either
|
||||
// absolute unix timestamps (seconds since 1970-01-01)
|
||||
// or time periods in seconds.
|
||||
address public beneficiary;
|
||||
uint public auctionEnd;
|
||||
address payable public beneficiary;
|
||||
uint public auctionEndTime;
|
||||
|
||||
// Current state of the auction.
|
||||
address public highestBidder;
|
||||
@ -241,10 +242,11 @@ activate themselves.
|
||||
// Allowed withdrawals of previous bids
|
||||
mapping(address => uint) pendingReturns;
|
||||
|
||||
// Set to true at the end, disallows any change
|
||||
// Set to true at the end, disallows any change.
|
||||
// By default initialized to `false`.
|
||||
bool ended;
|
||||
|
||||
// Events that will be fired on changes.
|
||||
// Events that will be emitted on changes.
|
||||
event HighestBidIncreased(address bidder, uint amount);
|
||||
event AuctionEnded(address winner, uint amount);
|
||||
|
||||
@ -258,10 +260,10 @@ activate themselves.
|
||||
/// beneficiary address `_beneficiary`.
|
||||
constructor(
|
||||
uint _biddingTime,
|
||||
address _beneficiary
|
||||
address payable _beneficiary
|
||||
) public {
|
||||
beneficiary = _beneficiary;
|
||||
auctionEnd = now + _biddingTime;
|
||||
auctionEndTime = now + _biddingTime;
|
||||
}
|
||||
|
||||
/// Bid on the auction with the value sent
|
||||
@ -278,7 +280,7 @@ activate themselves.
|
||||
// Revert the call if the bidding
|
||||
// period is over.
|
||||
require(
|
||||
now <= auctionEnd,
|
||||
now <= auctionEndTime,
|
||||
"Auction already ended."
|
||||
);
|
||||
|
||||
@ -337,7 +339,7 @@ activate themselves.
|
||||
// external contracts.
|
||||
|
||||
// 1. Conditions
|
||||
require(now >= auctionEnd, "Auction not yet ended.");
|
||||
require(now >= auctionEndTime, "Auction not yet ended.");
|
||||
require(!ended, "auctionEnd has already been called.");
|
||||
|
||||
// 2. Effects
|
||||
@ -372,7 +374,7 @@ is the same as the one provided during the bidding period.
|
||||
Another challenge is how to make the auction
|
||||
**binding and blind** at the same time: The only way to
|
||||
prevent the bidder from just not sending the money
|
||||
after he won the auction is to make her send it
|
||||
after they won the auction is to make her send it
|
||||
together with the bid. Since value transfers cannot
|
||||
be blinded in Ethereum, anyone can see the value.
|
||||
|
||||
@ -388,7 +390,7 @@ high or low invalid bids.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >0.4.23 <0.5.0;
|
||||
pragma solidity >0.4.23 <0.6.0;
|
||||
|
||||
contract BlindAuction {
|
||||
struct Bid {
|
||||
@ -396,7 +398,7 @@ high or low invalid bids.
|
||||
uint deposit;
|
||||
}
|
||||
|
||||
address public beneficiary;
|
||||
address payable public beneficiary;
|
||||
uint public biddingEnd;
|
||||
uint public revealEnd;
|
||||
bool public ended;
|
||||
@ -421,15 +423,15 @@ high or low invalid bids.
|
||||
constructor(
|
||||
uint _biddingTime,
|
||||
uint _revealTime,
|
||||
address _beneficiary
|
||||
address payable _beneficiary
|
||||
) public {
|
||||
beneficiary = _beneficiary;
|
||||
biddingEnd = now + _biddingTime;
|
||||
revealEnd = biddingEnd + _revealTime;
|
||||
}
|
||||
|
||||
/// Place a blinded bid with `_blindedBid` = keccak256(value,
|
||||
/// fake, secret).
|
||||
/// Place a blinded bid with `_blindedBid` =
|
||||
/// keccak256(abi.encodePacked(value, fake, secret)).
|
||||
/// The sent ether is only refunded if the bid is correctly
|
||||
/// revealed in the revealing phase. The bid is valid if the
|
||||
/// ether sent together with the bid is at least "value" and
|
||||
@ -452,9 +454,9 @@ high or low invalid bids.
|
||||
/// correctly blinded invalid bids and for all bids except for
|
||||
/// the totally highest.
|
||||
function reveal(
|
||||
uint[] _values,
|
||||
bool[] _fake,
|
||||
bytes32[] _secret
|
||||
uint[] memory _values,
|
||||
bool[] memory _fake,
|
||||
bytes32[] memory _secret
|
||||
)
|
||||
public
|
||||
onlyAfter(biddingEnd)
|
||||
@ -467,22 +469,22 @@ high or low invalid bids.
|
||||
|
||||
uint refund;
|
||||
for (uint i = 0; i < length; i++) {
|
||||
Bid storage bid = bids[msg.sender][i];
|
||||
Bid storage bidToCheck = bids[msg.sender][i];
|
||||
(uint value, bool fake, bytes32 secret) =
|
||||
(_values[i], _fake[i], _secret[i]);
|
||||
if (bid.blindedBid != keccak256(value, fake, secret)) {
|
||||
if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) {
|
||||
// Bid was not actually revealed.
|
||||
// Do not refund deposit.
|
||||
continue;
|
||||
}
|
||||
refund += bid.deposit;
|
||||
if (!fake && bid.deposit >= value) {
|
||||
refund += bidToCheck.deposit;
|
||||
if (!fake && bidToCheck.deposit >= value) {
|
||||
if (placeBid(msg.sender, value))
|
||||
refund -= value;
|
||||
}
|
||||
// Make it impossible for the sender to re-claim
|
||||
// the same deposit.
|
||||
bid.blindedBid = bytes32(0);
|
||||
bidToCheck.blindedBid = bytes32(0);
|
||||
}
|
||||
msg.sender.transfer(refund);
|
||||
}
|
||||
@ -496,7 +498,7 @@ high or low invalid bids.
|
||||
if (value <= highestBid) {
|
||||
return false;
|
||||
}
|
||||
if (highestBidder != 0) {
|
||||
if (highestBidder != address(0)) {
|
||||
// Refund the previously highest bidder.
|
||||
pendingReturns[highestBidder] += highestBid;
|
||||
}
|
||||
@ -541,12 +543,12 @@ Safe Remote Purchase
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.22;
|
||||
pragma solidity >=0.4.22 <0.6.0;
|
||||
|
||||
contract Purchase {
|
||||
uint public value;
|
||||
address public seller;
|
||||
address public buyer;
|
||||
address payable public seller;
|
||||
address payable public buyer;
|
||||
enum State { Created, Locked, Inactive }
|
||||
State public state;
|
||||
|
||||
@ -645,4 +647,489 @@ Safe Remote Purchase
|
||||
Micropayment Channel
|
||||
********************
|
||||
|
||||
To be written.
|
||||
In this section we will learn how to build a simple implementation
|
||||
of a payment channel. It use cryptographics signatures to make
|
||||
repeated transfers of Ether between the same parties secure, instantaneous, and
|
||||
without transaction fees. To do it we need to understand how to
|
||||
sign and verify signatures, and setup the payment channel.
|
||||
|
||||
Creating and verifying signatures
|
||||
=================================
|
||||
|
||||
Imagine Alice wants to send a quantity of Ether to Bob, i.e.
|
||||
Alice is the sender and the Bob is the recipient.
|
||||
Alice only needs to send cryptographically signed messages off-chain
|
||||
(e.g. via email) to Bob and it will be very similar to writing checks.
|
||||
|
||||
Signatures are used to authorize transactions,
|
||||
and they are a general tool that is available to
|
||||
smart contracts. Alice will build a simple
|
||||
smart contract that lets her transmit Ether, but
|
||||
in a unusual way, instead of calling a function herself
|
||||
to initiate a payment, she will let Bob
|
||||
do that, and therefore pay the transaction fee.
|
||||
The contract will work as follows:
|
||||
|
||||
1. Alice deploys the ``ReceiverPays`` contract, attaching enough Ether to cover the payments that will be made.
|
||||
2. Alice authorizes a payment by signing a message with their private key.
|
||||
3. Alice sends the cryptographically signed message to Bob. The message does not need to be kept secret
|
||||
(you will understand it later), and the mechanism for sending it does not matter.
|
||||
4. Bob claims their payment by presenting the signed message to the smart contract, it verifies the
|
||||
authenticity of the message and then releases the funds.
|
||||
|
||||
Creating the signature
|
||||
----------------------
|
||||
|
||||
Alice does not need to interact with Ethereum network to
|
||||
sign the transaction, the process is completely offline.
|
||||
In this tutorial, we will sign messages in the browser
|
||||
using ``web3.js`` and ``MetaMask``.
|
||||
In particular, we will use the standard way described in `EIP-762 <https://github.com/ethereum/EIPs/pull/712>`_,
|
||||
as it provides a number of other security benefits.
|
||||
|
||||
::
|
||||
|
||||
/// Hashing first makes a few things easier
|
||||
var hash = web3.sha3("message to sign");
|
||||
web3.personal.sign(hash, web3.eth.defaultAccount, function () {...});
|
||||
|
||||
|
||||
Note that the ``web3.personal.sign`` prepends the length of the message to the signed data.
|
||||
Since we hash first, the message will always be exactly 32 bytes long,
|
||||
and thus this length prefix is always the same, making everything easier.
|
||||
|
||||
What to Sign
|
||||
------------
|
||||
|
||||
For a contract that fulfills payments, the signed message must include:
|
||||
|
||||
1. The recipient's address
|
||||
2. The amount to be transferred
|
||||
3. Protection against replay attacks
|
||||
|
||||
A replay attack is when a signed message is reused to claim authorization for
|
||||
a second action.
|
||||
To avoid replay attacks we will use the same as in Ethereum transactions
|
||||
themselves, a so-called nonce, which is the number of transactions sent by an
|
||||
account.
|
||||
The smart contract will check if a nonce is used multiple times.
|
||||
|
||||
There is another type of replay attacks, it occurs when the
|
||||
owner deploys a ``ReceiverPays`` smart contract, performs some payments,
|
||||
and then destroy the contract. Later, she decides to deploy the
|
||||
``RecipientPays`` smart contract again, but the new contract does not
|
||||
know the nonces used in the previous deployment, so the attacker
|
||||
can use the old messages again.
|
||||
|
||||
Alice can protect against it including
|
||||
the contract's address in the message, and only
|
||||
messages containing contract's address itself will be accepted.
|
||||
This functionality can be found in the first two lines of the ``claimPayment()`` function in the full contract
|
||||
at the end of this chapter.
|
||||
|
||||
Packing arguments
|
||||
-----------------
|
||||
|
||||
Now that we have identified what information to include in the
|
||||
signed message, we are ready to put the message together, hash it,
|
||||
and sign it. For simplicity, we just concatenate the data.
|
||||
The
|
||||
`ethereumjs-abi <https://github.com/ethereumjs/ethereumjs-abi>`_ library provides
|
||||
a function called ``soliditySHA3`` that mimics the behavior
|
||||
of Solidity's ``keccak256`` function applied to arguments encoded
|
||||
using ``abi.encodePacked``.
|
||||
Putting it all together, here is a JavaScript function that
|
||||
creates the proper signature for the ``ReceiverPays`` example:
|
||||
|
||||
::
|
||||
|
||||
// recipient is the address that should be paid.
|
||||
// amount, in wei, specifies how much ether should be sent.
|
||||
// nonce can be any unique number to prevent replay attacks
|
||||
// contractAddress is used to prevent cross-contract replay attacks
|
||||
function signPayment(recipient, amount, nonce, contractAddress, callback) {
|
||||
var hash = "0x" + ethereumjs.ABI.soliditySHA3(
|
||||
["address", "uint256", "uint256", "address"],
|
||||
[recipient, amount, nonce, contractAddress]
|
||||
).toString("hex");
|
||||
|
||||
web3.personal.sign(hash, web3.eth.defaultAccount, callback);
|
||||
}
|
||||
|
||||
Recovering the Message Signer in Solidity
|
||||
-----------------------------------------
|
||||
|
||||
In general, ECDSA signatures consist of two parameters, ``r`` and ``s``.
|
||||
Signatures in Ethereum include a third parameter called ``v``, that can be used
|
||||
to recover which account's private key was used to sign in the message,
|
||||
the transaction's sender. Solidity provides a built-in function
|
||||
`ecrecover <mathematical-and-cryptographic-functions>`_
|
||||
that accepts a message along with the ``r``, ``s`` and ``v`` parameters and
|
||||
returns the address that was used to sign the message.
|
||||
|
||||
Extracting the Signature Parameters
|
||||
-----------------------------------
|
||||
|
||||
Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``,
|
||||
so the first step is splitting those parameters back out. It can be done on the client,
|
||||
but doing it inside the smart contract means only one signature parameter
|
||||
needs to be sent rather than three.
|
||||
Splitting apart a byte array into component parts is a little messy.
|
||||
We will use `inline assembly <assembly>`_ to do the job
|
||||
in the ``splitSignature`` function (the third function in the full contract
|
||||
at the end of this chapter).
|
||||
|
||||
Computing the Message Hash
|
||||
--------------------------
|
||||
|
||||
The smart contract needs to know exactly what parameters were signed,
|
||||
and so it must recreate the message from the parameters and use that
|
||||
for signature verification. The functions ``prefixed`` and
|
||||
``recoverSigner`` do this and their use can be found in the
|
||||
``claimPayment`` function.
|
||||
|
||||
|
||||
The full contract
|
||||
-----------------
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.24 <0.6.0;
|
||||
|
||||
contract ReceiverPays {
|
||||
address owner = msg.sender;
|
||||
|
||||
mapping(uint256 => bool) usedNonces;
|
||||
|
||||
constructor() public payable {}
|
||||
|
||||
function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public {
|
||||
require(!usedNonces[nonce]);
|
||||
usedNonces[nonce] = true;
|
||||
|
||||
// this recreates the message that was signed on the client
|
||||
bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this)));
|
||||
|
||||
require(recoverSigner(message, signature) == owner);
|
||||
|
||||
msg.sender.transfer(amount);
|
||||
}
|
||||
|
||||
/// destroy the contract and reclaim the leftover funds.
|
||||
function kill() public {
|
||||
require(msg.sender == owner);
|
||||
selfdestruct(msg.sender);
|
||||
}
|
||||
|
||||
/// signature methods.
|
||||
function splitSignature(bytes memory sig)
|
||||
internal
|
||||
pure
|
||||
returns (uint8 v, bytes32 r, bytes32 s)
|
||||
{
|
||||
require(sig.length == 65);
|
||||
|
||||
assembly {
|
||||
// first 32 bytes, after the length prefix.
|
||||
r := mload(add(sig, 32))
|
||||
// second 32 bytes.
|
||||
s := mload(add(sig, 64))
|
||||
// final byte (first byte of the next 32 bytes).
|
||||
v := byte(0, mload(add(sig, 96)))
|
||||
}
|
||||
|
||||
return (v, r, s);
|
||||
}
|
||||
|
||||
function recoverSigner(bytes32 message, bytes memory sig)
|
||||
internal
|
||||
pure
|
||||
returns (address)
|
||||
{
|
||||
(uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);
|
||||
|
||||
return ecrecover(message, v, r, s);
|
||||
}
|
||||
|
||||
/// builds a prefixed hash to mimic the behavior of eth_sign.
|
||||
function prefixed(bytes32 hash) internal pure returns (bytes32) {
|
||||
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Writing a Simple Payment Channel
|
||||
================================
|
||||
|
||||
Alice will now build a simple but complete implementation of a payment channel.
|
||||
Payment channels use cryptographic signatures to make repeated transfers
|
||||
of Ether securely, instantaneously, and without transaction fees.
|
||||
|
||||
What is a Payment Channel?
|
||||
--------------------------
|
||||
|
||||
Payment channels allow participants to make repeated transfers of Ether without
|
||||
using transactions. This means that the delays and fees associated with transactions
|
||||
can be avoided. We are going to explore a simple unidirectional payment channel between
|
||||
two parties (Alice and Bob). Using it involves three steps:
|
||||
|
||||
1. Alice funds a smart contract with Ether. This "opens" the payment channel.
|
||||
2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment.
|
||||
3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender.
|
||||
|
||||
Note that only steps 1 and 3 require Ethereum transactions, step 2 means that
|
||||
the sender transmits a cryptographically signed message to the recipient via off chain ways (e.g. email).
|
||||
This means only two transactions are required to support any number of transfers.
|
||||
|
||||
Bob is guaranteed to receive their funds because the smart contract escrows
|
||||
the Ether and honors a valid signed message. The smart contract also enforces a timeout,
|
||||
so Alice is guaranteed to eventually recover their funds even if the recipient refuses
|
||||
to close the channel.
|
||||
It is up to the participants in a payment channel to decide how long to keep it open.
|
||||
For a short-lived transaction, such as paying an internet cafe for each minute of network access,
|
||||
or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years.
|
||||
|
||||
Opening the Payment Channel
|
||||
---------------------------
|
||||
|
||||
To open the payment channel, Alice deploys the smart contract,
|
||||
attaching the Ether to be escrowed and specifying the intendend recipient
|
||||
and a maximum duration for the channel to exist. It is the function
|
||||
``SimplePaymentChannel`` in the contract, that is at the end of this chapter.
|
||||
|
||||
Making Payments
|
||||
---------------
|
||||
|
||||
Alice makes payments by sending signed messages to Bob.
|
||||
This step is performed entirely outside of the Ethereum network.
|
||||
Messages are cryptographically signed by the sender and then transmitted directly to the recipient.
|
||||
|
||||
Each message includes the following information:
|
||||
|
||||
* The smart contract's address, used to prevent cross-contract replay attacks.
|
||||
* The total amount of Ether that is owed the recipient so far.
|
||||
|
||||
A payment channel is closed just once, at the end of a series of transfers.
|
||||
Because of this, only one of the messages sent will be redeemed. This is why
|
||||
each message specifies a cumulative total amount of Ether owed, rather than the
|
||||
amount of the individual micropayment. The recipient will naturally choose to
|
||||
redeem the most recent message because that is the one with the highest total.
|
||||
The nonce per-message is not needed anymore, because the smart contract will
|
||||
only honor a single message. The address of the smart contract is still used
|
||||
to prevent a message intended for one payment channel from being used for a different channel.
|
||||
|
||||
Here is the modified javascript code to cryptographically sign a message from the previous chapter:
|
||||
|
||||
::
|
||||
|
||||
function constructPaymentMessage(contractAddress, amount) {
|
||||
return ethereumjs.ABI.soliditySHA3(
|
||||
["address", "uint256"],
|
||||
[contractAddress, amount]
|
||||
);
|
||||
}
|
||||
|
||||
function signMessage(message, callback) {
|
||||
web3.personal.sign(
|
||||
"0x" + message.toString("hex"),
|
||||
web3.eth.defaultAccount,
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
// contractAddress is used to prevent cross-contract replay attacks.
|
||||
// amount, in wei, specifies how much Ether should be sent.
|
||||
|
||||
function signPayment(contractAddress, amount, callback) {
|
||||
var message = constructPaymentMessage(contractAddress, amount);
|
||||
signMessage(message, callback);
|
||||
}
|
||||
|
||||
|
||||
Closing the Payment Channel
|
||||
---------------------------
|
||||
|
||||
When Bob is ready to receive their funds, it is time to
|
||||
close the payment channel by calling a ``close`` function on the smart contract.
|
||||
Closing the channel pays the recipient the Ether they are owed and destroys the contract,
|
||||
sending any remaining Ether back to Alice.
|
||||
To close the channel, Bob needs to provide a message signed by Alice.
|
||||
|
||||
The smart contract must verify that the message contains a valid signature from the sender.
|
||||
The process for doing this verification is the same as the process the recipient uses.
|
||||
The Solidity functions ``isValidSignature`` and ``recoverSigner`` work just like their
|
||||
JavaScript counterparts in the previous section. The latter is borrowed from the
|
||||
``ReceiverPays`` contract in the previous chapter.
|
||||
|
||||
The ``close`` function can only be called by the payment channel recipient,
|
||||
who will naturally pass the most recent payment message because that message
|
||||
carries the highest total owed. If the sender were allowed to call this function,
|
||||
they could provide a message with a lower amount and cheat the recipient out of what they are owed.
|
||||
|
||||
The function verifies the signed message matches the given parameters.
|
||||
If everything checks out, the recipient is sent their portion of the Ether,
|
||||
and the sender is sent the rest via a ``selfdestruct``.
|
||||
You can see the ``close`` function in the full contract.
|
||||
|
||||
Channel Expiration
|
||||
-------------------
|
||||
|
||||
Bob can close the payment channel at any time, but if they fail to do so,
|
||||
Alice needs a way to recover their escrowed funds. An *expiration* time was set
|
||||
at the time of contract deployment. Once that time is reached, Alice can call
|
||||
``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the
|
||||
full contract.
|
||||
|
||||
After this function is called, Bob can no longer receive any Ether,
|
||||
so it is important that Bob closes the channel before the expiration is reached.
|
||||
|
||||
|
||||
The full contract
|
||||
-----------------
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.4.24 <0.6.0;
|
||||
|
||||
contract SimplePaymentChannel {
|
||||
address payable public sender; // The account sending payments.
|
||||
address payable public recipient; // The account receiving the payments.
|
||||
uint256 public expiration; // Timeout in case the recipient never closes.
|
||||
|
||||
constructor (address payable _recipient, uint256 duration)
|
||||
public
|
||||
payable
|
||||
{
|
||||
sender = msg.sender;
|
||||
recipient = _recipient;
|
||||
expiration = now + duration;
|
||||
}
|
||||
|
||||
function isValidSignature(uint256 amount, bytes memory signature)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount)));
|
||||
|
||||
// check that the signature is from the payment sender
|
||||
return recoverSigner(message, signature) == sender;
|
||||
}
|
||||
|
||||
/// the recipient can close the channel at any time by presenting a
|
||||
/// signed amount from the sender. the recipient will be sent that amount,
|
||||
/// and the remainder will go back to the sender
|
||||
function close(uint256 amount, bytes memory signature) public {
|
||||
require(msg.sender == recipient);
|
||||
require(isValidSignature(amount, signature));
|
||||
|
||||
recipient.transfer(amount);
|
||||
selfdestruct(sender);
|
||||
}
|
||||
|
||||
/// the sender can extend the expiration at any time
|
||||
function extend(uint256 newExpiration) public {
|
||||
require(msg.sender == sender);
|
||||
require(newExpiration > expiration);
|
||||
|
||||
expiration = newExpiration;
|
||||
}
|
||||
|
||||
/// if the timeout is reached without the recipient closing the channel,
|
||||
/// then the Ether is released back to the sender.
|
||||
function claimTimeout() public {
|
||||
require(now >= expiration);
|
||||
selfdestruct(sender);
|
||||
}
|
||||
|
||||
/// All functions below this are just taken from the chapter
|
||||
/// 'creating and verifying signatures' chapter.
|
||||
|
||||
function splitSignature(bytes memory sig)
|
||||
internal
|
||||
pure
|
||||
returns (uint8 v, bytes32 r, bytes32 s)
|
||||
{
|
||||
require(sig.length == 65);
|
||||
|
||||
assembly {
|
||||
// first 32 bytes, after the length prefix
|
||||
r := mload(add(sig, 32))
|
||||
// second 32 bytes
|
||||
s := mload(add(sig, 64))
|
||||
// final byte (first byte of the next 32 bytes)
|
||||
v := byte(0, mload(add(sig, 96)))
|
||||
}
|
||||
|
||||
return (v, r, s);
|
||||
}
|
||||
|
||||
function recoverSigner(bytes32 message, bytes memory sig)
|
||||
internal
|
||||
pure
|
||||
returns (address)
|
||||
{
|
||||
(uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);
|
||||
|
||||
return ecrecover(message, v, r, s);
|
||||
}
|
||||
|
||||
/// builds a prefixed hash to mimic the behavior of eth_sign.
|
||||
function prefixed(bytes32 hash) internal pure returns (bytes32) {
|
||||
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Note: The function ``splitSignature`` is very simple and does not use all security checks.
|
||||
A real implementation should use a more rigorously tested library, such as
|
||||
openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code.
|
||||
|
||||
|
||||
|
||||
Verifying Payments
|
||||
------------------
|
||||
|
||||
Unlike in our previous chapter, messages in a payment channel aren't
|
||||
redeemed right away. The recipient keeps track of the latest message and
|
||||
redeems it when it's time to close the payment channel. This means it's
|
||||
critical that the recipient perform their own verification of each message.
|
||||
Otherwise there is no guarantee that the recipient will be able to get paid
|
||||
in the end.
|
||||
|
||||
The recipient should verify each message using the following process:
|
||||
|
||||
1. Verify that the contact address in the message matches the payment channel.
|
||||
2. Verify that the new total is the expected amount.
|
||||
3. Verify that the new total does not exceed the amount of Ether escrowed.
|
||||
4. Verify that the signature is valid and comes from the payment channel sender.
|
||||
|
||||
We'll use the `ethereumjs-util <https://github.com/ethereumjs/ethereumjs-util>`_
|
||||
library to write this verifications. The final step can be done a number of ways,
|
||||
but if it's being done in **JavaScript**.
|
||||
The following code borrows the `constructMessage` function from the signing **JavaScript code**
|
||||
above:
|
||||
|
||||
::
|
||||
|
||||
// this mimics the prefixing behavior of the eth_sign JSON-RPC method.
|
||||
function prefixed(hash) {
|
||||
return ethereumjs.ABI.soliditySHA3(
|
||||
["string", "bytes32"],
|
||||
["\x19Ethereum Signed Message:\n32", hash]
|
||||
);
|
||||
}
|
||||
|
||||
function recoverSigner(message, signature) {
|
||||
var split = ethereumjs.Util.fromRpcSig(signature);
|
||||
var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s);
|
||||
var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex");
|
||||
return signer;
|
||||
}
|
||||
|
||||
function isValidSignature(contractAddress, amount, signature, expectedSigner) {
|
||||
var message = prefixed(constructPaymentMessage(contractAddress, amount));
|
||||
var signer = recoverSigner(message, signature);
|
||||
return signer.toLowerCase() ==
|
||||
ethereumjs.Util.stripHexPrefix(expectedSigner).toLowerCase();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ Solidity in Depth
|
||||
|
||||
This section should provide you with all you need to know about Solidity.
|
||||
If something is missing here, please contact us on
|
||||
`Gitter <https://gitter.im/ethereum/solidity>`_ or make a pull request on
|
||||
`Gitter <https://gitter.im/ethereum/solidity>`_ or create a pull request on
|
||||
`Github <https://github.com/ethereum/solidity/pulls>`_.
|
||||
|
||||
.. toctree::
|
||||
@ -18,3 +18,4 @@ If something is missing here, please contact us on
|
||||
contracts.rst
|
||||
assembly.rst
|
||||
miscellaneous.rst
|
||||
050-breaking-changes.rst
|
||||
|
@ -11,16 +11,22 @@ Each contract can contain declarations of :ref:`structure-state-variables`, :ref
|
||||
:ref:`structure-function-modifiers`, :ref:`structure-events`, :ref:`structure-struct-types` and :ref:`structure-enum-types`.
|
||||
Furthermore, contracts can inherit from other contracts.
|
||||
|
||||
There are also special kinds of contracts called :ref:`libraries<libraries>` and :ref:`interfaces<interfaces>`.
|
||||
|
||||
The section about :ref:`contracts<contracts>` contains more details than this section,
|
||||
which serves to provide a quick overview.
|
||||
|
||||
.. _structure-state-variables:
|
||||
|
||||
State Variables
|
||||
===============
|
||||
|
||||
State variables are values which are permanently stored in contract storage.
|
||||
State variables are variables whose values are permanently stored in contract
|
||||
storage.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract SimpleStorage {
|
||||
uint storedData; // State variable
|
||||
@ -40,7 +46,7 @@ Functions are the executable units of code within a contract.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract SimpleAuction {
|
||||
function bid() public payable { // Function
|
||||
@ -49,7 +55,7 @@ Functions are the executable units of code within a contract.
|
||||
}
|
||||
|
||||
:ref:`function-calls` can happen internally or externally
|
||||
and have different levels of visibility (:ref:`visibility-and-getters`)
|
||||
and have different levels of :ref:`visibility<visibility-and-getters>`
|
||||
towards other contracts.
|
||||
|
||||
.. _structure-function-modifiers:
|
||||
@ -58,11 +64,11 @@ Function Modifiers
|
||||
==================
|
||||
|
||||
Function modifiers can be used to amend the semantics of functions in a declarative way
|
||||
(see :ref:`modifiers` in contracts section).
|
||||
(see :ref:`modifiers` in the contracts section).
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.22;
|
||||
pragma solidity >=0.4.22 <0.6.0;
|
||||
|
||||
contract Purchase {
|
||||
address public seller;
|
||||
@ -75,7 +81,7 @@ Function modifiers can be used to amend the semantics of functions in a declarat
|
||||
_;
|
||||
}
|
||||
|
||||
function abort() public onlySeller { // Modifier usage
|
||||
function abort() public view onlySeller { // Modifier usage
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@ -89,7 +95,7 @@ Events are convenience interfaces with the EVM logging facilities.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.21;
|
||||
pragma solidity >=0.4.21 <0.6.0;
|
||||
|
||||
contract SimpleAuction {
|
||||
event HighestBidIncreased(address bidder, uint amount); // Event
|
||||
@ -113,7 +119,7 @@ Structs are custom defined types that can group several variables (see
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract Ballot {
|
||||
struct Voter { // Struct
|
||||
@ -134,7 +140,7 @@ Enums can be used to create custom types with a finite set of 'constant values'
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract Purchase {
|
||||
enum State { Created, Locked, Inactive } // Enum
|
||||
|
@ -52,31 +52,35 @@ Surround top level declarations in solidity source with two blank lines.
|
||||
|
||||
Yes::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract A {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
contract B {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
contract C {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract A {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
contract B {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
contract C {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
Within a contract surround function declarations with a single blank line.
|
||||
@ -85,44 +89,48 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu
|
||||
|
||||
Yes::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract A {
|
||||
function spam() public;
|
||||
function ham() public;
|
||||
function spam() public pure;
|
||||
function ham() public pure;
|
||||
}
|
||||
|
||||
|
||||
contract B is A {
|
||||
function spam() public {
|
||||
...
|
||||
function spam() public pure {
|
||||
// ...
|
||||
}
|
||||
|
||||
function ham() public {
|
||||
...
|
||||
function ham() public pure {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract A {
|
||||
function spam() public {
|
||||
...
|
||||
function spam() public pure {
|
||||
// ...
|
||||
}
|
||||
function ham() public {
|
||||
...
|
||||
function ham() public pure {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
.. _maximum_line_length:
|
||||
|
||||
Maximum Line Length
|
||||
Maximum Line Length
|
||||
===================
|
||||
|
||||
Keeping lines under the `PEP 8 recommendation <https://www.python.org/dev/peps/pep-0008/#maximum-line-length>`_ to a maximum of 79 (or 99)
|
||||
Keeping lines under the `PEP 8 recommendation <https://www.python.org/dev/peps/pep-0008/#maximum-line-length>`_ to a maximum of 79 (or 99)
|
||||
characters helps readers easily parse the code.
|
||||
|
||||
Wrapped lines should conform to the following guidelines.
|
||||
|
||||
1. The first argument should not be attached to the opening parenthesis.
|
||||
1. The first argument should not be attached to the opening parenthesis.
|
||||
2. One, and only one, indent should be used.
|
||||
3. Each argument should fall on its own line.
|
||||
4. The terminating element, :code:`);`, should be placed on the final line by itself.
|
||||
@ -132,38 +140,38 @@ Function Calls
|
||||
Yes::
|
||||
|
||||
thisFunctionCallIsReallyLong(
|
||||
longArgument1,
|
||||
longArgument2,
|
||||
longArgument1,
|
||||
longArgument2,
|
||||
longArgument3
|
||||
);
|
||||
|
||||
No::
|
||||
|
||||
thisFunctionCallIsReallyLong(longArgument1,
|
||||
longArgument2,
|
||||
thisFunctionCallIsReallyLong(longArgument1,
|
||||
longArgument2,
|
||||
longArgument3
|
||||
);
|
||||
|
||||
thisFunctionCallIsReallyLong(longArgument1,
|
||||
longArgument2,
|
||||
|
||||
thisFunctionCallIsReallyLong(longArgument1,
|
||||
longArgument2,
|
||||
longArgument3
|
||||
);
|
||||
|
||||
);
|
||||
|
||||
thisFunctionCallIsReallyLong(
|
||||
longArgument1, longArgument2,
|
||||
longArgument3
|
||||
);
|
||||
);
|
||||
|
||||
thisFunctionCallIsReallyLong(
|
||||
longArgument1,
|
||||
longArgument2,
|
||||
longArgument1,
|
||||
longArgument2,
|
||||
longArgument3
|
||||
);
|
||||
|
||||
thisFunctionCallIsReallyLong(
|
||||
longArgument1,
|
||||
longArgument2,
|
||||
longArgument3);
|
||||
longArgument1,
|
||||
longArgument2,
|
||||
longArgument3);
|
||||
|
||||
Assignment Statements
|
||||
|
||||
@ -188,8 +196,8 @@ Event Definitions and Event Emitters
|
||||
Yes::
|
||||
|
||||
event LongAndLotsOfArgs(
|
||||
adress sender,
|
||||
adress recipient,
|
||||
address sender,
|
||||
address recipient,
|
||||
uint256 publicKey,
|
||||
uint256 amount,
|
||||
bytes32[] options
|
||||
@ -205,8 +213,8 @@ Yes::
|
||||
|
||||
No::
|
||||
|
||||
event LongAndLotsOfArgs(adress sender,
|
||||
adress recipient,
|
||||
event LongAndLotsOfArgs(address sender,
|
||||
address recipient,
|
||||
uint256 publicKey,
|
||||
uint256 amount,
|
||||
bytes32[] options);
|
||||
@ -215,7 +223,7 @@ No::
|
||||
recipient,
|
||||
publicKey,
|
||||
amount,
|
||||
options);
|
||||
options);
|
||||
|
||||
Source File Encoding
|
||||
====================
|
||||
@ -229,30 +237,32 @@ Import statements should always be placed at the top of the file.
|
||||
|
||||
Yes::
|
||||
|
||||
import "owned";
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
import "./Owned.sol";
|
||||
|
||||
contract A {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
contract B is owned {
|
||||
...
|
||||
contract B is Owned {
|
||||
// ...
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract A {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
import "owned";
|
||||
import "./Owned.sol";
|
||||
|
||||
|
||||
contract B is owned {
|
||||
...
|
||||
contract B is Owned {
|
||||
// ...
|
||||
}
|
||||
|
||||
Order of Functions
|
||||
@ -273,13 +283,15 @@ Within a grouping, place the ``view`` and ``pure`` functions last.
|
||||
|
||||
Yes::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract A {
|
||||
function A() public {
|
||||
...
|
||||
constructor() public {
|
||||
// ...
|
||||
}
|
||||
|
||||
function() public {
|
||||
...
|
||||
function() external {
|
||||
// ...
|
||||
}
|
||||
|
||||
// External functions
|
||||
@ -303,23 +315,25 @@ Yes::
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract A {
|
||||
|
||||
// External functions
|
||||
// ...
|
||||
|
||||
function() external {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Private functions
|
||||
// ...
|
||||
|
||||
// Public functions
|
||||
// ...
|
||||
|
||||
function A() public {
|
||||
...
|
||||
}
|
||||
|
||||
function() public {
|
||||
...
|
||||
constructor() public {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Internal functions
|
||||
@ -374,13 +388,13 @@ Don't include a whitespace in the fallback function:
|
||||
|
||||
Yes::
|
||||
|
||||
function() public {
|
||||
function() external {
|
||||
...
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
function () public {
|
||||
function () external {
|
||||
...
|
||||
}
|
||||
|
||||
@ -397,6 +411,8 @@ should:
|
||||
|
||||
Yes::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract Coin {
|
||||
struct Bank {
|
||||
address owner;
|
||||
@ -406,6 +422,8 @@ Yes::
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
contract Coin
|
||||
{
|
||||
struct Bank {
|
||||
@ -529,7 +547,7 @@ No::
|
||||
function increment(uint x) public pure returns (uint) {
|
||||
return x + 1;}
|
||||
|
||||
You should explicitly label the visibility of all functions, including constructors.
|
||||
You should explicitly label the visibility of all functions, including constructors.
|
||||
|
||||
Yes::
|
||||
|
||||
@ -540,7 +558,7 @@ Yes::
|
||||
No::
|
||||
|
||||
function implicitlyPublic(uint val) {
|
||||
doSomething();
|
||||
doSomething();
|
||||
}
|
||||
|
||||
The visibility modifier for a function should come before any custom
|
||||
@ -663,19 +681,19 @@ Yes::
|
||||
address a,
|
||||
address b,
|
||||
address c
|
||||
)
|
||||
public
|
||||
)
|
||||
public
|
||||
returns (
|
||||
address someAddressName,
|
||||
uint256 LongArgument,
|
||||
address someAddressName,
|
||||
uint256 LongArgument,
|
||||
uint256 Argument
|
||||
)
|
||||
{
|
||||
{
|
||||
doSomething()
|
||||
|
||||
|
||||
return (
|
||||
veryLongReturnArg1,
|
||||
veryLongReturnArg2,
|
||||
veryLongReturnArg1,
|
||||
veryLongReturnArg2,
|
||||
veryLongReturnArg3
|
||||
);
|
||||
}
|
||||
@ -686,16 +704,16 @@ No::
|
||||
address a,
|
||||
address b,
|
||||
address c
|
||||
)
|
||||
public
|
||||
returns (address someAddressName,
|
||||
uint256 LongArgument,
|
||||
)
|
||||
public
|
||||
returns (address someAddressName,
|
||||
uint256 LongArgument,
|
||||
uint256 Argument)
|
||||
{
|
||||
{
|
||||
doSomething()
|
||||
|
||||
return (veryLongReturnArg1,
|
||||
veryLongReturnArg1,
|
||||
|
||||
return (veryLongReturnArg1,
|
||||
veryLongReturnArg1,
|
||||
veryLongReturnArg1);
|
||||
}
|
||||
|
||||
@ -705,37 +723,76 @@ manner as modifiers if the function declaration is long or hard to read.
|
||||
|
||||
Yes::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
// Base contracts just to make this compile
|
||||
contract B {
|
||||
constructor(uint) public {
|
||||
}
|
||||
}
|
||||
contract C {
|
||||
constructor(uint, uint) public {
|
||||
}
|
||||
}
|
||||
contract D {
|
||||
constructor(uint) public {
|
||||
}
|
||||
}
|
||||
|
||||
contract A is B, C, D {
|
||||
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
|
||||
uint x;
|
||||
|
||||
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
|
||||
B(param1)
|
||||
C(param2, param3)
|
||||
D(param4)
|
||||
public
|
||||
{
|
||||
// do something with param5
|
||||
x = param5;
|
||||
}
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
// Base contracts just to make this compile
|
||||
contract B {
|
||||
constructor(uint) public {
|
||||
}
|
||||
}
|
||||
contract C {
|
||||
constructor(uint, uint) public {
|
||||
}
|
||||
}
|
||||
contract D {
|
||||
constructor(uint) public {
|
||||
}
|
||||
}
|
||||
|
||||
contract A is B, C, D {
|
||||
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
|
||||
uint x;
|
||||
|
||||
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
|
||||
B(param1)
|
||||
C(param2, param3)
|
||||
D(param4)
|
||||
public
|
||||
{
|
||||
// do something with param5
|
||||
x = param5;
|
||||
}
|
||||
}
|
||||
|
||||
contract A is B, C, D {
|
||||
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
|
||||
contract X is B, C, D {
|
||||
uint x;
|
||||
|
||||
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
|
||||
B(param1)
|
||||
C(param2, param3)
|
||||
D(param4)
|
||||
public {
|
||||
// do something with param5
|
||||
x = param5;
|
||||
}
|
||||
}
|
||||
|
||||
@ -817,6 +874,29 @@ No::
|
||||
x = y+z;
|
||||
x +=1;
|
||||
|
||||
***************
|
||||
Order of Layout
|
||||
***************
|
||||
|
||||
Layout contract elements in the following order:
|
||||
|
||||
1. Pragma statements
|
||||
2. Import statements
|
||||
3. Interfaces
|
||||
4. Libraries
|
||||
5. Contracts
|
||||
|
||||
Inside each contract, library or interface, use the following order:
|
||||
|
||||
1. Type declarations
|
||||
2. State variables
|
||||
3. Events
|
||||
4. Functions
|
||||
|
||||
.. note::
|
||||
|
||||
It might be clearer to declare types close to their use in events or state
|
||||
variables.
|
||||
|
||||
******************
|
||||
Naming Conventions
|
||||
@ -830,7 +910,7 @@ The naming recommendations given here are intended to improve the readability,
|
||||
and thus they are not rules, but rather guidelines to try and help convey the
|
||||
most information through the names of things.
|
||||
|
||||
Lastly, consistency within a codebase should always supercede any conventions
|
||||
Lastly, consistency within a codebase should always supersede any conventions
|
||||
outlined in this document.
|
||||
|
||||
|
||||
@ -867,7 +947,69 @@ indistinguishable from the numerals one and zero.
|
||||
Contract and Library Names
|
||||
==========================
|
||||
|
||||
Contracts and libraries should be named using the CapWords style. Examples: ``SimpleToken``, ``SmartBank``, ``CertificateHashRepository``, ``Player``.
|
||||
* Contracts and libraries should be named using the CapWords style. Examples: ``SimpleToken``, ``SmartBank``, ``CertificateHashRepository``, ``Player``, ``Congress``, ``Owned``.
|
||||
* Contract and library names should also match their filenames.
|
||||
* If a contract file includes multiple contracts and/or libraries, then the filename should match the *core contract*. This is not recommended however if it can be avoided.
|
||||
|
||||
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::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
// Owned.sol
|
||||
contract Owned {
|
||||
address public owner;
|
||||
|
||||
constructor() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
modifier onlyOwner {
|
||||
require(msg.sender == owner);
|
||||
_;
|
||||
}
|
||||
|
||||
function transferOwnership(address newOwner) public onlyOwner {
|
||||
owner = newOwner;
|
||||
}
|
||||
}
|
||||
|
||||
// Congress.sol
|
||||
import "./Owned.sol";
|
||||
|
||||
contract Congress is Owned, TokenRecipient {
|
||||
//...
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
pragma solidity >=0.4.0 <0.6.0;
|
||||
|
||||
// owned.sol
|
||||
contract owned {
|
||||
address public owner;
|
||||
|
||||
constructor() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
modifier onlyOwner {
|
||||
require(msg.sender == owner);
|
||||
_;
|
||||
}
|
||||
|
||||
function transferOwnership(address newOwner) public onlyOwner {
|
||||
owner = newOwner;
|
||||
}
|
||||
}
|
||||
|
||||
// Congress.sol
|
||||
import "./owned.sol";
|
||||
|
||||
contract Congress is owned, tokenRecipient {
|
||||
//...
|
||||
}
|
||||
|
||||
|
||||
Struct Names
|
||||
|
791
docs/types.rst
791
docs/types.rst
File diff suppressed because it is too large
Load Diff
@ -7,15 +7,25 @@ Units and Globally Available Variables
|
||||
Ether Units
|
||||
===========
|
||||
|
||||
A literal number can take a suffix of ``wei``, ``finney``, ``szabo`` or ``ether`` to convert between the subdenominations of Ether, where Ether currency numbers without a postfix are assumed to be Wei, e.g. ``2 ether == 2000 finney`` evaluates to ``true``.
|
||||
A literal number can take a suffix of ``wei``, ``finney``, ``szabo`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei.
|
||||
|
||||
::
|
||||
|
||||
assert(1 wei == 1);
|
||||
assert(1 szabo == 1e12);
|
||||
assert(1 finney == 1e15);
|
||||
assert(1 ether == 1e18);
|
||||
|
||||
The only effect of the subdenomination suffix is a multiplication by a power of ten.
|
||||
|
||||
|
||||
.. index:: time, seconds, minutes, hours, days, weeks, years
|
||||
|
||||
Time Units
|
||||
==========
|
||||
|
||||
Suffixes like ``seconds``, ``minutes``, ``hours``, ``days``, ``weeks`` and
|
||||
``years`` after literal numbers can be used to convert between units of time where seconds are the base
|
||||
Suffixes like ``seconds``, ``minutes``, ``hours``, ``days`` and ``weeks``
|
||||
after literal numbers can be used to specify units of time where seconds are the base
|
||||
unit and units are considered naively in the following way:
|
||||
|
||||
* ``1 == 1 seconds``
|
||||
@ -23,7 +33,6 @@ unit and units are considered naively in the following way:
|
||||
* ``1 hours == 60 minutes``
|
||||
* ``1 days == 24 hours``
|
||||
* ``1 weeks == 7 days``
|
||||
* ``1 years == 365 days``
|
||||
|
||||
Take care if you perform calendar calculations using these units, because
|
||||
not every year equals 365 days and not even every day has 24 hours
|
||||
@ -32,7 +41,7 @@ Due to the fact that leap seconds cannot be predicted, an exact calendar
|
||||
library has to be updated by an external oracle.
|
||||
|
||||
.. note::
|
||||
The suffix ``years`` has been deprecated due to the reasons above.
|
||||
The suffix ``years`` has been removed in version 0.5.0 due to the reasons above.
|
||||
|
||||
These suffixes cannot be applied to variables. If you want to
|
||||
interpret some input variable in e.g. days, you can do it in the following way::
|
||||
@ -56,21 +65,20 @@ or are general-use utility functions.
|
||||
Block and Transaction Properties
|
||||
--------------------------------
|
||||
|
||||
- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``.
|
||||
- ``block.coinbase`` (``address``): current block miner's address
|
||||
- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks
|
||||
- ``block.coinbase`` (``address payable``): current block miner's address
|
||||
- ``block.difficulty`` (``uint``): current block difficulty
|
||||
- ``block.gaslimit`` (``uint``): current block gaslimit
|
||||
- ``block.number`` (``uint``): current block number
|
||||
- ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch
|
||||
- ``gasleft() returns (uint256)``: remaining gas
|
||||
- ``msg.data`` (``bytes``): complete calldata
|
||||
- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()``
|
||||
- ``msg.sender`` (``address``): sender of the message (current call)
|
||||
- ``msg.data`` (``bytes calldata``): complete calldata
|
||||
- ``msg.sender`` (``address payable``): sender of the message (current call)
|
||||
- ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier)
|
||||
- ``msg.value`` (``uint``): number of wei sent with the message
|
||||
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
|
||||
- ``tx.gasprice`` (``uint``): gas price of the transaction
|
||||
- ``tx.origin`` (``address``): sender of the transaction (full call chain)
|
||||
- ``tx.origin`` (``address payable``): sender of the transaction (full call chain)
|
||||
|
||||
.. note::
|
||||
The values of all members of ``msg``, including ``msg.sender`` and
|
||||
@ -94,22 +102,30 @@ Block and Transaction Properties
|
||||
You can only access the hashes of the most recent 256 blocks, all other
|
||||
values will be zero.
|
||||
|
||||
.. index:: abi, encoding, packed
|
||||
|
||||
ABI Encoding Functions
|
||||
----------------------
|
||||
|
||||
- ``abi.encode(...) returns (bytes)``: ABI-encodes the given arguments
|
||||
- ``abi.encodePacked(...) returns (bytes)``: Performes packed encoding of the given arguments
|
||||
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: ABI-encodes the given arguments
|
||||
starting from the second and prepends the given four-byte selector
|
||||
- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(signature), ...)```
|
||||
.. note::
|
||||
The function ``blockhash`` was previously known as ``block.blockhash``. It was deprecated in
|
||||
version 0.4.22 and removed in version 0.5.0.
|
||||
|
||||
.. note::
|
||||
These encoding functions can be used to craft data for function calls without actually
|
||||
calling a function. Furthermore, ``keccak256(abi.encodePacked(a, b))`` is a more
|
||||
explicit way to compute ``keccak256(a, b)``, which will be deprecated in future
|
||||
versions.
|
||||
The function ``gasleft`` was previously known as ``msg.gas``. It was deprecated in
|
||||
version 0.4.21 and removed in version 0.5.0.
|
||||
|
||||
.. index:: abi, encoding, packed
|
||||
|
||||
ABI Encoding and Decoding Functions
|
||||
-----------------------------------
|
||||
|
||||
- ``abi.decode(bytes memory encodedData, (...)) returns (...)``: ABI-decodes the given data, while the types are given in parentheses as second argument. Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))``
|
||||
- ``abi.encode(...) returns (bytes memory)``: ABI-encodes the given arguments
|
||||
- ``abi.encodePacked(...) returns (bytes memory)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments
|
||||
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: ABI-encodes the given arguments starting from the second and prepends the given four-byte selector
|
||||
- ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)```
|
||||
|
||||
.. note::
|
||||
These encoding functions can be used to craft data for external function calls without actually
|
||||
calling an external function. Furthermore, ``keccak256(abi.encodePacked(a, b))`` is a way
|
||||
to compute the hash of structured data (although be aware that it is possible to
|
||||
craft a "hash collision" using different inputs types).
|
||||
|
||||
See the documentation about the :ref:`ABI <ABI>` and the
|
||||
:ref:`tightly packed encoding <abi_packed_mode>` for details about the encoding.
|
||||
@ -119,15 +135,18 @@ See the documentation about the :ref:`ABI <ABI>` and the
|
||||
Error Handling
|
||||
--------------
|
||||
|
||||
See the dedicated section on :ref:`assert and require<assert-and-require>` for
|
||||
more details on error handling and when to use which function.
|
||||
|
||||
``assert(bool condition)``:
|
||||
invalidates the transaction if the condition is not met - to be used for internal errors.
|
||||
causes an invalid opcode and thus state change reversion if the condition is not met - to be used for internal errors.
|
||||
``require(bool condition)``:
|
||||
reverts if the condition is not met - to be used for errors in inputs or external components.
|
||||
``require(bool condition, string message)``:
|
||||
``require(bool condition, string memory message)``:
|
||||
reverts if the condition is not met - to be used for errors in inputs or external components. Also provides an error message.
|
||||
``revert()``:
|
||||
abort execution and revert state changes
|
||||
``revert(string reason)``:
|
||||
``revert(string memory reason)``:
|
||||
abort execution and revert state changes, providing an explanatory string
|
||||
|
||||
.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography,
|
||||
@ -139,54 +158,44 @@ Mathematical and Cryptographic Functions
|
||||
compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
||||
``mulmod(uint x, uint y, uint k) returns (uint)``:
|
||||
compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
|
||||
``keccak256(...) returns (bytes32)``:
|
||||
compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
||||
``sha256(...) returns (bytes32)``:
|
||||
compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
||||
``sha3(...) returns (bytes32)``:
|
||||
alias to ``keccak256``
|
||||
``ripemd160(...) returns (bytes20)``:
|
||||
compute RIPEMD-160 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
|
||||
``keccak256(bytes memory) returns (bytes32)``:
|
||||
compute the Keccak-256 hash of the input
|
||||
``sha256(bytes memory) returns (bytes32)``:
|
||||
compute the SHA-256 hash of the input
|
||||
``ripemd160(bytes memory) returns (bytes20)``:
|
||||
compute RIPEMD-160 hash of the input
|
||||
``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``:
|
||||
recover the address associated with the public key from elliptic curve signature or return zero on error
|
||||
(`example usage <https://ethereum.stackexchange.com/q/1777/222>`_)
|
||||
|
||||
In the above, "tightly packed" means that the arguments are concatenated without padding.
|
||||
This means that the following are all identical::
|
||||
|
||||
keccak256("ab", "c")
|
||||
keccak256("abc")
|
||||
keccak256(0x616263)
|
||||
keccak256(6382179)
|
||||
keccak256(97, 98, 99)
|
||||
|
||||
If padding is needed, explicit type conversions can be used: ``keccak256("\x00\x12")`` is the
|
||||
same as ``keccak256(uint16(0x12))``.
|
||||
|
||||
Note that constants will be packed using the minimum number of bytes required to store them.
|
||||
This means that, for example, ``keccak256(0) == keccak256(uint8(0))`` and
|
||||
``keccak256(0x12345678) == keccak256(uint32(0x12345678))``.
|
||||
.. note::
|
||||
Function ``ecrecover`` returns an ``address``, and not an ``address
|
||||
payable``. See :ref:`address payable<address>` for conversion, in case you need
|
||||
to transfer funds to the recovered address.
|
||||
|
||||
It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net.
|
||||
|
||||
.. index:: balance, send, transfer, call, callcode, delegatecall
|
||||
.. note::
|
||||
There used to be an alias for ``keccak256`` called ``sha3``, which was removed in version 0.5.0.
|
||||
|
||||
.. index:: balance, send, transfer, call, callcode, delegatecall, staticcall
|
||||
.. _address_related:
|
||||
|
||||
Address Related
|
||||
---------------
|
||||
Members of Address Types
|
||||
------------------------
|
||||
|
||||
``<address>.balance`` (``uint256``):
|
||||
balance of the :ref:`address` in Wei
|
||||
``<address>.transfer(uint256 amount)``:
|
||||
send given amount of Wei to :ref:`address`, throws on failure, forwards 2300 gas stipend, not adjustable
|
||||
``<address>.send(uint256 amount) returns (bool)``:
|
||||
``<address payable>.transfer(uint256 amount)``:
|
||||
send given amount of Wei to :ref:`address`, reverts on failure, forwards 2300 gas stipend, not adjustable
|
||||
``<address payable>.send(uint256 amount) returns (bool)``:
|
||||
send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable
|
||||
``<address>.call(...) returns (bool)``:
|
||||
issue low-level ``CALL``, returns ``false`` on failure, forwards all available gas, adjustable
|
||||
``<address>.callcode(...) returns (bool)``:
|
||||
issue low-level ``CALLCODE``, returns ``false`` on failure, forwards all available gas, adjustable
|
||||
``<address>.delegatecall(...) returns (bool)``:
|
||||
issue low-level ``DELEGATECALL``, returns ``false`` on failure, forwards all available gas, adjustable
|
||||
``<address>.call(bytes memory) returns (bool, bytes memory)``:
|
||||
issue low-level ``CALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable
|
||||
``<address>.delegatecall(bytes memory) returns (bool, bytes memory)``:
|
||||
issue low-level ``DELEGATECALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable
|
||||
``<address>.staticcall(bytes memory) returns (bool, bytes memory)``:
|
||||
issue low-level ``STATICCALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable
|
||||
|
||||
For more information, see the section on :ref:`address`.
|
||||
|
||||
@ -197,14 +206,23 @@ For more information, see the section on :ref:`address`.
|
||||
Use a pattern where the recipient withdraws the money.
|
||||
|
||||
.. note::
|
||||
If storage variables are accessed via a low-level delegatecall, the storage layout of the two contracts
|
||||
Prior to version 0.5.0, Solidity allowed address members to be accessed by a contract instance, for example ``this.balance``.
|
||||
This is now forbidden and an explicit conversion to address must be done: ``address(this).balance``.
|
||||
|
||||
.. note::
|
||||
If state variables are accessed via a low-level delegatecall, the storage layout of the two contracts
|
||||
must align in order for the called contract to correctly access the storage variables of the calling contract by name.
|
||||
This is of course not the case if storage pointers are passed as function arguments as in the case for
|
||||
the high-level libraries.
|
||||
|
||||
|
||||
|
||||
.. note::
|
||||
The use of ``callcode`` is discouraged and will be removed in the future.
|
||||
Prior to version 0.5.0, ``.call``, ``.delegatecall`` and ``.staticcall`` only returned the
|
||||
success condition and not the return data.
|
||||
|
||||
.. note::
|
||||
Prior to version 0.5.0, there was a member called ``callcode`` with similar but slightly different
|
||||
semantics than ``delegatecall``.
|
||||
|
||||
|
||||
.. index:: this, selfdestruct
|
||||
|
||||
@ -214,11 +232,12 @@ Contract Related
|
||||
``this`` (current contract's type):
|
||||
the current contract, explicitly convertible to :ref:`address`
|
||||
|
||||
``selfdestruct(address recipient)``:
|
||||
``selfdestruct(address payable recipient)``:
|
||||
destroy the current contract, sending its funds to the given :ref:`address`
|
||||
|
||||
``suicide(address recipient)``:
|
||||
deprecated alias to ``selfdestruct``
|
||||
|
||||
Furthermore, all functions of the current contract are callable directly including the current function.
|
||||
|
||||
.. note::
|
||||
Prior to version 0.5.0, there was a function called ``suicide`` with the same
|
||||
semantics as ``selfdestruct``.
|
||||
|
||||
|
@ -10,50 +10,127 @@ Using the Commandline Compiler
|
||||
******************************
|
||||
|
||||
.. note::
|
||||
This section doesn't apply to :ref:`solcjs <solcjs>`.
|
||||
This section does not apply to :ref:`solcjs <solcjs>`, not even if it is used in commandline mode.
|
||||
|
||||
One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler.
|
||||
Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage.
|
||||
If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``.
|
||||
|
||||
Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. By default, the optimizer will optimize the contract for 200 runs. If you want to optimize for initial contract deployment and get the smallest output, set it to ``--runs=1``. If you expect many transactions and don't care for higher deployment cost and output size, set ``--runs`` to a high number.
|
||||
Before you deploy your contract, activate the optimizer when compiling using ``solc --optimize --bin sourceFile.sol``.
|
||||
By default, the optimizer will optimize the contract assuming it is called 200 times across its lifetime.
|
||||
If you want the initial contract deployment to be cheaper and the later function executions to be more expensive,
|
||||
set it to ``--runs=1``. If you expect many transactions and do not care for higher deployment cost and
|
||||
output size, set ``--runs`` to a high number.
|
||||
|
||||
The commandline compiler will automatically read imported files from the filesystem, but
|
||||
it is also possible to provide path redirects using ``prefix=path`` in the following way:
|
||||
|
||||
::
|
||||
|
||||
solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ =/usr/local/lib/fallback file.sol
|
||||
solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ file.sol
|
||||
|
||||
This essentially instructs the compiler to search for anything starting with
|
||||
``github.com/ethereum/dapp-bin/`` under ``/usr/local/lib/dapp-bin`` and if it does not
|
||||
find the file there, it will look at ``/usr/local/lib/fallback`` (the empty prefix
|
||||
always matches). ``solc`` will not read files from the filesystem that lie outside of
|
||||
``github.com/ethereum/dapp-bin/`` under ``/usr/local/lib/dapp-bin``.
|
||||
``solc`` will not read files from the filesystem that lie outside of
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
If your contracts use :ref:`libraries <libraries>`, you will notice that the bytecode contains substrings of the form ``__LibraryName______``. You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points:
|
||||
If your contracts use :ref:`libraries <libraries>`, you will notice that the bytecode contains substrings of the form ``__$53aea86b7d70b31448b230b20ae141a537$__``. These are placeholders for the actual library addresses.
|
||||
The placeholder is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library name.
|
||||
The bytecode file will also contain lines of the form ``// <placeholder> -> <fq library name>`` at the end to help
|
||||
identify which libraries the placeholders represent. Note that the fully qualified library name
|
||||
is the path of its source file and the library name separated by ``:``.
|
||||
You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points:
|
||||
|
||||
Either add ``--libraries "Math:0x12345678901234567890 Heap:0xabcdef0123456"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``.
|
||||
Either add ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``.
|
||||
|
||||
If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__LibraryName____``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case.
|
||||
If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case.
|
||||
|
||||
If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output.
|
||||
If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses.
|
||||
|
||||
.. note::
|
||||
The library placeholder used to be the fully qualified name of the library itself
|
||||
instead of the hash of it. This format is still supported by ``solc --link`` but
|
||||
the compiler will no longer output it. This change was made to reduce
|
||||
the likelihood of a collision between libraries, since only the first 36 characters
|
||||
of the fully qualified library name could be used.
|
||||
|
||||
.. _evm-version:
|
||||
.. index:: ! EVM version, compile target
|
||||
|
||||
Setting the EVM version to target
|
||||
*********************************
|
||||
|
||||
When you compile your contract code you can specify the Ethereum virtual machine
|
||||
version to compile for to avoid particular features or behaviours.
|
||||
|
||||
.. warning::
|
||||
|
||||
Compiling for the wrong EVM version can result in wrong, strange and failing
|
||||
behaviour. Please ensure, especially if running a private chain, that you
|
||||
use matching EVM versions.
|
||||
|
||||
On the command line, you can select the EVM version as follows:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
solc --evm-version <VERSION> contract.sol
|
||||
|
||||
In the :ref:`standard JSON interface <compiler-api>`, use the ``"evmVersion"``
|
||||
key in the ``"settings"`` field:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
{
|
||||
"sources": { ... },
|
||||
"settings": {
|
||||
"optimizer": { ... },
|
||||
"evmVersion": "<VERSION>"
|
||||
}
|
||||
}
|
||||
|
||||
Target options
|
||||
--------------
|
||||
|
||||
Below is a list of target EVM versions and the compiler-relevant changes introduced
|
||||
at each version. Backward compatibility is not guaranteed between each version.
|
||||
|
||||
- ``homestead`` (oldest version)
|
||||
- ``tangerineWhistle``
|
||||
- gas cost for access to other accounts increased, relevant for gas estimation and the optimizer.
|
||||
- all gas sent by default for external calls, previously a certain amount had to be retained.
|
||||
- ``spuriousDragon``
|
||||
- gas cost for the ``exp`` opcode increased, relevant for gas estimation and the optimizer.
|
||||
- ``byzantium`` (**default**)
|
||||
- opcodes ``returndatacopy``, ``returndatasize`` and ``staticcall`` are available in assembly.
|
||||
- the ``staticcall`` opcode is used when calling non-library view or pure functions, which prevents the functions from modifying state at the EVM level, i.e., even applies when you use invalid type conversions.
|
||||
- it is possible to access dynamic data returned from function calls.
|
||||
- ``revert`` opcode introduced, which means that ``revert()`` will not waste gas.
|
||||
- ``constantinople`` (still in progress)
|
||||
- opcodes ``shl``, ``shr`` and ``sar`` are available in assembly.
|
||||
- shifting operators use shifting opcodes and thus need less gas.
|
||||
|
||||
.. _compiler-api:
|
||||
|
||||
Compiler Input and Output JSON Description
|
||||
******************************************
|
||||
|
||||
These JSON formats are used by the compiler API as well as are available through ``solc``. These are subject to change,
|
||||
some fields are optional (as noted), but it is aimed at to only make backwards compatible changes.
|
||||
The recommended way to interface with the Solidity compiler especially for
|
||||
more complex and automated setups is the so-called JSON-input-output interface.
|
||||
The same interface is provided by all distributions of the compiler.
|
||||
|
||||
The fields are generally subject to change,
|
||||
some are optional (as noted), but we try to only make backwards compatible changes.
|
||||
|
||||
The compiler API expects a JSON formatted input and outputs the compilation result in a JSON formatted output.
|
||||
|
||||
The following subsections describe the format through an example.
|
||||
Comments are of course not permitted and used here only for explanatory purposes.
|
||||
|
||||
Input Description
|
||||
@ -62,7 +139,7 @@ Input Description
|
||||
.. code-block:: none
|
||||
|
||||
{
|
||||
// Required: Source code language, such as "Solidity", "serpent", "lll", "assembly", etc.
|
||||
// Required: Source code language, such as "Solidity", "Vyper", "lll", "assembly", etc.
|
||||
language: "Solidity",
|
||||
// Required
|
||||
sources:
|
||||
@ -82,6 +159,8 @@ Input Description
|
||||
[
|
||||
"bzzr://56ab...",
|
||||
"ipfs://Qma...",
|
||||
// If files are used, their directories should be added to the command line via
|
||||
// `--allow-paths <path>`.
|
||||
"file:///tmp/path/to/file.sol"
|
||||
]
|
||||
},
|
||||
|
@ -1,82 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re
|
||||
import copy
|
||||
|
||||
from pygments.lexer import RegexLexer, ExtendedRegexLexer, bygroups, using, \
|
||||
include, this
|
||||
from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
|
||||
Number, Other, Punctuation, Literal
|
||||
|
||||
__all__ = ['SolidityLexer']
|
||||
|
||||
class SolidityLexer(RegexLexer):
|
||||
name = "Solidity"
|
||||
aliases = ['sol', 'solidity']
|
||||
filenames = ['*.sol']
|
||||
mimetypes = []
|
||||
flags = re.DOTALL
|
||||
tokens = {
|
||||
'commentsandwhitespace': [
|
||||
(r'\s+', Text),
|
||||
(r'<!--', Comment),
|
||||
(r'///', Comment.Special, 'docstringsingle'),
|
||||
(r'//.*?\n', Comment.Single),
|
||||
(r'/\*\*', Comment.Special, 'docstringmulti'),
|
||||
(r'/\*.*?\*/', Comment.Multiline)
|
||||
],
|
||||
'natspec': [
|
||||
(r'@author|@dev|@notice|@return|@param|@title', Keyword),
|
||||
(r'.[^@*\n]*?', Comment.Special)
|
||||
],
|
||||
'docstringsingle': [
|
||||
(r'\n', Comment.Special, '#pop'),
|
||||
include('natspec')
|
||||
],
|
||||
'docstringmulti': [
|
||||
(r'\*/', Comment.Special, '#pop'),
|
||||
include('natspec')
|
||||
],
|
||||
'slashstartsregex': [
|
||||
include('commentsandwhitespace'),
|
||||
(r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/'
|
||||
r'([gim]+\b|\B)', String.Regex, '#pop'),
|
||||
(r'(?=/)', Text, ('#pop', 'badregex')),
|
||||
(r'', Text, '#pop')
|
||||
],
|
||||
'badregex': [
|
||||
(r'\n', Text, '#pop')
|
||||
],
|
||||
'root': [
|
||||
(r'^(?=\s|/|<!--)', Text, 'slashstartsregex'),
|
||||
include('commentsandwhitespace'),
|
||||
(r'\+\+|--|\*\*|~|&&|\?|:|\|\||\\(?=\n)|'
|
||||
r'(<<|>>>?|==?|!=?|[-<>+*%&\|\^/])=?', Operator, 'slashstartsregex'),
|
||||
(r'[{(\[;,]', Punctuation, 'slashstartsregex'),
|
||||
(r'[})\].]', Punctuation),
|
||||
(r'(anonymous|as|assembly|break|constant|continue|do|delete|else|external|for|hex|if|'
|
||||
r'indexed|internal|import|is|mapping|memory|new|payable|public|pragma|'
|
||||
r'private|pure|return|returns|storage|super|this|throw|using|view|while)\b', Keyword, 'slashstartsregex'),
|
||||
(r'(var|function|event|modifier|struct|enum|contract|library|interface)\b', Keyword.Declaration, 'slashstartsregex'),
|
||||
(r'(bytes|string|address|uint|int|bool|byte|' +
|
||||
'|'.join(
|
||||
['uint%d' % (i + 8) for i in range(0, 256, 8)] +
|
||||
['int%d' % (i + 8) for i in range(0, 256, 8)] +
|
||||
['bytes%d' % (i + 1) for i in range(0, 32)] +
|
||||
['ufixed%dx%d' % ((i), (j + 8)) for i in range(0, 256, 8) for j in range(0, 256 - i, 8)] +
|
||||
['fixed%dx%d' % ((i), (j + 8)) for i in range(0, 256, 8) for j in range(0, 256 - i, 8)]
|
||||
) + r')\b', Keyword.Type, 'slashstartsregex'),
|
||||
(r'(wei|szabo|finney|ether|seconds|minutes|hours|days|weeks|years)\b', Keyword.Type, 'slashstartsregex'),
|
||||
(r'(abstract|after|case|catch|default|final|in|inline|let|match|'
|
||||
r'null|of|relocatable|static|switch|try|type|typeof)\b', Keyword.Reserved),
|
||||
(r'(true|false)\b', Keyword.Constant),
|
||||
(r'(block|msg|tx|now|suicide|selfdestruct|addmod|mulmod|sha3|keccak256|log[0-4]|'
|
||||
r'sha256|ecrecover|ripemd160|assert|revert|require)', Name.Builtin),
|
||||
(r'[$a-zA-Z_][a-zA-Z0-9_]*', Name.Other),
|
||||
(r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?', Number.Float),
|
||||
(r'0x[0-9a-fA-F]+', Number.Hex),
|
||||
(r'[0-9]+([eE][0-9]+)?', Number.Integer),
|
||||
(r'"(\\\\|\\"|[^"])*"', String.Double),
|
||||
(r"'(\\\\|\\'|[^'])*'", String.Single),
|
||||
]
|
||||
}
|
@ -1,18 +1,19 @@
|
||||
#################################################
|
||||
Joyfully Universal Language for (Inline) Assembly
|
||||
#################################################
|
||||
###
|
||||
Yul
|
||||
###
|
||||
|
||||
.. _julia:
|
||||
.. _yul:
|
||||
|
||||
.. index:: ! assembly, ! asm, ! evmasm, ! julia
|
||||
.. index:: ! assembly, ! asm, ! evmasm, ! yul, julia, iulia
|
||||
|
||||
JULIA is an intermediate language that can compile to various different backends
|
||||
Yul (previously also called JULIA or IULIA) is an intermediate language that can
|
||||
compile to various different backends
|
||||
(EVM 1.0, EVM 1.5 and eWASM are planned).
|
||||
Because of that, it is designed to be a usable common denominator of all three
|
||||
platforms.
|
||||
It can already be used for "inline assembly" inside Solidity and
|
||||
future versions of the Solidity compiler will even use JULIA as intermediate
|
||||
language. It should also be easy to build high-level optimizer stages for JULIA.
|
||||
future versions of the Solidity compiler will even use Yul as intermediate
|
||||
language. It should also be easy to build high-level optimizer stages for Yul.
|
||||
|
||||
.. note::
|
||||
|
||||
@ -21,14 +22,14 @@ language. It should also be easy to build high-level optimizer stages for JULIA.
|
||||
to the EVM opcodes. Please resort to the inline assembly documentation
|
||||
for details.
|
||||
|
||||
The core components of JULIA are functions, blocks, variables, literals,
|
||||
The core components of Yul are functions, blocks, variables, literals,
|
||||
for-loops, if-statements, switch-statements, expressions and assignments to variables.
|
||||
|
||||
JULIA is typed, both variables and literals must specify the type with postfix
|
||||
Yul is typed, both variables and literals must specify the type with postfix
|
||||
notation. The supported types are ``bool``, ``u8``, ``s8``, ``u32``, ``s32``,
|
||||
``u64``, ``s64``, ``u128``, ``s128``, ``u256`` and ``s256``.
|
||||
|
||||
JULIA in itself does not even provide operators. If the EVM is targeted,
|
||||
Yul in itself does not even provide operators. If the EVM is targeted,
|
||||
opcodes will be available as built-in functions, but they can be reimplemented
|
||||
if the backend changes. For a list of mandatory built-in functions, see the section below.
|
||||
|
||||
@ -43,7 +44,7 @@ and ``mod`` are available either natively or as functions and computes exponenti
|
||||
switch exponent
|
||||
case 0:u256 { result := 1:u256 }
|
||||
case 1:u256 { result := base }
|
||||
default:
|
||||
default
|
||||
{
|
||||
result := power(mul(base, base), div(exponent, 2:u256))
|
||||
switch mod(exponent, 2:u256)
|
||||
@ -69,10 +70,10 @@ and ``add`` to be available.
|
||||
}
|
||||
}
|
||||
|
||||
Specification of JULIA
|
||||
======================
|
||||
Specification of Yul
|
||||
====================
|
||||
|
||||
JULIA code is described in this chapter. JULIA code is usually placed into a JULIA object, which is described in the following chapter.
|
||||
This chapter describes Yul code. It is usually placed inside a Yul object, which is described in the following chapter.
|
||||
|
||||
Grammar::
|
||||
|
||||
@ -82,6 +83,7 @@ Grammar::
|
||||
FunctionDefinition |
|
||||
VariableDeclaration |
|
||||
Assignment |
|
||||
If |
|
||||
Expression |
|
||||
Switch |
|
||||
ForLoop |
|
||||
@ -98,16 +100,18 @@ Grammar::
|
||||
If =
|
||||
'if' Expression Block
|
||||
Switch =
|
||||
'switch' Expression Case* ( 'default' Block )?
|
||||
'switch' Expression ( Case+ Default? | Default )
|
||||
Case =
|
||||
'case' Literal Block
|
||||
Default =
|
||||
'default' Block
|
||||
ForLoop =
|
||||
'for' Block Expression Block Block
|
||||
BreakContinue =
|
||||
'break' | 'continue'
|
||||
FunctionCall =
|
||||
Identifier '(' ( Expression ( ',' Expression )* )? ')'
|
||||
Identifier = [a-zA-Z_$] [a-zA-Z_0-9]*
|
||||
Identifier = [a-zA-Z_$] [a-zA-Z_$0-9]*
|
||||
IdentifierList = Identifier ( ',' Identifier)*
|
||||
TypeName = Identifier | BuiltinTypeName
|
||||
BuiltinTypeName = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )
|
||||
@ -156,7 +160,7 @@ Literals cannot be larger than the their type. The largest type defined is 256-b
|
||||
Scoping Rules
|
||||
-------------
|
||||
|
||||
Scopes in JULIA are tied to Blocks (exceptions are functions and the for loop
|
||||
Scopes in Yul are tied to Blocks (exceptions are functions and the for loop
|
||||
as explained below) and all declarations
|
||||
(``FunctionDefinition``, ``VariableDeclaration``)
|
||||
introduce new identifiers into these scopes.
|
||||
@ -186,7 +190,7 @@ outside of that function.
|
||||
Formal Specification
|
||||
--------------------
|
||||
|
||||
We formally specify JULIA by providing an evaluation function E overloaded
|
||||
We formally specify Yul by providing an evaluation function E overloaded
|
||||
on the various nodes of the AST. Any functions can have side effects, so
|
||||
E takes two state objects and the AST node and returns two new
|
||||
state objects and a variable number of other values.
|
||||
@ -303,7 +307,7 @@ We will use a destructuring notation for the AST nodes.
|
||||
Type Conversion Functions
|
||||
-------------------------
|
||||
|
||||
JULIA has no support for implicit type conversion and therefore functions exists to provide explicit conversion.
|
||||
Yul has no support for implicit type conversion and therefore functions exist to provide explicit conversion.
|
||||
When converting a larger type to a shorter type a runtime exception can occur in case of an overflow.
|
||||
|
||||
Truncating conversions are supported between the following types:
|
||||
@ -337,7 +341,7 @@ The following functions must be available:
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| xor(x:bool, y:bool) -> z:bool | xor |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| *Arithmetics* |
|
||||
| *Arithmetic* |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| addu256(x:u256, y:u256) -> z:u256 | x + y |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
@ -357,9 +361,9 @@ The following functions must be available:
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| expu256(x:u256, y:u256) -> z:u256 | x to the power of y |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetics |
|
||||
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetic |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetics |
|
||||
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetic |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| ltu256(x:u256, y:u256) -> z:bool | true if x < y, false otherwise |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
@ -411,9 +415,15 @@ The following functions must be available:
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| *Execution control* |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei |
|
||||
| create(v:u256, p:u256, n:u256) | create new contract with code mem[p..(p+n)) and send v wei |
|
||||
| | and return the new address |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| create2(v:u256, p:u256, n:u256, s:u256) | create new contract with code mem[p...(p+n)) at address |
|
||||
| | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) |
|
||||
| | and send v wei and return the new address, where ``0xff`` is a |
|
||||
| | 8 byte value, ``this`` is the current contract's address |
|
||||
| | as a 20 byte value and ``s`` is a big-endian 256-bit value |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) |
|
||||
| insize:u256, out:u256, | providing g gas and v wei and output area |
|
||||
| outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
|
||||
@ -489,6 +499,8 @@ The following functions must be available:
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| extcodehash(a:u256) | code hash of address a |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| *Others* |
|
||||
+---------------------------------------------+-----------------------------------------------------------------+
|
||||
| discard(unused:bool) | discard value |
|
||||
@ -507,7 +519,7 @@ The following functions must be available:
|
||||
Backends
|
||||
--------
|
||||
|
||||
Backends or targets are the translators from JULIA to a specific bytecode. Each of the backends can expose functions
|
||||
Backends or targets are the translators from Yul to a specific bytecode. Each of the backends can expose functions
|
||||
prefixed with the name of the backend. We reserve ``evm_`` and ``ewasm_`` prefixes for the two proposed backends.
|
||||
|
||||
Backend: EVM
|
||||
@ -525,8 +537,8 @@ Backend: eWASM
|
||||
|
||||
TBD
|
||||
|
||||
Specification of JULIA Object
|
||||
=============================
|
||||
Specification of Yul Object
|
||||
===========================
|
||||
|
||||
Grammar::
|
||||
|
||||
@ -537,11 +549,11 @@ Grammar::
|
||||
HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
|
||||
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
|
||||
|
||||
Above, ``Block`` refers to ``Block`` in the JULIA code grammar explained in the previous chapter.
|
||||
Above, ``Block`` refers to ``Block`` in the Yul code grammar explained in the previous chapter.
|
||||
|
||||
An example JULIA Object is shown below:
|
||||
An example Yul Object is shown below:
|
||||
|
||||
..code::
|
||||
.. code::
|
||||
|
||||
// Code consists of a single object. A single "code" node is the code of the object.
|
||||
// Every (other) named object or data section is serialized and
|
@ -32,11 +32,13 @@ template <typename V>
|
||||
class CycleDetector
|
||||
{
|
||||
public:
|
||||
using Visitor = std::function<void(V const&, CycleDetector&, size_t)>;
|
||||
|
||||
/// Initializes the cycle detector
|
||||
/// @param _visit function that is given the current vertex
|
||||
/// and is supposed to call @a run on all
|
||||
/// adjacent vertices.
|
||||
explicit CycleDetector(std::function<void(V const&, CycleDetector&)> _visit):
|
||||
explicit CycleDetector(Visitor _visit):
|
||||
m_visit(std::move(_visit))
|
||||
{ }
|
||||
|
||||
@ -55,7 +57,7 @@ public:
|
||||
m_processing.insert(&_vertex);
|
||||
|
||||
m_depth++;
|
||||
m_visit(_vertex, *this);
|
||||
m_visit(_vertex, *this, m_depth);
|
||||
m_depth--;
|
||||
if (m_firstCycleVertex && m_depth == 1)
|
||||
m_firstCycleVertex = &_vertex;
|
||||
@ -66,7 +68,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(V const&, CycleDetector&)> m_visit;
|
||||
Visitor m_visit;
|
||||
std::set<V const*> m_processing;
|
||||
std::set<V const*> m_processed;
|
||||
size_t m_depth = 0;
|
||||
|
@ -40,7 +40,6 @@
|
||||
#include <libdevcore/vector_ref.h>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma warning(push)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif // defined(__GNUC__)
|
||||
@ -57,7 +56,6 @@
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma warning(pop)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif // defined(__GNUC__)
|
||||
|
||||
@ -66,15 +64,13 @@
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
using byte = uint8_t;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
// Binary data types.
|
||||
using bytes = std::vector<byte>;
|
||||
using bytesRef = vector_ref<byte>;
|
||||
using bytesConstRef = vector_ref<byte const>;
|
||||
using bytes = std::vector<uint8_t>;
|
||||
using bytesRef = vector_ref<uint8_t>;
|
||||
using bytesConstRef = vector_ref<uint8_t const>;
|
||||
|
||||
// Numeric types.
|
||||
using bigint = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<>>;
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <libdevcore/CommonData.h>
|
||||
#include <libdevcore/Exceptions.h>
|
||||
#include <libdevcore/Assertions.h>
|
||||
#include <libdevcore/SHA3.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@ -64,7 +64,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw)
|
||||
int h = fromHex(_s[i], WhenError::DontThrow);
|
||||
int l = fromHex(_s[i + 1], WhenError::DontThrow);
|
||||
if (h != -1 && l != -1)
|
||||
ret.push_back((byte)(h * 16 + l));
|
||||
ret.push_back((uint8_t)(h * 16 + l));
|
||||
else if (_throw == WhenError::Throw)
|
||||
BOOST_THROW_EXCEPTION(BadHexCharacter());
|
||||
else
|
||||
@ -76,18 +76,18 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw)
|
||||
|
||||
bool dev::passesAddressChecksum(string const& _str, bool _strict)
|
||||
{
|
||||
string s = _str.substr(0, 2) == "0x" ? _str.substr(2) : _str;
|
||||
string s = _str.substr(0, 2) == "0x" ? _str : "0x" + _str;
|
||||
|
||||
if (s.length() != 40)
|
||||
if (s.length() != 42)
|
||||
return false;
|
||||
|
||||
if (!_strict && (
|
||||
_str.find_first_of("abcdef") == string::npos ||
|
||||
_str.find_first_of("ABCDEF") == string::npos
|
||||
s.find_first_of("abcdef") == string::npos ||
|
||||
s.find_first_of("ABCDEF") == string::npos
|
||||
))
|
||||
return true;
|
||||
|
||||
return _str == dev::getChecksummedAddress(_str);
|
||||
return s == dev::getChecksummedAddress(s);
|
||||
}
|
||||
|
||||
string dev::getChecksummedAddress(string const& _addr)
|
||||
@ -110,3 +110,26 @@ string dev::getChecksummedAddress(string const& _addr)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool dev::isValidHex(string const& _string)
|
||||
{
|
||||
if (_string.substr(0, 2) != "0x")
|
||||
return false;
|
||||
if (_string.find_first_not_of("0123456789abcdefABCDEF", 2) != string::npos)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dev::isValidDecimal(string const& _string)
|
||||
{
|
||||
if (_string.empty())
|
||||
return false;
|
||||
if (_string == "0")
|
||||
return true;
|
||||
// No leading zeros
|
||||
if (_string.front() == '0')
|
||||
return false;
|
||||
if (_string.find_first_not_of("0123456789") != string::npos)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -25,11 +25,14 @@
|
||||
|
||||
#include <libdevcore/Common.h>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <functional>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -85,7 +88,7 @@ inline std::string asString(bytesConstRef _b)
|
||||
/// Converts a string to a byte array containing the string's (byte) data.
|
||||
inline bytes asBytes(std::string const& _b)
|
||||
{
|
||||
return bytes((byte const*)_b.data(), (byte const*)(_b.data() + _b.size()));
|
||||
return bytes((uint8_t const*)_b.data(), (uint8_t const*)(_b.data() + _b.size()));
|
||||
}
|
||||
|
||||
// Big-endian to/from host endian conversion functions.
|
||||
@ -114,7 +117,7 @@ inline T fromBigEndian(_In const& _bytes)
|
||||
{
|
||||
T ret = (T)0;
|
||||
for (auto i: _bytes)
|
||||
ret = (T)((ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i);
|
||||
ret = (T)((ret << 8) | (uint8_t)(typename std::make_unsigned<typename _In::value_type>::type)i);
|
||||
return ret;
|
||||
}
|
||||
inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; }
|
||||
@ -132,7 +135,7 @@ inline bytes toCompactBigEndian(T _val, unsigned _min = 0)
|
||||
toBigEndian(_val, ret);
|
||||
return ret;
|
||||
}
|
||||
inline bytes toCompactBigEndian(byte _val, unsigned _min = 0)
|
||||
inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0)
|
||||
{
|
||||
return (_min || _val) ? bytes{ _val } : bytes{};
|
||||
}
|
||||
@ -229,6 +232,37 @@ bool contains(T const& _t, V const& _v)
|
||||
return std::end(_t) != std::find(std::begin(_t), std::end(_t), _v);
|
||||
}
|
||||
|
||||
|
||||
/// Function that iterates over a vector, calling a function on each of its
|
||||
/// elements. If that function returns a vector, the element is replaced by
|
||||
/// the returned vector. During the iteration, the original vector is only valid
|
||||
/// on the current element and after that. The actual replacement takes
|
||||
/// place at the end, but already visited elements might be invalidated.
|
||||
/// If nothing is replaced, no copy is performed.
|
||||
template <typename T, typename F>
|
||||
void iterateReplacing(std::vector<T>& _vector, const F& _f)
|
||||
{
|
||||
// Concept: _f must be Callable, must accept param T&, must return optional<vector<T>>
|
||||
bool useModified = false;
|
||||
std::vector<T> modifiedVector;
|
||||
for (size_t i = 0; i < _vector.size(); ++i)
|
||||
{
|
||||
if (boost::optional<std::vector<T>> r = _f(_vector[i]))
|
||||
{
|
||||
if (!useModified)
|
||||
{
|
||||
std::move(_vector.begin(), _vector.begin() + i, back_inserter(modifiedVector));
|
||||
useModified = true;
|
||||
}
|
||||
modifiedVector += std::move(*r);
|
||||
}
|
||||
else if (useModified)
|
||||
modifiedVector.emplace_back(std::move(_vector[i]));
|
||||
}
|
||||
if (useModified)
|
||||
_vector = std::move(modifiedVector);
|
||||
}
|
||||
|
||||
/// @returns true iff @a _str passess the hex address checksum test.
|
||||
/// @param _strict if false, hex strings with only uppercase or only lowercase letters
|
||||
/// are considered valid.
|
||||
@ -238,4 +272,7 @@ bool passesAddressChecksum(std::string const& _str, bool _strict);
|
||||
/// @param hex strings that look like an address
|
||||
std::string getChecksummedAddress(std::string const& _addr);
|
||||
|
||||
bool isValidHex(std::string const& _string);
|
||||
bool isValidDecimal(std::string const& _string);
|
||||
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <stdio.h>
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#else
|
||||
@ -81,45 +80,6 @@ string dev::readStandardInput()
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
if (_writeDeleteRename)
|
||||
{
|
||||
fs::path tempPath = fs::unique_path(_file + "-%%%%%%");
|
||||
writeFile(tempPath.string(), _data, false);
|
||||
// will delete _file if it exists
|
||||
fs::rename(tempPath, _file);
|
||||
}
|
||||
else
|
||||
{
|
||||
// create directory if not existent
|
||||
fs::path p(_file);
|
||||
if (!fs::exists(p.parent_path()))
|
||||
{
|
||||
fs::create_directories(p.parent_path());
|
||||
try
|
||||
{
|
||||
fs::permissions(p.parent_path(), fs::owner_all);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
ofstream s(_file, ios::trunc | ios::binary);
|
||||
s.write(reinterpret_cast<char const*>(_data.data()), _data.size());
|
||||
assertThrow(s, FileError, "Could not write to file: " + _file);
|
||||
try
|
||||
{
|
||||
fs::permissions(_file, fs::owner_read|fs::owner_write);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
class DisableConsoleBuffering
|
||||
{
|
||||
@ -187,3 +147,23 @@ boost::filesystem::path dev::weaklyCanonicalFilesystemPath(boost::filesystem::pa
|
||||
return head / tail;
|
||||
}
|
||||
}
|
||||
|
||||
string dev::absolutePath(string const& _path, string const& _reference)
|
||||
{
|
||||
boost::filesystem::path p(_path);
|
||||
// Anything that does not start with `.` is an absolute path.
|
||||
if (p.begin() == p.end() || (*p.begin() != "." && *p.begin() != ".."))
|
||||
return _path;
|
||||
boost::filesystem::path result(_reference);
|
||||
result.remove_filename();
|
||||
for (boost::filesystem::path::iterator it = p.begin(); it != p.end(); ++it)
|
||||
if (*it == "..")
|
||||
result = result.parent_path();
|
||||
else if (*it != ".")
|
||||
result /= *it;
|
||||
return result.generic_string();
|
||||
}
|
||||
|
||||
string dev::sanitizePath(string const& _path) {
|
||||
return boost::filesystem::path(_path).generic_string();
|
||||
}
|
||||
|
@ -41,14 +41,6 @@ std::string readStandardInput();
|
||||
/// Retrieve and returns a character from standard input (without waiting for EOL).
|
||||
int readStandardInputChar();
|
||||
|
||||
/// Write the given binary data into the given file, replacing the file if it pre-exists.
|
||||
/// Throws exception on error.
|
||||
/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in
|
||||
/// the same directory and then moves that file.
|
||||
void writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename = false);
|
||||
/// Write the given binary data into the given file, replacing the file if it pre-exists.
|
||||
inline void writeFile(std::string const& _file, bytes const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(&_data), _writeDeleteRename); }
|
||||
inline void writeFile(std::string const& _file, std::string const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); }
|
||||
/// Converts arbitrary value to string representation using std::stringstream.
|
||||
template <class _T>
|
||||
std::string toString(_T const& _t)
|
||||
@ -62,4 +54,10 @@ std::string toString(_T const& _t)
|
||||
/// Should be replaced by the boost implementation as soon as support for boost<1.60 can be dropped.
|
||||
boost::filesystem::path weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path);
|
||||
|
||||
/// @returns the absolute path corresponding to @a _path relative to @a _reference.
|
||||
std::string absolutePath(std::string const& _path, std::string const& _reference);
|
||||
|
||||
/// Helper function to return path converted strings.
|
||||
std::string sanitizePath(std::string const& _path);
|
||||
|
||||
}
|
||||
|
@ -17,8 +17,6 @@
|
||||
|
||||
#include <libdevcore/Exceptions.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
|
||||
@ -27,7 +25,7 @@ char const* Exception::what() const noexcept
|
||||
// Return the comment if available.
|
||||
if (string const* cmt = comment())
|
||||
return cmt->data();
|
||||
|
||||
|
||||
// Fallback to base what().
|
||||
// Boost accepts nullptr, but the C++ standard doesn't
|
||||
// and crashes on some platforms.
|
||||
@ -43,7 +41,7 @@ string Exception::lineInfo() const
|
||||
ret += *file;
|
||||
ret += ':';
|
||||
if (line)
|
||||
ret += boost::lexical_cast<string>(*line);
|
||||
ret += to_string(*line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ public:
|
||||
operator Arith() const { return fromBigEndian<Arith>(m_data); }
|
||||
|
||||
/// @returns true iff this is the empty hash.
|
||||
explicit operator bool() const { return std::any_of(m_data.begin(), m_data.end(), [](byte _b) { return _b != 0; }); }
|
||||
explicit operator bool() const { return std::any_of(m_data.begin(), m_data.end(), [](uint8_t _b) { return _b != 0; }); }
|
||||
|
||||
// The obvious comparison operators.
|
||||
bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; }
|
||||
@ -90,9 +90,9 @@ public:
|
||||
FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; }
|
||||
|
||||
/// @returns a particular byte from the hash.
|
||||
byte& operator[](unsigned _i) { return m_data[_i]; }
|
||||
uint8_t& operator[](unsigned _i) { return m_data[_i]; }
|
||||
/// @returns a particular byte from the hash.
|
||||
byte operator[](unsigned _i) const { return m_data[_i]; }
|
||||
uint8_t operator[](unsigned _i) const { return m_data[_i]; }
|
||||
|
||||
/// @returns the hash as a user-readable hex string.
|
||||
std::string hex() const { return toHex(ref()); }
|
||||
@ -104,19 +104,19 @@ public:
|
||||
bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); }
|
||||
|
||||
/// @returns a mutable byte pointer to the object's data.
|
||||
byte* data() { return m_data.data(); }
|
||||
uint8_t* data() { return m_data.data(); }
|
||||
|
||||
/// @returns a constant byte pointer to the object's data.
|
||||
byte const* data() const { return m_data.data(); }
|
||||
uint8_t const* data() const { return m_data.data(); }
|
||||
|
||||
/// @returns a copy of the object's data as a byte vector.
|
||||
bytes asBytes() const { return bytes(data(), data() + N); }
|
||||
|
||||
/// @returns a mutable reference to the object's data as an STL array.
|
||||
std::array<byte, N>& asArray() { return m_data; }
|
||||
std::array<uint8_t, N>& asArray() { return m_data; }
|
||||
|
||||
/// @returns a constant reference to the object's data as an STL array.
|
||||
std::array<byte, N> const& asArray() const { return m_data; }
|
||||
std::array<uint8_t, N> const& asArray() const { return m_data; }
|
||||
|
||||
/// Returns the index of the first bit set to one, or size() * 8 if no bits are set.
|
||||
inline unsigned firstBitSet() const
|
||||
@ -137,7 +137,7 @@ public:
|
||||
void clear() { m_data.fill(0); }
|
||||
|
||||
private:
|
||||
std::array<byte, N> m_data; ///< The binary data.
|
||||
std::array<uint8_t, N> m_data; ///< The binary data.
|
||||
};
|
||||
|
||||
/// Stream I/O for the FixedHash class.
|
||||
|
186
libdevcore/Keccak256.cpp
Normal file
186
libdevcore/Keccak256.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/** @file SHA3.cpp
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/** libkeccak-tiny
|
||||
*
|
||||
* A single-file implementation of SHA-3 and SHAKE.
|
||||
*
|
||||
* Implementor: David Leon Gil
|
||||
* License: CC0, attribution kindly requested. Blame taken too,
|
||||
* but not liability.
|
||||
*/
|
||||
|
||||
/******** The Keccak-f[1600] permutation ********/
|
||||
|
||||
/*** Constants. ***/
|
||||
static const uint8_t rho[24] = \
|
||||
{ 1, 3, 6, 10, 15, 21,
|
||||
28, 36, 45, 55, 2, 14,
|
||||
27, 41, 56, 8, 25, 43,
|
||||
62, 18, 39, 61, 20, 44};
|
||||
static const uint8_t pi[24] = \
|
||||
{10, 7, 11, 17, 18, 3,
|
||||
5, 16, 8, 21, 24, 4,
|
||||
15, 23, 19, 13, 12, 2,
|
||||
20, 14, 22, 9, 6, 1};
|
||||
static const uint64_t RC[24] = \
|
||||
{1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
|
||||
0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
|
||||
0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL,
|
||||
0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
|
||||
0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL,
|
||||
0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL};
|
||||
|
||||
/*** Helper macros to unroll the permutation. ***/
|
||||
#define rol(x, s) (((x) << s) | ((x) >> (64 - s)))
|
||||
#define REPEAT6(e) e e e e e e
|
||||
#define REPEAT24(e) REPEAT6(e e e e)
|
||||
#define REPEAT5(e) e e e e e
|
||||
#define FOR5(v, s, e) \
|
||||
v = 0; \
|
||||
REPEAT5(e; v += s;)
|
||||
|
||||
/*** Keccak-f[1600] ***/
|
||||
static inline void keccakf(void* state) {
|
||||
uint64_t* a = (uint64_t*)state;
|
||||
uint64_t b[5] = {0};
|
||||
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
uint8_t x, y;
|
||||
// Theta
|
||||
FOR5(x, 1,
|
||||
b[x] = 0;
|
||||
FOR5(y, 5,
|
||||
b[x] ^= a[x + y]; ))
|
||||
FOR5(x, 1,
|
||||
FOR5(y, 5,
|
||||
a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); ))
|
||||
// Rho and pi
|
||||
uint64_t t = a[1];
|
||||
x = 0;
|
||||
REPEAT24(b[0] = a[pi[x]];
|
||||
a[pi[x]] = rol(t, rho[x]);
|
||||
t = b[0];
|
||||
x++; )
|
||||
// Chi
|
||||
FOR5(y,
|
||||
5,
|
||||
FOR5(x, 1,
|
||||
b[x] = a[y + x];)
|
||||
FOR5(x, 1,
|
||||
a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); ))
|
||||
// Iota
|
||||
a[0] ^= RC[i];
|
||||
}
|
||||
}
|
||||
|
||||
/******** The FIPS202-defined functions. ********/
|
||||
|
||||
/*** Some helper macros. ***/
|
||||
|
||||
#define _(S) do { S } while (0)
|
||||
#define FOR(i, ST, L, S) \
|
||||
_(for (size_t i = 0; i < L; i += ST) { S; })
|
||||
#define mkapply_ds(NAME, S) \
|
||||
static inline void NAME(uint8_t* dst, \
|
||||
const uint8_t* src, \
|
||||
size_t len) { \
|
||||
FOR(i, 1, len, S); \
|
||||
}
|
||||
#define mkapply_sd(NAME, S) \
|
||||
static inline void NAME(const uint8_t* src, \
|
||||
uint8_t* dst, \
|
||||
size_t len) { \
|
||||
FOR(i, 1, len, S); \
|
||||
}
|
||||
|
||||
mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
|
||||
mkapply_sd(setout, dst[i] = src[i]) // setout
|
||||
|
||||
#define P keccakf
|
||||
#define Plen 200
|
||||
|
||||
// Fold P*F over the full blocks of an input.
|
||||
#define foldP(I, L, F) \
|
||||
while (L >= rate) { \
|
||||
F(a, I, rate); \
|
||||
P(a); \
|
||||
I += rate; \
|
||||
L -= rate; \
|
||||
}
|
||||
|
||||
/** The sponge-based hash construction. **/
|
||||
inline void hash(
|
||||
uint8_t* out,
|
||||
size_t outlen,
|
||||
const uint8_t* in,
|
||||
size_t inlen,
|
||||
size_t rate,
|
||||
uint8_t delim
|
||||
)
|
||||
{
|
||||
uint8_t a[Plen] = {0};
|
||||
// Absorb input.
|
||||
foldP(in, inlen, xorin);
|
||||
// Xor in the DS and pad frame.
|
||||
a[inlen] ^= delim;
|
||||
a[rate - 1] ^= 0x80;
|
||||
// Xor in the last block.
|
||||
xorin(a, in, inlen);
|
||||
// Apply P
|
||||
P(a);
|
||||
// Squeeze output.
|
||||
foldP(out, outlen, setout);
|
||||
setout(a, out, outlen);
|
||||
memset(a, 0, 200);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
h256 keccak256(bytesConstRef _input)
|
||||
{
|
||||
h256 output;
|
||||
// Parameters used:
|
||||
// The 0x01 is the specific padding for keccak (sha3 uses 0x06) and
|
||||
// the way the round size (or window or whatever it was) is calculated.
|
||||
// 200 - (256 / 4) is the "rate"
|
||||
hash(output.data(), output.size, _input.data(), _input.size(), 200 - (256 / 4), 0x01);
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
@ -30,14 +30,8 @@
|
||||
namespace dev
|
||||
{
|
||||
|
||||
// Keccak-256 convenience routines.
|
||||
|
||||
/// Calculate Keccak-256 hash of the given input and load it into the given output.
|
||||
/// @returns false if o_output.size() != 32.
|
||||
bool keccak256(bytesConstRef _input, bytesRef o_output);
|
||||
|
||||
/// Calculate Keccak-256 hash of the given input, returning as a 256-bit hash.
|
||||
inline h256 keccak256(bytesConstRef _input) { h256 ret; keccak256(_input, ret.ref()); return ret; }
|
||||
h256 keccak256(bytesConstRef _input);
|
||||
|
||||
/// Calculate Keccak-256 hash of the given input, returning as a 256-bit hash.
|
||||
inline h256 keccak256(bytes const& _input) { return keccak256(bytesConstRef(&_input)); }
|
@ -1,240 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/** @file SHA3.cpp
|
||||
* @author Gav Wood <i@gavwood.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
#include "SHA3.h"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
namespace keccak
|
||||
{
|
||||
|
||||
/** libkeccak-tiny
|
||||
*
|
||||
* A single-file implementation of SHA-3 and SHAKE.
|
||||
*
|
||||
* Implementor: David Leon Gil
|
||||
* License: CC0, attribution kindly requested. Blame taken too,
|
||||
* but not liability.
|
||||
*/
|
||||
|
||||
#define decshake(bits) \
|
||||
int shake##bits(uint8_t*, size_t, const uint8_t*, size_t);
|
||||
|
||||
#define decsha3(bits) \
|
||||
int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t);
|
||||
|
||||
#define deckeccak(bits) \
|
||||
int keccak##bits(uint8_t*, size_t, const uint8_t*, size_t);
|
||||
|
||||
decshake(128)
|
||||
decshake(256)
|
||||
decsha3(224)
|
||||
decsha3(256)
|
||||
decsha3(384)
|
||||
decsha3(512)
|
||||
deckeccak(224)
|
||||
deckeccak(256)
|
||||
deckeccak(384)
|
||||
deckeccak(512)
|
||||
|
||||
/******** The Keccak-f[1600] permutation ********/
|
||||
|
||||
/*** Constants. ***/
|
||||
static const uint8_t rho[24] = \
|
||||
{ 1, 3, 6, 10, 15, 21,
|
||||
28, 36, 45, 55, 2, 14,
|
||||
27, 41, 56, 8, 25, 43,
|
||||
62, 18, 39, 61, 20, 44};
|
||||
static const uint8_t pi[24] = \
|
||||
{10, 7, 11, 17, 18, 3,
|
||||
5, 16, 8, 21, 24, 4,
|
||||
15, 23, 19, 13, 12, 2,
|
||||
20, 14, 22, 9, 6, 1};
|
||||
static const uint64_t RC[24] = \
|
||||
{1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
|
||||
0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
|
||||
0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL,
|
||||
0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
|
||||
0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL,
|
||||
0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL};
|
||||
|
||||
/*** Helper macros to unroll the permutation. ***/
|
||||
#define rol(x, s) (((x) << s) | ((x) >> (64 - s)))
|
||||
#define REPEAT6(e) e e e e e e
|
||||
#define REPEAT24(e) REPEAT6(e e e e)
|
||||
#define REPEAT5(e) e e e e e
|
||||
#define FOR5(v, s, e) \
|
||||
v = 0; \
|
||||
REPEAT5(e; v += s;)
|
||||
|
||||
/*** Keccak-f[1600] ***/
|
||||
static inline void keccakf(void* state) {
|
||||
uint64_t* a = (uint64_t*)state;
|
||||
uint64_t b[5] = {0};
|
||||
|
||||
for (int i = 0; i < 24; i++) {
|
||||
uint8_t x, y;
|
||||
// Theta
|
||||
FOR5(x, 1,
|
||||
b[x] = 0;
|
||||
FOR5(y, 5,
|
||||
b[x] ^= a[x + y]; ))
|
||||
FOR5(x, 1,
|
||||
FOR5(y, 5,
|
||||
a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); ))
|
||||
// Rho and pi
|
||||
uint64_t t = a[1];
|
||||
x = 0;
|
||||
REPEAT24(b[0] = a[pi[x]];
|
||||
a[pi[x]] = rol(t, rho[x]);
|
||||
t = b[0];
|
||||
x++; )
|
||||
// Chi
|
||||
FOR5(y,
|
||||
5,
|
||||
FOR5(x, 1,
|
||||
b[x] = a[y + x];)
|
||||
FOR5(x, 1,
|
||||
a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); ))
|
||||
// Iota
|
||||
a[0] ^= RC[i];
|
||||
}
|
||||
}
|
||||
|
||||
/******** The FIPS202-defined functions. ********/
|
||||
|
||||
/*** Some helper macros. ***/
|
||||
|
||||
#define _(S) do { S } while (0)
|
||||
#define FOR(i, ST, L, S) \
|
||||
_(for (size_t i = 0; i < L; i += ST) { S; })
|
||||
#define mkapply_ds(NAME, S) \
|
||||
static inline void NAME(uint8_t* dst, \
|
||||
const uint8_t* src, \
|
||||
size_t len) { \
|
||||
FOR(i, 1, len, S); \
|
||||
}
|
||||
#define mkapply_sd(NAME, S) \
|
||||
static inline void NAME(const uint8_t* src, \
|
||||
uint8_t* dst, \
|
||||
size_t len) { \
|
||||
FOR(i, 1, len, S); \
|
||||
}
|
||||
|
||||
mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
|
||||
mkapply_sd(setout, dst[i] = src[i]) // setout
|
||||
|
||||
#define P keccakf
|
||||
#define Plen 200
|
||||
|
||||
// Fold P*F over the full blocks of an input.
|
||||
#define foldP(I, L, F) \
|
||||
while (L >= rate) { \
|
||||
F(a, I, rate); \
|
||||
P(a); \
|
||||
I += rate; \
|
||||
L -= rate; \
|
||||
}
|
||||
|
||||
/** The sponge-based hash construction. **/
|
||||
static inline int hash(uint8_t* out, size_t outlen,
|
||||
const uint8_t* in, size_t inlen,
|
||||
size_t rate, uint8_t delim) {
|
||||
if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t a[Plen] = {0};
|
||||
// Absorb input.
|
||||
foldP(in, inlen, xorin);
|
||||
// Xor in the DS and pad frame.
|
||||
a[inlen] ^= delim;
|
||||
a[rate - 1] ^= 0x80;
|
||||
// Xor in the last block.
|
||||
xorin(a, in, inlen);
|
||||
// Apply P
|
||||
P(a);
|
||||
// Squeeze output.
|
||||
foldP(out, outlen, setout);
|
||||
setout(a, out, outlen);
|
||||
memset(a, 0, 200);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*** Helper macros to define SHA3 and SHAKE instances. ***/
|
||||
#define defshake(bits) \
|
||||
int shake##bits(uint8_t* out, size_t outlen, \
|
||||
const uint8_t* in, size_t inlen) { \
|
||||
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x1f); \
|
||||
}
|
||||
#define defsha3(bits) \
|
||||
int sha3_##bits(uint8_t* out, size_t outlen, \
|
||||
const uint8_t* in, size_t inlen) { \
|
||||
if (outlen > (bits/8)) { \
|
||||
return -1; \
|
||||
} \
|
||||
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x06); \
|
||||
}
|
||||
#define defkeccak(bits) \
|
||||
int keccak##bits(uint8_t* out, size_t outlen, \
|
||||
const uint8_t* in, size_t inlen) { \
|
||||
if (outlen > (bits/8)) { \
|
||||
return -1; \
|
||||
} \
|
||||
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \
|
||||
}
|
||||
|
||||
/*** FIPS202 SHAKE VOFs ***/
|
||||
defshake(128)
|
||||
defshake(256)
|
||||
|
||||
/*** FIPS202 SHA3 FOFs ***/
|
||||
defsha3(224)
|
||||
defsha3(256)
|
||||
defsha3(384)
|
||||
defsha3(512)
|
||||
|
||||
/*** KECCAK FOFs ***/
|
||||
defkeccak(224)
|
||||
defkeccak(256)
|
||||
defkeccak(384)
|
||||
defkeccak(512)
|
||||
|
||||
}
|
||||
|
||||
bool keccak256(bytesConstRef _input, bytesRef o_output)
|
||||
{
|
||||
// FIXME: What with unaligned memory?
|
||||
if (o_output.size() != 32)
|
||||
return false;
|
||||
keccak::keccak256(o_output.data(), 32, _input.data(), _input.size());
|
||||
// keccak::keccak(ret.data(), 32, (uint64_t const*)_input.data(), _input.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -29,13 +29,16 @@
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
|
||||
bool dev::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance)
|
||||
bool dev::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance, size_t _lenThreshold)
|
||||
{
|
||||
if (_str1 == _str2)
|
||||
return true;
|
||||
|
||||
size_t n1 = _str1.size();
|
||||
size_t n2 = _str2.size();
|
||||
if (_lenThreshold > 0 && n1 * n2 > _lenThreshold)
|
||||
return false;
|
||||
|
||||
size_t distance = stringDistance(_str1, _str2);
|
||||
|
||||
// if distance is not greater than _maxDistance, and distance is strictly less than length of both names, they can be considered similar
|
||||
@ -85,17 +88,11 @@ size_t dev::stringDistance(string const& _str1, string const& _str2)
|
||||
|
||||
string dev::quotedAlternativesList(vector<string> const& suggestions)
|
||||
{
|
||||
if (suggestions.empty())
|
||||
return "";
|
||||
if (suggestions.size() == 1)
|
||||
return "\"" + suggestions.front() + "\"";
|
||||
vector<string> quotedSuggestions;
|
||||
|
||||
string choices = "\"" + suggestions.front() + "\"";
|
||||
for (size_t i = 1; i + 1 < suggestions.size(); ++i)
|
||||
choices += ", \"" + suggestions[i] + "\"";
|
||||
for (auto& suggestion: suggestions)
|
||||
quotedSuggestions.push_back("\"" + suggestion + "\"");
|
||||
|
||||
choices += " or \"" + suggestions.back() + "\"";
|
||||
|
||||
return choices;
|
||||
return joinHumanReadable(quotedSuggestions, ", ", " or ");
|
||||
}
|
||||
|
||||
|
@ -30,10 +30,46 @@ namespace dev
|
||||
{
|
||||
|
||||
// Calculates the Damerau–Levenshtein distance between _str1 and _str2 and returns true if that distance is not greater than _maxDistance
|
||||
bool stringWithinDistance(std::string const& _str1, std::string const& _str2, size_t _maxDistance);
|
||||
// if _lenThreshold > 0 and the product of the strings length is greater than _lenThreshold, the function will return false
|
||||
bool stringWithinDistance(std::string const& _str1, std::string const& _str2, size_t _maxDistance, size_t _lenThreshold = 0);
|
||||
// Calculates the Damerau–Levenshtein distance between _str1 and _str2
|
||||
size_t stringDistance(std::string const& _str1, std::string const& _str2);
|
||||
// Return a string having elements of suggestions as quoted, alternative suggestions. e.g. "a", "b" or "c"
|
||||
std::string quotedAlternativesList(std::vector<std::string> const& suggestions);
|
||||
|
||||
/// Joins collection of strings into one string with separators between, last separator can be different.
|
||||
/// @param _list collection of strings to join
|
||||
/// @param _separator defaults to ", "
|
||||
/// @param _lastSeparator (optional) will be used to separate last two strings instead of _separator
|
||||
/// @example join(vector<string>{"a", "b", "c"}, "; ", " or ") == "a; b or c"
|
||||
template<class T>
|
||||
std::string joinHumanReadable
|
||||
(
|
||||
T const& _list,
|
||||
std::string const& _separator = ", ",
|
||||
std::string const& _lastSeparator = ""
|
||||
)
|
||||
{
|
||||
auto const itEnd = end(_list);
|
||||
|
||||
std::string result;
|
||||
|
||||
for (auto it = begin(_list); it != itEnd; )
|
||||
{
|
||||
std::string element = *it;
|
||||
bool first = (it == begin(_list));
|
||||
++it;
|
||||
if (!first)
|
||||
{
|
||||
if (it == itEnd && !_lastSeparator.empty())
|
||||
result += _lastSeparator; // last iteration
|
||||
else
|
||||
result += _separator;
|
||||
}
|
||||
result += std::move(element);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include <libdevcore/SwarmHash.h>
|
||||
|
||||
#include <libdevcore/SHA3.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
|
128
libdevcore/Visitor.h
Normal file
128
libdevcore/Visitor.h
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/**
|
||||
* Visitor templates.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <boost/variant/static_visitor.hpp>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
/// Generic visitor used as follows:
|
||||
/// boost::apply_visitor(GenericVisitor<Class1, Class2>(
|
||||
/// [](Class1& _c) { _c.f(); },
|
||||
/// [](Class2& _c) { _c.g(); }
|
||||
/// ), variant);
|
||||
/// This one does not have a fallback and will fail at
|
||||
/// compile-time if you do not specify all variants.
|
||||
|
||||
template <class...>
|
||||
struct GenericVisitor{};
|
||||
|
||||
template <class Visitable, class... Others>
|
||||
struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...>
|
||||
{
|
||||
using GenericVisitor<Others...>::operator ();
|
||||
explicit GenericVisitor(
|
||||
std::function<void(Visitable&)> _visitor,
|
||||
std::function<void(Others&)>... _otherVisitors
|
||||
):
|
||||
GenericVisitor<Others...>(std::move(_otherVisitors)...),
|
||||
m_visitor(std::move(_visitor))
|
||||
{}
|
||||
|
||||
void operator()(Visitable& _v) const { m_visitor(_v); }
|
||||
|
||||
std::function<void(Visitable&)> m_visitor;
|
||||
};
|
||||
template <>
|
||||
struct GenericVisitor<>: public boost::static_visitor<> {
|
||||
void operator()() const {}
|
||||
};
|
||||
|
||||
/// Generic visitor with fallback:
|
||||
/// boost::apply_visitor(GenericFallbackVisitor<Class1, Class2>(
|
||||
/// [](Class1& _c) { _c.f(); },
|
||||
/// [](Class2& _c) { _c.g(); }
|
||||
/// ), variant);
|
||||
/// This one DOES have a fallback and will NOT fail at
|
||||
/// compile-time if you do not specify all variants.
|
||||
|
||||
template <class...>
|
||||
struct GenericFallbackVisitor{};
|
||||
|
||||
template <class Visitable, class... Others>
|
||||
struct GenericFallbackVisitor<Visitable, Others...>: public GenericFallbackVisitor<Others...>
|
||||
{
|
||||
explicit GenericFallbackVisitor(
|
||||
std::function<void(Visitable&)> _visitor,
|
||||
std::function<void(Others&)>... _otherVisitors
|
||||
):
|
||||
GenericFallbackVisitor<Others...>(std::move(_otherVisitors)...),
|
||||
m_visitor(std::move(_visitor))
|
||||
{}
|
||||
|
||||
using GenericFallbackVisitor<Others...>::operator ();
|
||||
void operator()(Visitable& _v) const { m_visitor(_v); }
|
||||
|
||||
std::function<void(Visitable&)> m_visitor;
|
||||
};
|
||||
template <>
|
||||
struct GenericFallbackVisitor<>: public boost::static_visitor<> {
|
||||
template <class T>
|
||||
void operator()(T&) const { }
|
||||
};
|
||||
|
||||
/// Generic visitor with fallback that can return a value:
|
||||
/// boost::apply_visitor(GenericFallbackReturnsVisitor<ReturnType, Class1, Class2>(
|
||||
/// [](Class1& _c) { return _c.f(); },
|
||||
/// [](Class2& _c) { return _c.g(); }
|
||||
/// ), variant);
|
||||
/// This one DOES have a fallback and will NOT fail at
|
||||
/// compile-time if you do not specify all variants.
|
||||
/// The fallback {}-constructs the return value.
|
||||
|
||||
template <class R, class...>
|
||||
struct GenericFallbackReturnsVisitor{};
|
||||
|
||||
template <class R, class Visitable, class... Others>
|
||||
struct GenericFallbackReturnsVisitor<R, Visitable, Others...>: public GenericFallbackReturnsVisitor<R, Others...>
|
||||
{
|
||||
explicit GenericFallbackReturnsVisitor(
|
||||
std::function<R(Visitable&)> _visitor,
|
||||
std::function<R(Others&)>... _otherVisitors
|
||||
):
|
||||
GenericFallbackReturnsVisitor<R, Others...>(std::move(_otherVisitors)...),
|
||||
m_visitor(std::move(_visitor))
|
||||
{}
|
||||
|
||||
using GenericFallbackReturnsVisitor<R, Others...>::operator ();
|
||||
R operator()(Visitable& _v) const { return m_visitor(_v); }
|
||||
|
||||
std::function<R(Visitable&)> m_visitor;
|
||||
};
|
||||
template <class R>
|
||||
struct GenericFallbackReturnsVisitor<R>: public boost::static_visitor<R> {
|
||||
template <class T>
|
||||
R operator()(T&) const { return {}; }
|
||||
};
|
||||
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
|
||||
// This is a copy of boost/multiprecision/detail/number_compare.hpp from boost 1.59 to replace buggy version from 1.58.
|
||||
// This is a copy of boost/multiprecision/detail/number_compare.hpp from boost 1.59 to replace buggy version from 1.58.
|
||||
|
||||
#ifdef BOOST_MP_COMPARE_HPP
|
||||
#error This bug workaround header must be included before original boost/multiprecision/detail/number_compare.hpp
|
||||
#error This bug workaround header must be included before original boost/multiprecision/detail/number_compare.hpp
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -150,11 +150,11 @@ template <class B, expression_template_option ET>
|
||||
struct is_valid_mixed_compare<number<B, ET>, number<B, ET> > : public mpl::false_ {};
|
||||
|
||||
template <class B, expression_template_option ET, class tag, class Arg1, class Arg2, class Arg3, class Arg4>
|
||||
struct is_valid_mixed_compare<number<B, ET>, expression<tag, Arg1, Arg2, Arg3, Arg4> >
|
||||
struct is_valid_mixed_compare<number<B, ET>, expression<tag, Arg1, Arg2, Arg3, Arg4> >
|
||||
: public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {};
|
||||
|
||||
template <class tag, class Arg1, class Arg2, class Arg3, class Arg4, class B, expression_template_option ET>
|
||||
struct is_valid_mixed_compare<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >
|
||||
struct is_valid_mixed_compare<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >
|
||||
: public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {};
|
||||
|
||||
template <class Backend, expression_template_option ExpressionTemplates>
|
||||
@ -196,7 +196,7 @@ inline bool operator == (const number<Backend, ExpressionTemplates>& a, const nu
|
||||
return eval_eq(a.backend(), b.backend());
|
||||
}
|
||||
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator == (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
|
||||
{
|
||||
using default_ops::eval_eq;
|
||||
@ -204,7 +204,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return eval_eq(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
|
||||
}
|
||||
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator == (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
|
||||
{
|
||||
using default_ops::eval_eq;
|
||||
@ -212,7 +212,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return eval_eq(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
|
||||
}
|
||||
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator == (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -222,7 +222,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return eval_eq(t.backend(), result_type::canonical_value(a));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -232,7 +232,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return eval_eq(t.backend(), result_type::canonical_value(b));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
|
||||
{
|
||||
using default_ops::eval_eq;
|
||||
@ -250,7 +250,7 @@ inline bool operator != (const number<Backend, ExpressionTemplates>& a, const nu
|
||||
return !eval_eq(a.backend(), b.backend());
|
||||
}
|
||||
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator != (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
|
||||
{
|
||||
using default_ops::eval_eq;
|
||||
@ -258,7 +258,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return !eval_eq(a.backend(), number<Backend, et_on>::canonical_value(b));
|
||||
}
|
||||
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator != (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
|
||||
{
|
||||
using default_ops::eval_eq;
|
||||
@ -266,7 +266,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return !eval_eq(b.backend(), number<Backend, et_on>::canonical_value(a));
|
||||
}
|
||||
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator != (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -276,7 +276,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return !eval_eq(t.backend(), result_type::canonical_value(a));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -286,7 +286,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return !eval_eq(t.backend(), result_type::canonical_value(b));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
|
||||
{
|
||||
using default_ops::eval_eq;
|
||||
@ -304,7 +304,7 @@ inline bool operator < (const number<Backend, ExpressionTemplates>& a, const num
|
||||
return eval_lt(a.backend(), b.backend());
|
||||
}
|
||||
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator < (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
|
||||
{
|
||||
using default_ops::eval_lt;
|
||||
@ -312,7 +312,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
|
||||
}
|
||||
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator < (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
|
||||
{
|
||||
using default_ops::eval_gt;
|
||||
@ -320,7 +320,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
|
||||
}
|
||||
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator < (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -330,7 +330,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return eval_gt(t.backend(), result_type::canonical_value(a));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -340,7 +340,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return eval_lt(t.backend(), result_type::canonical_value(b));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
|
||||
{
|
||||
using default_ops::eval_lt;
|
||||
@ -358,7 +358,7 @@ inline bool operator > (const number<Backend, ExpressionTemplates>& a, const num
|
||||
return eval_gt(a.backend(), b.backend());
|
||||
}
|
||||
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator > (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
|
||||
{
|
||||
using default_ops::eval_gt;
|
||||
@ -366,7 +366,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
|
||||
}
|
||||
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator > (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
|
||||
{
|
||||
using default_ops::eval_lt;
|
||||
@ -374,7 +374,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
|
||||
}
|
||||
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator > (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -384,7 +384,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return a > t;
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -394,7 +394,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return t > b;
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
|
||||
{
|
||||
using default_ops::eval_gt;
|
||||
@ -412,7 +412,7 @@ inline bool operator <= (const number<Backend, ExpressionTemplates>& a, const nu
|
||||
return !eval_gt(a.backend(), b.backend());
|
||||
}
|
||||
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator <= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
|
||||
{
|
||||
using default_ops::eval_gt;
|
||||
@ -420,7 +420,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return !eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
|
||||
}
|
||||
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator <= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
|
||||
{
|
||||
using default_ops::eval_lt;
|
||||
@ -428,7 +428,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return !eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
|
||||
}
|
||||
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator <= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -440,7 +440,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return !eval_lt(t.backend(), result_type::canonical_value(a));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -450,7 +450,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return !eval_gt(t.backend(), result_type::canonical_value(b));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
|
||||
{
|
||||
using default_ops::eval_gt;
|
||||
@ -468,7 +468,7 @@ inline bool operator >= (const number<Backend, ExpressionTemplates>& a, const nu
|
||||
return !eval_lt(a.backend(), b.backend());
|
||||
}
|
||||
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator >= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
|
||||
{
|
||||
using default_ops::eval_lt;
|
||||
@ -476,7 +476,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return !eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
|
||||
}
|
||||
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type
|
||||
operator >= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
|
||||
{
|
||||
using default_ops::eval_gt;
|
||||
@ -484,7 +484,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
|
||||
return !eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
|
||||
}
|
||||
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator >= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -494,7 +494,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return !eval_gt(t.backend(), result_type::canonical_value(a));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
|
||||
operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b)
|
||||
{
|
||||
typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type;
|
||||
@ -504,7 +504,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
|
||||
return !eval_lt(t.backend(), result_type::canonical_value(b));
|
||||
}
|
||||
template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
|
||||
operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b)
|
||||
{
|
||||
using default_ops::eval_lt;
|
||||
|
@ -41,10 +41,19 @@ void Assembly::append(Assembly const& _a)
|
||||
auto newDeposit = m_deposit + _a.deposit();
|
||||
for (AssemblyItem i: _a.m_items)
|
||||
{
|
||||
if (i.type() == Tag || i.type() == PushTag)
|
||||
switch (i.type())
|
||||
{
|
||||
case Tag:
|
||||
case PushTag:
|
||||
i.setData(i.data() + m_usedTags);
|
||||
else if (i.type() == PushSub || i.type() == PushSubSize)
|
||||
break;
|
||||
case PushSub:
|
||||
case PushSubSize:
|
||||
i.setData(i.data() + m_subs.size());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
append(i);
|
||||
}
|
||||
m_deposit = newDeposit;
|
||||
@ -69,6 +78,21 @@ void Assembly::append(Assembly const& _a, int _deposit)
|
||||
append(Instruction::POP);
|
||||
}
|
||||
|
||||
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
|
||||
{
|
||||
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
|
||||
m_deposit += _i.deposit();
|
||||
m_items.push_back(_i);
|
||||
if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty())
|
||||
m_items.back().setLocation(m_currentSourceLocation);
|
||||
return back();
|
||||
}
|
||||
|
||||
void Assembly::injectStart(AssemblyItem const& _i)
|
||||
{
|
||||
m_items.insert(m_items.begin(), _i);
|
||||
}
|
||||
|
||||
unsigned Assembly::bytesRequired(unsigned subTagSize) const
|
||||
{
|
||||
for (unsigned tagSize = subTagSize; true; ++tagSize)
|
||||
@ -264,7 +288,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
|
||||
createJsonValue("PUSH [ErrorTag]", i.location().start, i.location().end, ""));
|
||||
else
|
||||
collection.append(
|
||||
createJsonValue("PUSH [tag]", i.location().start, i.location().end, string(i.data())));
|
||||
createJsonValue("PUSH [tag]", i.location().start, i.location().end, dev::toString(i.data())));
|
||||
break;
|
||||
case PushSub:
|
||||
collection.append(
|
||||
@ -290,7 +314,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
|
||||
break;
|
||||
case Tag:
|
||||
collection.append(
|
||||
createJsonValue("tag", i.location().start, i.location().end, string(i.data())));
|
||||
createJsonValue("tag", i.location().start, i.location().end, dev::toString(i.data())));
|
||||
collection.append(
|
||||
createJsonValue("JUMPDEST", i.location().start, i.location().end));
|
||||
break;
|
||||
@ -323,16 +347,6 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
|
||||
return root;
|
||||
}
|
||||
|
||||
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
|
||||
{
|
||||
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
|
||||
m_deposit += _i.deposit();
|
||||
m_items.push_back(_i);
|
||||
if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty())
|
||||
m_items.back().setLocation(m_currentSourceLocation);
|
||||
return back();
|
||||
}
|
||||
|
||||
AssemblyItem Assembly::namedTag(string const& _name)
|
||||
{
|
||||
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
|
||||
@ -348,11 +362,6 @@ AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
|
||||
return AssemblyItem(PushLibraryAddress, h);
|
||||
}
|
||||
|
||||
void Assembly::injectStart(AssemblyItem const& _i)
|
||||
{
|
||||
m_items.insert(m_items.begin(), _i);
|
||||
}
|
||||
|
||||
Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
|
||||
{
|
||||
OptimiserSettings settings;
|
||||
@ -516,14 +525,14 @@ LinkerObject const& Assembly::assemble() const
|
||||
multimap<size_t, size_t> subRef;
|
||||
vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted
|
||||
unsigned bytesPerTag = dev::bytesRequired(bytesRequiredForCode);
|
||||
byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag;
|
||||
uint8_t tagPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerTag;
|
||||
|
||||
unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + m_auxiliaryData.size();
|
||||
for (auto const& sub: m_subs)
|
||||
bytesRequiredIncludingData += sub->assemble().bytecode.size();
|
||||
|
||||
unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData);
|
||||
byte dataRefPush = (byte)Instruction::PUSH1 - 1 + bytesPerDataRef;
|
||||
uint8_t dataRefPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerDataRef;
|
||||
ret.bytecode.reserve(bytesRequiredIncludingData);
|
||||
|
||||
for (AssemblyItem const& i: m_items)
|
||||
@ -535,25 +544,25 @@ LinkerObject const& Assembly::assemble() const
|
||||
switch (i.type())
|
||||
{
|
||||
case Operation:
|
||||
ret.bytecode.push_back((byte)i.instruction());
|
||||
ret.bytecode.push_back((uint8_t)i.instruction());
|
||||
break;
|
||||
case PushString:
|
||||
{
|
||||
ret.bytecode.push_back((byte)Instruction::PUSH32);
|
||||
ret.bytecode.push_back((uint8_t)Instruction::PUSH32);
|
||||
unsigned ii = 0;
|
||||
for (auto j: m_strings.at((h256)i.data()))
|
||||
if (++ii > 32)
|
||||
break;
|
||||
else
|
||||
ret.bytecode.push_back((byte)j);
|
||||
ret.bytecode.push_back((uint8_t)j);
|
||||
while (ii++ < 32)
|
||||
ret.bytecode.push_back(0);
|
||||
break;
|
||||
}
|
||||
case Push:
|
||||
{
|
||||
byte b = max<unsigned>(1, dev::bytesRequired(i.data()));
|
||||
ret.bytecode.push_back((byte)Instruction::PUSH1 - 1 + b);
|
||||
uint8_t b = max<unsigned>(1, dev::bytesRequired(i.data()));
|
||||
ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b);
|
||||
ret.bytecode.resize(ret.bytecode.size() + b);
|
||||
bytesRef byr(&ret.bytecode.back() + 1 - b, b);
|
||||
toBigEndian(i.data(), byr);
|
||||
@ -580,8 +589,8 @@ LinkerObject const& Assembly::assemble() const
|
||||
{
|
||||
auto s = m_subs.at(size_t(i.data()))->assemble().bytecode.size();
|
||||
i.setPushedValue(u256(s));
|
||||
byte b = max<unsigned>(1, dev::bytesRequired(s));
|
||||
ret.bytecode.push_back((byte)Instruction::PUSH1 - 1 + b);
|
||||
uint8_t b = max<unsigned>(1, dev::bytesRequired(s));
|
||||
ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b);
|
||||
ret.bytecode.resize(ret.bytecode.size() + b);
|
||||
bytesRef byr(&ret.bytecode.back() + 1 - b, b);
|
||||
toBigEndian(s, byr);
|
||||
@ -595,12 +604,12 @@ LinkerObject const& Assembly::assemble() const
|
||||
break;
|
||||
}
|
||||
case PushLibraryAddress:
|
||||
ret.bytecode.push_back(byte(Instruction::PUSH20));
|
||||
ret.bytecode.push_back(uint8_t(Instruction::PUSH20));
|
||||
ret.linkReferences[ret.bytecode.size()] = m_libraries.at(i.data());
|
||||
ret.bytecode.resize(ret.bytecode.size() + 20);
|
||||
break;
|
||||
case PushDeployTimeAddress:
|
||||
ret.bytecode.push_back(byte(Instruction::PUSH20));
|
||||
ret.bytecode.push_back(uint8_t(Instruction::PUSH20));
|
||||
ret.bytecode.resize(ret.bytecode.size() + 20);
|
||||
break;
|
||||
case Tag:
|
||||
@ -609,7 +618,7 @@ LinkerObject const& Assembly::assemble() const
|
||||
assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large.");
|
||||
assertThrow(m_tagPositionsInBytecode[size_t(i.data())] == size_t(-1), AssemblyException, "Duplicate tag position.");
|
||||
m_tagPositionsInBytecode[size_t(i.data())] = ret.bytecode.size();
|
||||
ret.bytecode.push_back((byte)Instruction::JUMPDEST);
|
||||
ret.bytecode.push_back((uint8_t)Instruction::JUMPDEST);
|
||||
break;
|
||||
default:
|
||||
BOOST_THROW_EXCEPTION(InvalidOpcode());
|
||||
@ -617,8 +626,8 @@ LinkerObject const& Assembly::assemble() const
|
||||
}
|
||||
|
||||
if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty())
|
||||
// Append a STOP just to be sure.
|
||||
ret.bytecode.push_back(0);
|
||||
// Append an INVALID here to help tests find miscompilation.
|
||||
ret.bytecode.push_back(uint8_t(Instruction::INVALID));
|
||||
|
||||
for (size_t i = 0; i < m_subs.size(); ++i)
|
||||
{
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
#include <libdevcore/Common.h>
|
||||
#include <libdevcore/Assertions.h>
|
||||
#include <libdevcore/SHA3.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
@ -52,18 +52,19 @@ public:
|
||||
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
|
||||
AssemblyItem namedTag(std::string const& _name);
|
||||
AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
|
||||
bytes const& data(h256 const& _i) const { return m_data.at(_i); }
|
||||
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
|
||||
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
|
||||
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
|
||||
AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); }
|
||||
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
|
||||
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
|
||||
|
||||
void append(Assembly const& _a);
|
||||
void append(Assembly const& _a, int _deposit);
|
||||
AssemblyItem const& append(AssemblyItem const& _i);
|
||||
AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); }
|
||||
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
|
||||
|
||||
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
|
||||
|
||||
/// Pushes the final size of the current assembly itself. Use this when the code is modified
|
||||
/// after compilation and CODESIZE is not an option.
|
||||
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
|
||||
@ -84,12 +85,9 @@ public:
|
||||
/// Appends @a _data literally to the very end of the bytecode.
|
||||
void appendAuxiliaryDataToEnd(bytes const& _data) { m_auxiliaryData += _data; }
|
||||
|
||||
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
|
||||
/// Returns the assembly items.
|
||||
AssemblyItems const& items() const { return m_items; }
|
||||
AssemblyItem const& back() const { return m_items.back(); }
|
||||
std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); }
|
||||
|
||||
void injectStart(AssemblyItem const& _i);
|
||||
int deposit() const { return m_deposit; }
|
||||
void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
|
||||
void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); }
|
||||
@ -97,9 +95,8 @@ public:
|
||||
/// Changes the source location used for each appended item.
|
||||
void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; }
|
||||
|
||||
/// Assembles the assembly into bytecode. The assembly should not be modified after this call.
|
||||
/// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached.
|
||||
LinkerObject const& assemble() const;
|
||||
bytes const& data(h256 const& _i) const { return m_data.at(_i); }
|
||||
|
||||
struct OptimiserSettings
|
||||
{
|
||||
@ -140,6 +137,18 @@ public:
|
||||
StringMap const& _sourceCodes = StringMap()
|
||||
) const;
|
||||
|
||||
public:
|
||||
// These features are only used by LLL
|
||||
AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); }
|
||||
|
||||
void append(Assembly const& _a);
|
||||
void append(Assembly const& _a, int _deposit);
|
||||
|
||||
void injectStart(AssemblyItem const& _i);
|
||||
|
||||
AssemblyItem const& back() const { return m_items.back(); }
|
||||
std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); }
|
||||
|
||||
protected:
|
||||
/// Does the same operations as @a optimise, but should only be applied to a sub and
|
||||
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
|
||||
|
@ -69,7 +69,7 @@ public:
|
||||
m_location(_location)
|
||||
{
|
||||
if (m_type == Operation)
|
||||
m_instruction = Instruction(byte(_data));
|
||||
m_instruction = Instruction(uint8_t(_data));
|
||||
else
|
||||
m_data = std::make_shared<u256>(_data);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <libdevcore/SHA3.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||
#include <libevmasm/AssemblyItem.h>
|
||||
|
||||
@ -160,7 +160,7 @@ AssemblyItems CSECodeGenerator::generateCode(
|
||||
if (seqNr < _initialSequenceNumber)
|
||||
// Invalid sequenced operation.
|
||||
// @todo quick fix for now. Proper fix needs to choose representative with higher
|
||||
// sequence number during dependency analyis.
|
||||
// sequence number during dependency analysis.
|
||||
BOOST_THROW_EXCEPTION(StackTooDeepException());
|
||||
sequencedExpressions.insert(make_pair(seqNr, id));
|
||||
}
|
||||
|
@ -93,15 +93,8 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
|
||||
|
||||
bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
|
||||
{
|
||||
if (m_params.isCreation)
|
||||
{
|
||||
bigint gas;
|
||||
for (auto b: _data)
|
||||
gas += b ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas;
|
||||
return gas;
|
||||
}
|
||||
else
|
||||
return GasCosts::createDataGas * dataSize();
|
||||
assertThrow(_data.size() > 0, OptimizerException, "Empty bytecode generated.");
|
||||
return bigint(GasMeter::dataGas(_data, m_params.isCreation));
|
||||
}
|
||||
|
||||
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
|
||||
|
@ -75,8 +75,6 @@ public:
|
||||
virtual AssemblyItems execute(Assembly& _assembly) const = 0;
|
||||
|
||||
protected:
|
||||
size_t dataSize() const { return std::max<size_t>(1, dev::bytesRequired(m_value)); }
|
||||
|
||||
/// @returns the run gas for the given items ignoring special gas costs
|
||||
static bigint simpleRunGas(AssemblyItems const& _items);
|
||||
/// @returns the gas needed to store the given data literally
|
||||
|
@ -39,7 +39,7 @@ using KnownStatePointer = std::shared_ptr<KnownState>;
|
||||
|
||||
/**
|
||||
* Identifier for a block, coincides with the tag number of an AssemblyItem but adds a special
|
||||
* ID for the inital block.
|
||||
* ID for the initial block.
|
||||
*/
|
||||
class BlockId
|
||||
{
|
||||
|
@ -184,6 +184,7 @@ string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const
|
||||
ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr)
|
||||
{
|
||||
static Rules rules;
|
||||
assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized.");
|
||||
|
||||
if (
|
||||
!_expr.item ||
|
||||
|
@ -101,8 +101,8 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
||||
break;
|
||||
case Instruction::KECCAK256:
|
||||
gas = GasCosts::keccak256Gas;
|
||||
gas += wordGas(GasCosts::keccak256WordGas, m_state->relativeStackElement(-1));
|
||||
gas += memoryGas(0, -1);
|
||||
gas += wordGas(GasCosts::keccak256WordGas, m_state->relativeStackElement(-1));
|
||||
break;
|
||||
case Instruction::CALLDATACOPY:
|
||||
case Instruction::CODECOPY:
|
||||
@ -114,6 +114,9 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
||||
case Instruction::EXTCODESIZE:
|
||||
gas = GasCosts::extCodeGas(m_evmVersion);
|
||||
break;
|
||||
case Instruction::EXTCODEHASH:
|
||||
gas = GasCosts::balanceGas(m_evmVersion);
|
||||
break;
|
||||
case Instruction::EXTCODECOPY:
|
||||
gas = GasCosts::extCodeGas(m_evmVersion);
|
||||
gas += memoryGas(-1, -3);
|
||||
@ -125,8 +128,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
||||
case Instruction::LOG3:
|
||||
case Instruction::LOG4:
|
||||
{
|
||||
unsigned n = unsigned(_item.instruction()) - unsigned(Instruction::LOG0);
|
||||
gas = GasCosts::logGas + GasCosts::logTopicGas * n;
|
||||
gas = GasCosts::logGas + GasCosts::logTopicGas * getLogNumber(_item.instruction());
|
||||
gas += memoryGas(0, -1);
|
||||
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
|
||||
gas += GasCosts::logDataGas * (*value);
|
||||
@ -215,7 +217,7 @@ GasMeter::GasConsumption GasMeter::memoryGas(ExpressionClasses::Id _position)
|
||||
if (!value)
|
||||
return GasConsumption::infinite();
|
||||
if (*value < m_largestMemoryAccess)
|
||||
return GasConsumption(u256(0));
|
||||
return GasConsumption(0);
|
||||
u256 previous = m_largestMemoryAccess;
|
||||
m_largestMemoryAccess = *value;
|
||||
auto memGas = [=](u256 const& pos) -> u256
|
||||
@ -258,4 +260,16 @@ unsigned GasMeter::runGas(Instruction _instruction)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
u256 GasMeter::dataGas(bytes const& _data, bool _inCreation)
|
||||
{
|
||||
bigint gas = 0;
|
||||
if (_inCreation)
|
||||
{
|
||||
for (auto b: _data)
|
||||
gas += (b != 0) ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas;
|
||||
}
|
||||
else
|
||||
gas = bigint(GasCosts::createDataGas) * _data.size();
|
||||
assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits.");
|
||||
return u256(gas);
|
||||
}
|
||||
|
@ -112,9 +112,10 @@ public:
|
||||
static GasConsumption infinite() { return GasConsumption(0, true); }
|
||||
|
||||
GasConsumption& operator+=(GasConsumption const& _other);
|
||||
bool operator<(GasConsumption const& _other) const { return this->tuple() < _other.tuple(); }
|
||||
|
||||
std::tuple<bool const&, u256 const&> tuple() const { return std::tie(isInfinite, value); }
|
||||
bool operator<(GasConsumption const& _other) const
|
||||
{
|
||||
return std::make_pair(isInfinite, value) < std::make_pair(_other.isInfinite, _other.value);
|
||||
}
|
||||
|
||||
u256 value;
|
||||
bool isInfinite;
|
||||
@ -135,6 +136,11 @@ public:
|
||||
/// change with EVM versions)
|
||||
static unsigned runGas(Instruction _instruction);
|
||||
|
||||
/// @returns the gas cost of the supplied data, depending whether it is in creation code, or not.
|
||||
/// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas
|
||||
/// otherwise code will be stored and have to pay "createDataGas" cost.
|
||||
static u256 dataGas(bytes const& _data, bool _inCreation);
|
||||
|
||||
private:
|
||||
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.
|
||||
GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value);
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "./Instruction.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <libdevcore/Common.h>
|
||||
#include <libdevcore/CommonIO.h>
|
||||
@ -72,6 +73,7 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions =
|
||||
{ "EXTCODECOPY", Instruction::EXTCODECOPY },
|
||||
{ "RETURNDATASIZE", Instruction::RETURNDATASIZE },
|
||||
{ "RETURNDATACOPY", Instruction::RETURNDATACOPY },
|
||||
{ "EXTCODEHASH", Instruction::EXTCODEHASH },
|
||||
{ "BLOCKHASH", Instruction::BLOCKHASH },
|
||||
{ "COINBASE", Instruction::COINBASE },
|
||||
{ "TIMESTAMP", Instruction::TIMESTAMP },
|
||||
@ -215,6 +217,7 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
|
||||
{ Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } },
|
||||
{ Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } },
|
||||
{ Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } },
|
||||
{ Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Balance } },
|
||||
{ Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } },
|
||||
{ Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } },
|
||||
{ Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } },
|
||||
@ -325,13 +328,20 @@ void dev::solidity::eachInstruction(
|
||||
size_t additional = 0;
|
||||
if (isValidInstruction(instr))
|
||||
additional = instructionInfo(instr).additional;
|
||||
|
||||
u256 data;
|
||||
for (size_t i = 0; i < additional; ++i)
|
||||
|
||||
// fill the data with the additional data bytes from the instruction stream
|
||||
while (additional > 0 && std::next(it) < _mem.end())
|
||||
{
|
||||
data <<= 8;
|
||||
if (++it < _mem.end())
|
||||
data |= *it;
|
||||
data |= *++it;
|
||||
--additional;
|
||||
}
|
||||
|
||||
// pad the remaining number of additional octets with zeros
|
||||
data <<= 8 * additional;
|
||||
|
||||
_onInstruction(instr, data);
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ enum class Instruction: uint8_t
|
||||
EXTCODECOPY, ///< copy external code (from another contract)
|
||||
RETURNDATASIZE = 0x3d, ///< get size of return data buffer
|
||||
RETURNDATACOPY = 0x3e, ///< copy return data in current environment to memory
|
||||
EXTCODEHASH = 0x3f, ///< get external code hash (from another contract)
|
||||
|
||||
BLOCKHASH = 0x40, ///< get hash of most recent complete block
|
||||
COINBASE, ///< get the block's coinbase address
|
||||
@ -192,8 +193,8 @@ enum class Instruction: uint8_t
|
||||
CALLCODE, ///< message-call with another account's code only
|
||||
RETURN, ///< halt execution returning output data
|
||||
DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender
|
||||
CREATE2 = 0xf5, ///< create new account with associated code at address `sha3(0xff + sender + salt + init code) % 2**160`
|
||||
STATICCALL = 0xfa, ///< like CALL but disallow state modifications
|
||||
CREATE2 = 0xfb, ///< create new account with associated code at address `sha3(sender + salt + sha3(init code)) % 2**160`
|
||||
|
||||
REVERT = 0xfd, ///< halt execution, revert state and return output data
|
||||
INVALID = 0xfe, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero)
|
||||
@ -218,22 +219,34 @@ inline bool isSwapInstruction(Instruction _inst)
|
||||
return Instruction::SWAP1 <= _inst && _inst <= Instruction::SWAP16;
|
||||
}
|
||||
|
||||
/// @returns true if the instruction is a LOG
|
||||
inline bool isLogInstruction(Instruction _inst)
|
||||
{
|
||||
return Instruction::LOG0 <= _inst && _inst <= Instruction::LOG4;
|
||||
}
|
||||
|
||||
/// @returns the number of PUSH Instruction _inst
|
||||
inline unsigned getPushNumber(Instruction _inst)
|
||||
{
|
||||
return (byte)_inst - unsigned(Instruction::PUSH1) + 1;
|
||||
return (uint8_t)_inst - unsigned(Instruction::PUSH1) + 1;
|
||||
}
|
||||
|
||||
/// @returns the number of DUP Instruction _inst
|
||||
inline unsigned getDupNumber(Instruction _inst)
|
||||
{
|
||||
return (byte)_inst - unsigned(Instruction::DUP1) + 1;
|
||||
return (uint8_t)_inst - unsigned(Instruction::DUP1) + 1;
|
||||
}
|
||||
|
||||
/// @returns the number of SWAP Instruction _inst
|
||||
inline unsigned getSwapNumber(Instruction _inst)
|
||||
{
|
||||
return (byte)_inst - unsigned(Instruction::SWAP1) + 1;
|
||||
return (uint8_t)_inst - unsigned(Instruction::SWAP1) + 1;
|
||||
}
|
||||
|
||||
/// @returns the number of LOG Instruction _inst
|
||||
inline unsigned getLogNumber(Instruction _inst)
|
||||
{
|
||||
return (uint8_t)_inst - unsigned(Instruction::LOG0);
|
||||
}
|
||||
|
||||
/// @returns the PUSH<_number> instruction
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include "KnownState.h"
|
||||
#include <functional>
|
||||
#include <libdevcore/SHA3.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
#include <libevmasm/AssemblyItem.h>
|
||||
|
||||
using namespace std;
|
||||
@ -121,28 +121,33 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
|
||||
vector<Id> arguments(info.args);
|
||||
for (int i = 0; i < info.args; ++i)
|
||||
arguments[i] = stackElement(m_stackHeight - i, _item.location());
|
||||
|
||||
if (_item.instruction() == Instruction::SSTORE)
|
||||
switch (_item.instruction())
|
||||
{
|
||||
case Instruction::SSTORE:
|
||||
op = storeInStorage(arguments[0], arguments[1], _item.location());
|
||||
else if (_item.instruction() == Instruction::SLOAD)
|
||||
break;
|
||||
case Instruction::SLOAD:
|
||||
setStackElement(
|
||||
m_stackHeight + _item.deposit(),
|
||||
loadFromStorage(arguments[0], _item.location())
|
||||
);
|
||||
else if (_item.instruction() == Instruction::MSTORE)
|
||||
break;
|
||||
case Instruction::MSTORE:
|
||||
op = storeInMemory(arguments[0], arguments[1], _item.location());
|
||||
else if (_item.instruction() == Instruction::MLOAD)
|
||||
break;
|
||||
case Instruction::MLOAD:
|
||||
setStackElement(
|
||||
m_stackHeight + _item.deposit(),
|
||||
loadFromMemory(arguments[0], _item.location())
|
||||
);
|
||||
else if (_item.instruction() == Instruction::KECCAK256)
|
||||
break;
|
||||
case Instruction::KECCAK256:
|
||||
setStackElement(
|
||||
m_stackHeight + _item.deposit(),
|
||||
applyKeccak256(arguments.at(0), arguments.at(1), _item.location())
|
||||
);
|
||||
else
|
||||
{
|
||||
break;
|
||||
default:
|
||||
bool invMem = SemanticInformation::invalidatesMemory(_item.instruction());
|
||||
bool invStor = SemanticInformation::invalidatesStorage(_item.instruction());
|
||||
// We could be a bit more fine-grained here (CALL only invalidates part of
|
||||
|
@ -29,12 +29,18 @@
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#pragma warning(push)
|
||||
#pragma GCC diagnostic push
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wredeclared-class-member"
|
||||
#endif // defined(__clang__)
|
||||
|
||||
#include <boost/bimap.hpp>
|
||||
#pragma warning(pop)
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif // defined(__clang__)
|
||||
|
||||
#include <libdevcore/CommonIO.h>
|
||||
#include <libdevcore/Exceptions.h>
|
||||
#include <libevmasm/ExpressionClasses.h>
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <libevmasm/LinkerObject.h>
|
||||
#include <libdevcore/CommonData.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
using namespace dev;
|
||||
using namespace dev::eth;
|
||||
@ -50,14 +51,19 @@ string LinkerObject::toHex() const
|
||||
for (auto const& ref: linkReferences)
|
||||
{
|
||||
size_t pos = ref.first * 2;
|
||||
string const& name = ref.second;
|
||||
string hash = libraryPlaceholder(ref.second);
|
||||
hex[pos] = hex[pos + 1] = hex[pos + 38] = hex[pos + 39] = '_';
|
||||
for (size_t i = 0; i < 36; ++i)
|
||||
hex[pos + 2 + i] = i < name.size() ? name[i] : '_';
|
||||
hex[pos + 2 + i] = hash.at(i);
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
string LinkerObject::libraryPlaceholder(string const& _libraryName)
|
||||
{
|
||||
return "$" + keccak256(_libraryName).hex().substr(0, 34) + "$";
|
||||
}
|
||||
|
||||
h160 const*
|
||||
LinkerObject::matchLibrary(
|
||||
string const& _linkRefName,
|
||||
@ -68,7 +74,7 @@ LinkerObject::matchLibrary(
|
||||
if (it != _libraryAddresses.end())
|
||||
return &it->second;
|
||||
// If the user did not supply a fully qualified library name,
|
||||
// try to match only the simple libary name
|
||||
// try to match only the simple library name
|
||||
size_t colon = _linkRefName.find(':');
|
||||
if (colon == string::npos)
|
||||
return nullptr;
|
||||
|
@ -50,6 +50,11 @@ struct LinkerObject
|
||||
/// addresses by placeholders.
|
||||
std::string toHex() const;
|
||||
|
||||
/// @returns a 36 character string that is used as a placeholder for the library
|
||||
/// address (enclosed by `__` on both sides). The placeholder is the hex representation
|
||||
/// of the first 18 bytes of the keccak-256 hash of @a _libraryName.
|
||||
static std::string libraryPlaceholder(std::string const& _libraryName);
|
||||
|
||||
private:
|
||||
static h160 const* matchLibrary(
|
||||
std::string const& _linkRefName,
|
||||
|
@ -57,6 +57,16 @@ public:
|
||||
|
||||
GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state);
|
||||
|
||||
static GasMeter::GasConsumption estimateMax(
|
||||
AssemblyItems const& _items,
|
||||
solidity::EVMVersion _evmVersion,
|
||||
size_t _startIndex,
|
||||
std::shared_ptr<KnownState> const& _state
|
||||
)
|
||||
{
|
||||
return PathGasMeter(_items, _evmVersion).estimateMax(_startIndex, _state);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Adds a new path item to the queue, but only if we do not already have
|
||||
/// a higher gas usage at that point.
|
||||
|
@ -249,6 +249,23 @@ struct TagConjunctions: SimplePeepholeOptimizerMethod<TagConjunctions, 3>
|
||||
}
|
||||
};
|
||||
|
||||
struct TruthyAnd: SimplePeepholeOptimizerMethod<TruthyAnd, 3>
|
||||
{
|
||||
static bool applySimple(
|
||||
AssemblyItem const& _push,
|
||||
AssemblyItem const& _not,
|
||||
AssemblyItem const& _and,
|
||||
std::back_insert_iterator<AssemblyItems>
|
||||
)
|
||||
{
|
||||
return (
|
||||
_push.type() == Push && _push.data() == 0 &&
|
||||
_not == Instruction::NOT &&
|
||||
_and == Instruction::AND
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/// Removes everything after a JUMP (or similar) until the next JUMPDEST.
|
||||
struct UnreachableCode
|
||||
{
|
||||
@ -305,7 +322,7 @@ bool PeepholeOptimiser::optimise()
|
||||
{
|
||||
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
|
||||
while (state.i < m_items.size())
|
||||
applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity());
|
||||
applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), JumpToNext(), UnreachableCode(), TagConjunctions(), TruthyAnd(), Identity());
|
||||
if (m_optimisedItems.size() < m_items.size() || (
|
||||
m_optimisedItems.size() == m_items.size() && (
|
||||
eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3) ||
|
||||
|
@ -44,12 +44,11 @@ template <class S> S modWorkaround(S const& _a, S const& _b)
|
||||
return (S)(bigint(_a) % bigint(_b));
|
||||
}
|
||||
|
||||
/// @returns a list of simplification rules given certain match placeholders.
|
||||
/// A, B and C should represent constants, X and Y arbitrary expressions.
|
||||
/// The simplifications should neven change the order of evaluation of
|
||||
/// arbitrary operations.
|
||||
// This part of simplificationRuleList below was split out to prevent
|
||||
// stack overflows in the JavaScript optimizer for emscripten builds
|
||||
// that affected certain browser versions.
|
||||
template <class Pattern>
|
||||
std::vector<SimplificationRule<Pattern>> simplificationRuleList(
|
||||
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
|
||||
Pattern A,
|
||||
Pattern B,
|
||||
Pattern C,
|
||||
@ -57,9 +56,8 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
|
||||
Pattern Y
|
||||
)
|
||||
{
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
rules += std::vector<SimplificationRule<Pattern>>{
|
||||
// arithmetics on constants
|
||||
return std::vector<SimplificationRule<Pattern>> {
|
||||
// arithmetic on constants
|
||||
{{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
|
||||
{{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
|
||||
{{Instruction::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false},
|
||||
@ -162,6 +160,22 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
|
||||
{{Instruction::OR, {X, {Instruction::NOT, {X}}}}, [=]{ return ~u256(0); }, true},
|
||||
{{Instruction::OR, {{Instruction::NOT, {X}}, X}}, [=]{ return ~u256(0); }, true},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// This part of simplificationRuleList below was split out to prevent
|
||||
// stack overflows in the JavaScript optimizer for emscripten builds
|
||||
// that affected certain browser versions.
|
||||
template <class Pattern>
|
||||
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
|
||||
Pattern A,
|
||||
Pattern B,
|
||||
Pattern,
|
||||
Pattern X,
|
||||
Pattern Y
|
||||
)
|
||||
{
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
|
||||
// Replace MOD X, <power-of-two> with AND X, <power-of-two> - 1
|
||||
for (size_t i = 0; i < 256; ++i)
|
||||
@ -292,5 +306,24 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
|
||||
return rules;
|
||||
}
|
||||
|
||||
/// @returns a list of simplification rules given certain match placeholders.
|
||||
/// A, B and C should represent constants, X and Y arbitrary expressions.
|
||||
/// The simplifications should never change the order of evaluation of
|
||||
/// arbitrary operations.
|
||||
template <class Pattern>
|
||||
std::vector<SimplificationRule<Pattern>> simplificationRuleList(
|
||||
Pattern A,
|
||||
Pattern B,
|
||||
Pattern C,
|
||||
Pattern X,
|
||||
Pattern Y
|
||||
)
|
||||
{
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
rules += simplificationRuleListPart1(A, B, C, X, Y);
|
||||
rules += simplificationRuleListPart2(A, B, C, X, Y);
|
||||
return rules;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +151,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
|
||||
case Instruction::MSIZE: // depends on previous writes and reads, not only on content
|
||||
case Instruction::BALANCE: // depends on previous calls
|
||||
case Instruction::EXTCODESIZE:
|
||||
case Instruction::EXTCODEHASH:
|
||||
case Instruction::RETURNDATACOPY: // depends on previous calls
|
||||
case Instruction::RETURNDATASIZE:
|
||||
return false;
|
||||
@ -172,6 +173,7 @@ bool SemanticInformation::movable(Instruction _instruction)
|
||||
case Instruction::KECCAK256:
|
||||
case Instruction::BALANCE:
|
||||
case Instruction::EXTCODESIZE:
|
||||
case Instruction::EXTCODEHASH:
|
||||
case Instruction::RETURNDATASIZE:
|
||||
case Instruction::SLOAD:
|
||||
case Instruction::PC:
|
||||
@ -233,6 +235,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction)
|
||||
case Instruction::GASPRICE:
|
||||
case Instruction::EXTCODESIZE:
|
||||
case Instruction::EXTCODECOPY:
|
||||
case Instruction::EXTCODEHASH:
|
||||
case Instruction::BLOCKHASH:
|
||||
case Instruction::COINBASE:
|
||||
case Instruction::TIMESTAMP:
|
||||
|
@ -21,16 +21,19 @@
|
||||
* Container for equivalence classes of expressions for use in common subexpression elimination.
|
||||
*/
|
||||
|
||||
#include <libevmasm/ExpressionClasses.h>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||
#include <libevmasm/SimplificationRules.h>
|
||||
|
||||
#include <libevmasm/ExpressionClasses.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||
#include <libevmasm/RuleList.h>
|
||||
#include <libdevcore/Assertions.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
@ -45,7 +48,7 @@ SimplificationRule<Pattern> const* Rules::findFirstMatch(
|
||||
resetMatchGroups();
|
||||
|
||||
assertThrow(_expr.item, OptimizerException, "");
|
||||
for (auto const& rule: m_rules[byte(_expr.item->instruction())])
|
||||
for (auto const& rule: m_rules[uint8_t(_expr.item->instruction())])
|
||||
{
|
||||
if (rule.pattern.matches(_expr, _classes))
|
||||
return &rule;
|
||||
@ -54,6 +57,11 @@ SimplificationRule<Pattern> const* Rules::findFirstMatch(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Rules::isInitialized() const
|
||||
{
|
||||
return !m_rules[uint8_t(Instruction::ADD)].empty();
|
||||
}
|
||||
|
||||
void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules)
|
||||
{
|
||||
for (auto const& r: _rules)
|
||||
@ -62,12 +70,12 @@ void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules)
|
||||
|
||||
void Rules::addRule(SimplificationRule<Pattern> const& _rule)
|
||||
{
|
||||
m_rules[byte(_rule.pattern.instruction())].push_back(_rule);
|
||||
m_rules[uint8_t(_rule.pattern.instruction())].push_back(_rule);
|
||||
}
|
||||
|
||||
Rules::Rules()
|
||||
{
|
||||
// Multiple occurences of one of these inside one rule must match the same equivalence class.
|
||||
// Multiple occurrences of one of these inside one rule must match the same equivalence class.
|
||||
// Constants.
|
||||
Pattern A(Push);
|
||||
Pattern B(Push);
|
||||
@ -82,6 +90,7 @@ Rules::Rules()
|
||||
Y.setMatchGroup(5, m_matchGroups);
|
||||
|
||||
addRules(simplificationRuleList(A, B, C, X, Y));
|
||||
assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized.");
|
||||
}
|
||||
|
||||
Pattern::Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments):
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include <libevmasm/ExpressionClasses.h>
|
||||
#include <libevmasm/SimplificationRule.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
@ -53,6 +55,10 @@ public:
|
||||
ExpressionClasses const& _classes
|
||||
);
|
||||
|
||||
/// Checks whether the rulelist is non-empty. This is usually enforced
|
||||
/// by the constructor, but we had some issues with static initialization.
|
||||
bool isInitialized() const;
|
||||
|
||||
private:
|
||||
void addRules(std::vector<SimplificationRule<Pattern>> const& _rules);
|
||||
void addRule(SimplificationRule<Pattern> const& _rule);
|
||||
|
@ -1,259 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that performs function inlining for arbitrary functions.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/FullInliner.h>
|
||||
|
||||
#include <libjulia/optimiser/ASTCopier.h>
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
#include <libjulia/optimiser/NameCollector.h>
|
||||
#include <libjulia/optimiser/Semantics.h>
|
||||
#include <libjulia/Exceptions.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
using namespace dev::solidity;
|
||||
|
||||
FullInliner::FullInliner(Block& _ast):
|
||||
m_ast(_ast)
|
||||
{
|
||||
assertThrow(m_ast.statements.size() >= 1, OptimizerException, "");
|
||||
assertThrow(m_ast.statements.front().type() == typeid(Block), OptimizerException, "");
|
||||
m_nameDispenser.m_usedNames = NameCollector(m_ast).names();
|
||||
|
||||
for (size_t i = 1; i < m_ast.statements.size(); ++i)
|
||||
{
|
||||
assertThrow(m_ast.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, "");
|
||||
FunctionDefinition& fun = boost::get<FunctionDefinition>(m_ast.statements.at(i));
|
||||
m_functions[fun.name] = &fun;
|
||||
m_functionsToVisit.insert(&fun);
|
||||
}
|
||||
}
|
||||
|
||||
void FullInliner::run()
|
||||
{
|
||||
assertThrow(m_ast.statements[0].type() == typeid(Block), OptimizerException, "");
|
||||
InlineModifier(*this, m_nameDispenser, "").visit(m_ast.statements[0]);
|
||||
while (!m_functionsToVisit.empty())
|
||||
handleFunction(**m_functionsToVisit.begin());
|
||||
}
|
||||
|
||||
void FullInliner::handleFunction(FunctionDefinition& _fun)
|
||||
{
|
||||
if (!m_functionsToVisit.count(&_fun))
|
||||
return;
|
||||
m_functionsToVisit.erase(&_fun);
|
||||
(InlineModifier(*this, m_nameDispenser, _fun.name))(_fun.body);
|
||||
}
|
||||
|
||||
void InlineModifier::operator()(FunctionalInstruction& _instruction)
|
||||
{
|
||||
visitArguments(_instruction.arguments);
|
||||
}
|
||||
|
||||
void InlineModifier::operator()(FunctionCall&)
|
||||
{
|
||||
assertThrow(false, OptimizerException, "Should be handled in visit() instead.");
|
||||
}
|
||||
|
||||
void InlineModifier::operator()(ForLoop& _loop)
|
||||
{
|
||||
(*this)(_loop.pre);
|
||||
// Do not visit the condition because we cannot inline there.
|
||||
(*this)(_loop.post);
|
||||
(*this)(_loop.body);
|
||||
}
|
||||
|
||||
void InlineModifier::operator()(Block& _block)
|
||||
{
|
||||
// This is only used if needed to minimize the number of move operations.
|
||||
vector<Statement> modifiedStatements;
|
||||
for (size_t i = 0; i < _block.statements.size(); ++i)
|
||||
{
|
||||
visit(_block.statements.at(i));
|
||||
if (!m_statementsToPrefix.empty())
|
||||
{
|
||||
if (modifiedStatements.empty())
|
||||
std::move(
|
||||
_block.statements.begin(),
|
||||
_block.statements.begin() + i,
|
||||
back_inserter(modifiedStatements)
|
||||
);
|
||||
modifiedStatements += std::move(m_statementsToPrefix);
|
||||
m_statementsToPrefix.clear();
|
||||
}
|
||||
if (!modifiedStatements.empty())
|
||||
modifiedStatements.emplace_back(std::move(_block.statements[i]));
|
||||
}
|
||||
if (!modifiedStatements.empty())
|
||||
_block.statements = std::move(modifiedStatements);
|
||||
}
|
||||
|
||||
void InlineModifier::visit(Expression& _expression)
|
||||
{
|
||||
if (_expression.type() != typeid(FunctionCall))
|
||||
return ASTModifier::visit(_expression);
|
||||
|
||||
FunctionCall& funCall = boost::get<FunctionCall>(_expression);
|
||||
FunctionDefinition& fun = m_driver.function(funCall.functionName.name);
|
||||
|
||||
m_driver.handleFunction(fun);
|
||||
|
||||
// TODO: Insert good heuristic here. Perhaps implement that inside the driver.
|
||||
bool doInline = funCall.functionName.name != m_currentFunction;
|
||||
|
||||
if (fun.returnVariables.size() > 1)
|
||||
doInline = false;
|
||||
|
||||
{
|
||||
vector<string> argNames;
|
||||
vector<string> argTypes;
|
||||
for (auto const& arg: fun.parameters)
|
||||
{
|
||||
argNames.push_back(fun.name + "_" + arg.name);
|
||||
argTypes.push_back(arg.type);
|
||||
}
|
||||
visitArguments(funCall.arguments, argNames, argTypes, doInline);
|
||||
}
|
||||
|
||||
if (!doInline)
|
||||
return;
|
||||
|
||||
map<string, string> variableReplacements;
|
||||
for (size_t i = 0; i < funCall.arguments.size(); ++i)
|
||||
variableReplacements[fun.parameters[i].name] = boost::get<Identifier>(funCall.arguments[i]).name;
|
||||
if (fun.returnVariables.empty())
|
||||
_expression = noop(funCall.location);
|
||||
else
|
||||
{
|
||||
string returnVariable = fun.returnVariables[0].name;
|
||||
variableReplacements[returnVariable] = newName(fun.name + "_" + returnVariable);
|
||||
|
||||
m_statementsToPrefix.emplace_back(VariableDeclaration{
|
||||
funCall.location,
|
||||
{{funCall.location, variableReplacements[returnVariable], fun.returnVariables[0].type}},
|
||||
{}
|
||||
});
|
||||
_expression = Identifier{funCall.location, variableReplacements[returnVariable]};
|
||||
}
|
||||
m_statementsToPrefix.emplace_back(BodyCopier(m_nameDispenser, fun.name + "_", variableReplacements)(fun.body));
|
||||
}
|
||||
|
||||
void InlineModifier::visit(Statement& _statement)
|
||||
{
|
||||
ASTModifier::visit(_statement);
|
||||
// Replace pop(0) expression statemets (and others) by empty blocks.
|
||||
if (_statement.type() == typeid(ExpressionStatement))
|
||||
{
|
||||
ExpressionStatement& expSt = boost::get<ExpressionStatement>(_statement);
|
||||
if (expSt.expression.type() == typeid(FunctionalInstruction))
|
||||
{
|
||||
FunctionalInstruction& funInstr = boost::get<FunctionalInstruction>(expSt.expression);
|
||||
if (funInstr.instruction == solidity::Instruction::POP)
|
||||
if (MovableChecker(funInstr.arguments.at(0)).movable())
|
||||
_statement = Block{expSt.location, {}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InlineModifier::visitArguments(
|
||||
vector<Expression>& _arguments,
|
||||
vector<string> const& _nameHints,
|
||||
vector<string> const& _types,
|
||||
bool _moveToFront
|
||||
)
|
||||
{
|
||||
// If one of the elements moves parts to the front, all other elements right of it
|
||||
// also have to be moved to the front to keep the order of evaluation.
|
||||
vector<Statement> prefix;
|
||||
for (size_t i = 0; i < _arguments.size(); ++i)
|
||||
{
|
||||
auto& arg = _arguments[i];
|
||||
// TODO optimize vector operations, check that it actually moves
|
||||
auto internalPrefix = visitRecursively(arg);
|
||||
if (!internalPrefix.empty())
|
||||
{
|
||||
_moveToFront = true;
|
||||
// We go through the arguments left to right, so we have to invert
|
||||
// the prefixes.
|
||||
prefix = std::move(internalPrefix) + std::move(prefix);
|
||||
}
|
||||
else if (_moveToFront)
|
||||
{
|
||||
auto location = locationOf(arg);
|
||||
string var = newName(i < _nameHints.size() ? _nameHints[i] : "");
|
||||
prefix.emplace(prefix.begin(), VariableDeclaration{
|
||||
location,
|
||||
{{TypedName{location, var, i < _types.size() ? _types[i] : ""}}},
|
||||
make_shared<Expression>(std::move(arg))
|
||||
});
|
||||
arg = Identifier{location, var};
|
||||
}
|
||||
}
|
||||
m_statementsToPrefix += std::move(prefix);
|
||||
}
|
||||
|
||||
vector<Statement> InlineModifier::visitRecursively(Expression& _expression)
|
||||
{
|
||||
vector<Statement> saved;
|
||||
saved.swap(m_statementsToPrefix);
|
||||
visit(_expression);
|
||||
saved.swap(m_statementsToPrefix);
|
||||
return saved;
|
||||
}
|
||||
|
||||
string InlineModifier::newName(string const& _prefix)
|
||||
{
|
||||
return m_nameDispenser.newName(_prefix);
|
||||
}
|
||||
|
||||
Expression InlineModifier::noop(SourceLocation const& _location)
|
||||
{
|
||||
return FunctionalInstruction{_location, solidity::Instruction::POP, {
|
||||
Literal{_location, assembly::LiteralKind::Number, "0", ""}
|
||||
}};
|
||||
}
|
||||
|
||||
Statement BodyCopier::operator()(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
for (auto const& var: _varDecl.variables)
|
||||
m_variableReplacements[var.name] = m_nameDispenser.newName(m_varNamePrefix + var.name);
|
||||
return ASTCopier::operator()(_varDecl);
|
||||
}
|
||||
|
||||
Statement BodyCopier::operator()(FunctionDefinition const& _funDef)
|
||||
{
|
||||
assertThrow(false, OptimizerException, "Function hoisting has to be done before function inlining.");
|
||||
return _funDef;
|
||||
}
|
||||
|
||||
string BodyCopier::translateIdentifier(string const& _name)
|
||||
{
|
||||
if (m_variableReplacements.count(_name))
|
||||
return m_variableReplacements.at(_name);
|
||||
else
|
||||
return _name;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user