diff --git a/.circleci/README.md b/.circleci/README.md index 7f7b01455..c8bcd45f5 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -7,22 +7,24 @@ The docker images are build locally on the developer machine: ```!sh cd .circleci/docker/ -docker build -t ethereum/solidity-buildpack-deps:ubuntu1904 -f Dockerfile.ubuntu1904 . -docker push ethereum/solidity-buildpack-deps:ubuntu1904 +docker build -t ethereum/solidity-buildpack-deps:ubuntu1904- -f Dockerfile.ubuntu1904 . +docker push ethereum/solidity-buildpack-deps:ubuntu1904- ``` -which you can find on Dockerhub after the push at: +The current revision is `2`. - https://hub.docker.com/r/ethereum/solidity-buildpack-deps +Once the docker image has been built and pushed to Dockerhub, you can find it at: -where the image tag reflects the target OS to build Solidity and run its test on. + https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu1904- + +where the image tag reflects the target OS and revision to build Solidity and run its tests on. ### Testing docker images locally ```!sh cd solidity # Mounts your local solidity directory in docker container for testing -docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904 /bin/bash +docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904- /bin/bash cd /src/solidity -``` \ No newline at end of file +``` diff --git a/.circleci/config.yml b/.circleci/config.yml index 0140126e5..0787855df 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -106,7 +106,7 @@ defaults: - test_ubuntu1904_clang: &test_ubuntu1904_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-2 steps: - checkout - attach_workspace: @@ -117,7 +117,7 @@ defaults: - test_ubuntu1904: &test_ubuntu1904 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 steps: - checkout - attach_workspace: @@ -287,7 +287,7 @@ jobs: b_ubu_clang: &build_ubuntu1904_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-2 environment: CC: clang CXX: clang++ @@ -299,7 +299,7 @@ jobs: b_ubu: &build_ubuntu1904 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 steps: - checkout - run: *run_build @@ -313,7 +313,7 @@ jobs: b_ubu18: &build_ubuntu1804 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1804 + - image: ethereum/solidity-buildpack-deps:ubuntu1804-2 environment: CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 CMAKE_BUILD_TYPE: RelWithDebugInfo @@ -519,7 +519,7 @@ jobs: b_docs: docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 steps: - checkout - run: *setup_prerelease_commit_hash @@ -544,7 +544,7 @@ jobs: t_ubu_cli: &t_ubu_cli docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 + - image: ethereum/solidity-buildpack-deps:ubuntu1904-2 environment: TERM: xterm steps: diff --git a/.circleci/docker/Dockerfile.clang.ubuntu1904 b/.circleci/docker/Dockerfile.clang.ubuntu1904 index 4fb9b1132..3678afbed 100644 --- a/.circleci/docker/Dockerfile.clang.ubuntu1904 +++ b/.circleci/docker/Dockerfile.clang.ubuntu1904 @@ -89,43 +89,24 @@ RUN set -ex; \ ninja install/strip; \ rm -rf /usr/src/libprotobuf-mutator -# ETHASH -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \ - cd ethash; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/ethash - -# INTX -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \ - cd intx; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/intx; - # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ + # isoltest links against the evmone shared library cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ ninja; \ ninja install/strip; \ + # abiv2_proto_ossfuzz links against the evmone standalone static library + cmake -G Ninja -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX="/usr" ..; \ + ninja; \ + ninja install/strip; \ rm -rf /usr/src/evmone FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin -COPY --from=libraries /usr/include /usr/include \ No newline at end of file +COPY --from=libraries /usr/include /usr/include diff --git a/.circleci/docker/Dockerfile.ubuntu1804 b/.circleci/docker/Dockerfile.ubuntu1804 index 0242661e7..fbf664a49 100644 --- a/.circleci/docker/Dockerfile.ubuntu1804 +++ b/.circleci/docker/Dockerfile.ubuntu1804 @@ -74,34 +74,10 @@ RUN set -ex; \ ar r /usr/lib/libFuzzingEngine.a *.o; \ rm -rf /var/lib/libfuzzer -# ETHASH -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \ - cd ethash; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/ethash - -# INTX -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \ - cd intx; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/intx; - # EVMONE -ARG EVMONE_HASH="f10d12c190f55a9d373e78b2dc0074d35d752c02cb536bb6fe754fb3719dd69e" +ARG EVMONE_HASH="fa4f40daf7cf9ccbcca6c78345977e084ea2136a8eae661e4d19471be852b15b" ARG EVMONE_MAJOR="0" -ARG EVMONE_MINOR="1" +ARG EVMONE_MINOR="3" ARG EVMONE_MICRO="0" RUN set -ex; \ EVMONE_VERSION="$EVMONE_MAJOR.$EVMONE_MINOR.$EVMONE_MICRO"; \ diff --git a/.circleci/docker/Dockerfile.ubuntu1904 b/.circleci/docker/Dockerfile.ubuntu1904 index 504e13d1f..2c211d956 100644 --- a/.circleci/docker/Dockerfile.ubuntu1904 +++ b/.circleci/docker/Dockerfile.ubuntu1904 @@ -74,37 +74,14 @@ RUN set -ex; \ ar r /usr/lib/libFuzzingEngine.a *.o; \ rm -rf /var/lib/libfuzzer -# ETHASH -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \ - cd ethash; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/ethash - -# INTX -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \ - cd intx; \ - mkdir build; \ - cd build; \ - cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/intx; - # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ + # isoltest links against the evmone shared library cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ ninja; \ ninja install/strip; \ diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..048e2dcb4 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,17 @@ +name: Mark stale pull requests + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-pr-message: "A reminder that this pull request has had no activity for 14 days" + stale-pr-label: "no-pr-activity" + days-before-stale: 14 diff --git a/Changelog.md b/Changelog.md index f7fd08c09..fb3558632 100644 --- a/Changelog.md +++ b/Changelog.md @@ -42,6 +42,7 @@ Compiler Features: Bugfixes: + * SMTChecker: Fix internal error when using ``abi.decode``. diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index e67f614ae..01b2fa963 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -67,47 +67,48 @@ std::vector> simplificationRuleListPart1( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; return std::vector> { // arithmetic on constants - {{Pattern::Builtins::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false}, - {{Pattern::Builtins::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false}, - {{Pattern::Builtins::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false}, - {{Pattern::Builtins::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, - {{Pattern::Builtins::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {{Pattern::Builtins::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, - {{Pattern::Builtins::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {{Pattern::Builtins::EXP, {A, B}}, [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, - {{Pattern::Builtins::NOT, {A}}, [=]{ return ~A.d(); }, false}, - {{Pattern::Builtins::LT, {A, B}}, [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, - {{Pattern::Builtins::GT, {A, B}}, [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, - {{Pattern::Builtins::SLT, {A, B}}, [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, - {{Pattern::Builtins::SGT, {A, B}}, [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, - {{Pattern::Builtins::EQ, {A, B}}, [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, - {{Pattern::Builtins::ISZERO, {A}}, [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, - {{Pattern::Builtins::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false}, - {{Pattern::Builtins::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false}, - {{Pattern::Builtins::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false}, - {{Pattern::Builtins::BYTE, {A, B}}, [=]{ + {Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }, false}, + {Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }, false}, + {Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }, false}, + {Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, + {Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, + {Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, + {Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, + {Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, + {Builtins::NOT(A), [=]{ return ~A.d(); }, false}, + {Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, + {Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, + {Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, + {Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, + {Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, + {Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, + {Builtins::AND(A, B), [=]{ return A.d() & B.d(); }, false}, + {Builtins::OR(A, B), [=]{ return A.d() | B.d(); }, false}, + {Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }, false}, + {Builtins::BYTE(A, B), [=]{ return A.d() >= Pattern::WordSize / 8 ? 0 : (B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff; }, false}, - {{Pattern::Builtins::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, - {{Pattern::Builtins::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, - {{Pattern::Builtins::SIGNEXTEND, {A, B}}, [=]() -> Word { + {Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, + {Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, + {Builtins::SIGNEXTEND(A, B), [=]() -> Word { if (A.d() >= Pattern::WordSize / 8 - 1) return B.d(); unsigned testBit = unsigned(A.d()) * 8 + 7; Word mask = (Word(1) << testBit) - 1; return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask; }, false}, - {{Pattern::Builtins::SHL, {A, B}}, [=]{ + {Builtins::SHL(A, B), [=]{ if (A.d() >= Pattern::WordSize) return Word(0); return shlWorkaround(B.d(), unsigned(A.d())); }, false}, - {{Pattern::Builtins::SHR, {A, B}}, [=]{ + {Builtins::SHR(A, B), [=]{ if (A.d() >= Pattern::WordSize) return Word(0); return B.d() >> unsigned(A.d()); @@ -115,6 +116,7 @@ std::vector> simplificationRuleListPart1( }; } + template std::vector> simplificationRuleListPart2( Pattern, @@ -125,50 +127,51 @@ std::vector> simplificationRuleListPart2( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; return std::vector> { // invariants involving known constants - {{Pattern::Builtins::ADD, {X, 0}}, [=]{ return X; }, false}, - {{Pattern::Builtins::ADD, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SUB, {X, 0}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SUB, {~Word(0), X}}, [=]() -> Pattern { return {Pattern::Builtins::NOT, {X}}; }, false}, - {{Pattern::Builtins::MUL, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::MUL, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::MUL, {X, 1}}, [=]{ return X; }, false}, - {{Pattern::Builtins::MUL, {1, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::MUL, {X, Word(-1)}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false}, - {{Pattern::Builtins::MUL, {Word(-1), X}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false}, - {{Pattern::Builtins::DIV, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::DIV, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::DIV, {X, 1}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SDIV, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SDIV, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SDIV, {X, 1}}, [=]{ return X; }, false}, - {{Pattern::Builtins::AND, {X, ~Word(0)}}, [=]{ return X; }, false}, - {{Pattern::Builtins::AND, {~Word(0), X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::AND, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::AND, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::OR, {X, 0}}, [=]{ return X; }, false}, - {{Pattern::Builtins::OR, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::OR, {X, ~Word(0)}}, [=]{ return ~Word(0); }, true}, - {{Pattern::Builtins::OR, {~Word(0), X}}, [=]{ return ~Word(0); }, true}, - {{Pattern::Builtins::XOR, {X, 0}}, [=]{ return X; }, false}, - {{Pattern::Builtins::XOR, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::MOD, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::MOD, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::EQ, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false }, - {{Pattern::Builtins::EQ, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false }, - {{Pattern::Builtins::SHL, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SHR, {0, X}}, [=]{ return X; }, false}, - {{Pattern::Builtins::SHL, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SHR, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::GT, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false}, - {{Pattern::Builtins::LT, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false}, - {{Pattern::Builtins::GT, {X, ~Word(0)}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::LT, {~Word(0), X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::GT, {0, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::LT, {X, 0}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::AND, {{Pattern::Builtins::BYTE, {X, Y}}, {Word(0xff)}}}, [=]() -> Pattern { return {Pattern::Builtins::BYTE, {X, Y}}; }, false}, - {{Pattern::Builtins::BYTE, {Pattern::WordSize / 8 - 1, X}}, [=]() -> Pattern { return {Pattern::Builtins::AND, {X, Word(0xff)}}; }, false} + {Builtins::ADD(X, 0), [=]{ return X; }, false}, + {Builtins::ADD(0, X), [=]{ return X; }, false}, + {Builtins::SUB(X, 0), [=]{ return X; }, false}, + {Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }, false}, + {Builtins::MUL(X, 0), [=]{ return Word(0); }, true}, + {Builtins::MUL(0, X), [=]{ return Word(0); }, true}, + {Builtins::MUL(X, 1), [=]{ return X; }, false}, + {Builtins::MUL(1, X), [=]{ return X; }, false}, + {Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, + {Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, + {Builtins::DIV(X, 0), [=]{ return Word(0); }, true}, + {Builtins::DIV(0, X), [=]{ return Word(0); }, true}, + {Builtins::DIV(X, 1), [=]{ return X; }, false}, + {Builtins::SDIV(X, 0), [=]{ return Word(0); }, true}, + {Builtins::SDIV(0, X), [=]{ return Word(0); }, true}, + {Builtins::SDIV(X, 1), [=]{ return X; }, false}, + {Builtins::AND(X, ~Word(0)), [=]{ return X; }, false}, + {Builtins::AND(~Word(0), X), [=]{ return X; }, false}, + {Builtins::AND(X, 0), [=]{ return Word(0); }, true}, + {Builtins::AND(0, X), [=]{ return Word(0); }, true}, + {Builtins::OR(X, 0), [=]{ return X; }, false}, + {Builtins::OR(0, X), [=]{ return X; }, false}, + {Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }, true}, + {Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }, true}, + {Builtins::XOR(X, 0), [=]{ return X; }, false}, + {Builtins::XOR(0, X), [=]{ return X; }, false}, + {Builtins::MOD(X, 0), [=]{ return Word(0); }, true}, + {Builtins::MOD(0, X), [=]{ return Word(0); }, true}, + {Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, + {Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, + {Builtins::SHL(0, X), [=]{ return X; }, false}, + {Builtins::SHR(0, X), [=]{ return X; }, false}, + {Builtins::SHL(X, 0), [=]{ return Word(0); }, true}, + {Builtins::SHR(X, 0), [=]{ return Word(0); }, true}, + {Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, + {Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, + {Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }, true}, + {Builtins::LT(~Word(0), X), [=]{ return Word(0); }, true}, + {Builtins::GT(0, X), [=]{ return Word(0); }, true}, + {Builtins::LT(X, 0), [=]{ return Word(0); }, true}, + {Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }, false}, + {Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }, false} }; } @@ -182,18 +185,19 @@ std::vector> simplificationRuleListPart3( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; return std::vector> { // operations involving an expression and itself - {{Pattern::Builtins::AND, {X, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::OR, {X, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::XOR, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SUB, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::EQ, {X, X}}, [=]{ return Word(1); }, true}, - {{Pattern::Builtins::LT, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SLT, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::GT, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::SGT, {X, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::MOD, {X, X}}, [=]{ return Word(0); }, true} + {Builtins::AND(X, X), [=]{ return X; }, true}, + {Builtins::OR(X, X), [=]{ return X; }, true}, + {Builtins::XOR(X, X), [=]{ return Word(0); }, true}, + {Builtins::SUB(X, X), [=]{ return Word(0); }, true}, + {Builtins::EQ(X, X), [=]{ return Word(1); }, true}, + {Builtins::LT(X, X), [=]{ return Word(0); }, true}, + {Builtins::SLT(X, X), [=]{ return Word(0); }, true}, + {Builtins::GT(X, X), [=]{ return Word(0); }, true}, + {Builtins::SGT(X, X), [=]{ return Word(0); }, true}, + {Builtins::MOD(X, X), [=]{ return Word(0); }, true} }; } @@ -207,25 +211,26 @@ std::vector> simplificationRuleListPart4( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; return std::vector> { // logical instruction combinations - {{Pattern::Builtins::NOT, {{Pattern::Builtins::NOT, {X}}}}, [=]{ return X; }, false}, - {{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {X, Y}}}}, [=]{ return Y; }, true}, - {{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {Y, X}}}}, [=]{ return Y; }, true}, - {{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {X, Y}}, X}}, [=]{ return Y; }, true}, - {{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {Y, X}}, X}}, [=]{ return Y; }, true}, - {{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {X, Y}}}}, [=]{ return X; }, true}, - {{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {Y, X}}}}, [=]{ return X; }, true}, - {{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {X, Y}}, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {Y, X}}, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {X, Y}}}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {Y, X}}}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {X, Y}}, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {Y, X}}, X}}, [=]{ return X; }, true}, - {{Pattern::Builtins::AND, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::AND, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return Word(0); }, true}, - {{Pattern::Builtins::OR, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return ~Word(0); }, true}, - {{Pattern::Builtins::OR, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return ~Word(0); }, true}, + {Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }, false}, + {Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }, true}, + {Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }, true}, + {Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }, true}, + {Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }, true}, + {Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }, true}, + {Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }, true}, + {Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }, true}, + {Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }, true}, + {Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }, true}, + {Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }, true}, + {Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }, true}, + {Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }, true}, + {Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }, true}, + {Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }, true}, + {Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }, true}, + {Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }, true}, }; } @@ -240,6 +245,7 @@ std::vector> simplificationRuleListPart5( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; std::vector> rules; @@ -248,15 +254,15 @@ std::vector> simplificationRuleListPart5( { Word value = Word(1) << i; rules.push_back({ - {Pattern::Builtins::MOD, {X, value}}, - [=]() -> Pattern { return {Pattern::Builtins::AND, {X, value - 1}}; }, + Builtins::MOD(X, value), + [=]() -> Pattern { return Builtins::AND(X, value - 1); }, false }); } // Replace SHL >=256, X with 0 rules.push_back({ - {Pattern::Builtins::SHL, {A, X}}, + Builtins::SHL(A, X), [=]() -> Pattern { return Word(0); }, true, [=]() { return A.d() >= Pattern::WordSize; } @@ -264,7 +270,7 @@ std::vector> simplificationRuleListPart5( // Replace SHR >=256, X with 0 rules.push_back({ - {Pattern::Builtins::SHR, {A, X}}, + Builtins::SHR(A, X), [=]() -> Pattern { return Word(0); }, true, [=]() { return A.d() >= Pattern::WordSize; } @@ -272,29 +278,29 @@ std::vector> simplificationRuleListPart5( // Replace BYTE(A, X), A >= 32 with 0 rules.push_back({ - {Pattern::Builtins::BYTE, {A, X}}, + Builtins::BYTE(A, X), [=]() -> Pattern { return Word(0); }, true, [=]() { return A.d() >= Pattern::WordSize / 8; } }); - for (auto const& op: std::vector{ - Pattern::Builtins::ADDRESS, - Pattern::Builtins::CALLER, - Pattern::Builtins::ORIGIN, - Pattern::Builtins::COINBASE + for (auto instr: { + Instruction::ADDRESS, + Instruction::CALLER, + Instruction::ORIGIN, + Instruction::COINBASE }) { assertThrow(Pattern::WordSize > 160, OptimizerException, ""); Word const mask = (Word(1) << 160) - 1; rules.push_back({ - {Pattern::Builtins::AND, {{op, mask}}}, - [=]() -> Pattern { return op; }, + Builtins::AND(Pattern{instr}, mask), + [=]() -> Pattern { return {instr}; }, false }); rules.push_back({ - {Pattern::Builtins::AND, {{mask, op}}}, - [=]() -> Pattern { return op; }, + Builtins::AND(mask, Pattern{instr}), + [=]() -> Pattern { return {instr}; }, false }); } @@ -311,30 +317,35 @@ std::vector> simplificationRuleListPart6( Pattern Y ) { + using Builtins = typename Pattern::Builtins; + std::vector> rules; // Double negation of opcodes with boolean result - for (auto const& op: std::vector{ - Pattern::Builtins::EQ, - Pattern::Builtins::LT, - Pattern::Builtins::SLT, - Pattern::Builtins::GT, - Pattern::Builtins::SGT + for (auto instr: { + Instruction::EQ, + Instruction::LT, + Instruction::SLT, + Instruction::GT, + Instruction::SGT }) + { + typename Builtins::PatternGeneratorInstance op{instr}; rules.push_back({ - {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{op, {X, Y}}}}}}, - [=]() -> Pattern { return {op, {X, Y}}; }, + Builtins::ISZERO(Builtins::ISZERO(op(X, Y))), + [=]() -> Pattern { return op(X, Y); }, false }); + } rules.push_back({ - {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}}}, - [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, + Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))), + [=]() -> Pattern { return Builtins::ISZERO(X); }, false }); rules.push_back({ - {Pattern::Builtins::ISZERO, {{Pattern::Builtins::XOR, {X, Y}}}}, - [=]() -> Pattern { return { Pattern::Builtins::EQ, {X, Y} }; }, + Builtins::ISZERO(Builtins::XOR(X, Y)), + [=]() -> Pattern { return Builtins::EQ(X, Y); }, false }); @@ -351,42 +362,44 @@ std::vector> simplificationRuleListPart7( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; + std::vector> rules; // Associative operations - for (auto const& opFun: std::vector>>{ - {Pattern::Builtins::ADD, std::plus()}, - {Pattern::Builtins::MUL, std::multiplies()}, - {Pattern::Builtins::AND, std::bit_and()}, - {Pattern::Builtins::OR, std::bit_or()}, - {Pattern::Builtins::XOR, std::bit_xor()} + for (auto&& instrAndFunc: std::vector>>{ + {Instruction::ADD, std::plus()}, + {Instruction::MUL, std::multiplies()}, + {Instruction::AND, std::bit_and()}, + {Instruction::OR, std::bit_or()}, + {Instruction::XOR, std::bit_xor()} }) { - auto op = opFun.first; - auto fun = opFun.second; + typename Builtins::PatternGeneratorInstance op{instrAndFunc.first}; + std::function fun = instrAndFunc.second; // Moving constants to the outside, order matters here - we first add rules // for constants and then for non-constants. // xa can be (X, A) or (A, X) - for (auto xa: {std::vector{X, A}, std::vector{A, X}}) + for (auto const& opXA: {op(X, A), op(A, X)}) { rules += std::vector>{{ // (X+A)+B -> X+(A+B) - {op, {{op, xa}, B}}, - [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }, + op(opXA, B), + [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, false }, { // (X+A)+Y -> (X+Y)+A - {op, {{op, xa}, Y}}, - [=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }, + op(opXA, Y), + [=]() -> Pattern { return op(op(X, Y), A); }, false }, { // B+(X+A) -> X+(A+B) - {op, {B, {op, xa}}}, - [=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }, + op(B, opXA), + [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, false }, { // Y+(X+A) -> (Y+X)+A - {op, {Y, {op, xa}}}, - [=]() -> Pattern { return {op, {{op, {Y, X}}, A}}; }, + op(Y, opXA), + [=]() -> Pattern { return op(op(Y, X), A); }, false }}; } @@ -395,13 +408,13 @@ std::vector> simplificationRuleListPart7( // Combine two SHL by constant rules.push_back({ // SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X) - {Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}}, + Builtins::SHL(B, Builtins::SHL(A, X)), [=]() -> Pattern { bigint sum = bigint(A.d()) + B.d(); if (sum >= Pattern::WordSize) - return {Pattern::Builtins::AND, {X, Word(0)}}; + return Builtins::AND(X, Word(0)); else - return {Pattern::Builtins::SHL, {Word(sum), X}}; + return Builtins::SHL(Word(sum), X); }, false }); @@ -409,13 +422,13 @@ std::vector> simplificationRuleListPart7( // Combine two SHR by constant rules.push_back({ // SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X) - {Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}}, + Builtins::SHR(B, Builtins::SHR(A, X)), [=]() -> Pattern { bigint sum = bigint(A.d()) + B.d(); if (sum >= Pattern::WordSize) - return {Pattern::Builtins::AND, {X, Word(0)}}; + return Builtins::AND(X, Word(0)); else - return {Pattern::Builtins::SHR, {Word(sum), X}}; + return Builtins::SHR(Word(sum), X); }, false }); @@ -423,16 +436,16 @@ std::vector> simplificationRuleListPart7( // Combine SHL-SHR by constant rules.push_back({ // SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask) - {Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}}, + Builtins::SHR(B, Builtins::SHL(A, X)), [=]() -> Pattern { Word mask = shlWorkaround(~Word(0), unsigned(A.d())) >> unsigned(B.d()); if (A.d() > B.d()) - return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {A.d() - B.d(), X}}, mask}}; + return Builtins::AND(Builtins::SHL(A.d() - B.d(), X), mask); else if (B.d() > A.d()) - return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B.d() - A.d(), X}}, mask}}; + return Builtins::AND(Builtins::SHR(B.d() - A.d(), X), mask); else - return {Pattern::Builtins::AND, {X, mask}}; + return Builtins::AND(X, mask); }, false, [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } @@ -441,41 +454,42 @@ std::vector> simplificationRuleListPart7( // Combine SHR-SHL by constant rules.push_back({ // SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask) - {Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}}, + Builtins::SHL(B, Builtins::SHR(A, X)), [=]() -> Pattern { Word mask = shlWorkaround((~Word(0)) >> unsigned(A.d()), unsigned(B.d())); if (A.d() > B.d()) - return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {A.d() - B.d(), X}}, mask}}; + return Builtins::AND(Builtins::SHR(A.d() - B.d(), X), mask); else if (B.d() > A.d()) - return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {B.d() - A.d(), X}}, mask}}; + return Builtins::AND(Builtins::SHL(B.d() - A.d(), X), mask); else - return {Pattern::Builtins::AND, {X, mask}}; + return Builtins::AND(X, mask); }, false, [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } }); // Move AND with constant across SHL and SHR by constant - for (auto shiftOp: {Pattern::Builtins::SHL, Pattern::Builtins::SHR}) + for (auto instr: {Instruction::SHL, Instruction::SHR}) { + typename Builtins::PatternGeneratorInstance shiftOp{instr}; auto replacement = [=]() -> Pattern { Word mask = - shiftOp == Pattern::Builtins::SHL ? + instr == Instruction::SHL ? shlWorkaround(A.d(), unsigned(B.d())) : A.d() >> unsigned(B.d()); - return {Pattern::Builtins::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}}; + return Builtins::AND(shiftOp(B.d(), X), std::move(mask)); }; rules.push_back({ // SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) - {shiftOp, {{B}, {Pattern::Builtins::AND, {{X}, {A}}}}}, + shiftOp(B, Builtins::AND(X, A)), replacement, false, [=] { return B.d() < Pattern::WordSize; } }); rules.push_back({ // SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) - {shiftOp, {{B}, {Pattern::Builtins::AND, {{A}, {X}}}}}, + shiftOp(B, Builtins::AND(A, X)), replacement, false, [=] { return B.d() < Pattern::WordSize; } @@ -484,27 +498,27 @@ std::vector> simplificationRuleListPart7( rules.push_back({ // MUL(X, SHL(Y, 1)) -> SHL(Y, X) - {Pattern::Builtins::MUL, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}}, + Builtins::MUL(X, Builtins::SHL(Y, Word(1))), [=]() -> Pattern { - return {Pattern::Builtins::SHL, {Y, X}}; + return Builtins::SHL(Y, X); }, // Actually only changes the order, does not remove. true }); rules.push_back({ // MUL(SHL(X, 1), Y) -> SHL(X, Y) - {Pattern::Builtins::MUL, {{Pattern::Builtins::SHL, {X, Word(1)}}, Y}}, + Builtins::MUL(Builtins::SHL(X, Word(1)), Y), [=]() -> Pattern { - return {Pattern::Builtins::SHL, {X, Y}}; + return Builtins::SHL(X, Y); }, false }); rules.push_back({ // DIV(X, SHL(Y, 1)) -> SHR(Y, X) - {Pattern::Builtins::DIV, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}}, + Builtins::DIV(X, Builtins::SHL(Y, Word(1))), [=]() -> Pattern { - return {Pattern::Builtins::SHR, {Y, X}}; + return Builtins::SHR(Y, X); }, // Actually only changes the order, does not remove. true @@ -519,16 +533,16 @@ std::vector> simplificationRuleListPart7( rules.push_back({ // AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B) - {Pattern::Builtins::AND, {A, {Pattern::Builtins::SHR, {B, X}}}}, - [=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; }, + Builtins::AND(A, Builtins::SHR(B, X)), + [=]() -> Pattern { return Builtins::SHR(B, X); }, false, feasibilityFunction }); rules.push_back({ // AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B) - {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B, X}}, A}}, - [=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; }, + Builtins::AND(Builtins::SHR(B, X), A), + [=]() -> Pattern { return Builtins::SHR(B, X); }, false, feasibilityFunction }); @@ -545,34 +559,35 @@ std::vector> simplificationRuleListPart8( Pattern Y ) { + using Builtins = typename Pattern::Builtins; std::vector> rules; // move constants across subtractions rules += std::vector>{ { // X - A -> X + (-A) - {Pattern::Builtins::SUB, {X, A}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {X, 0 - A.d()}}; }, + Builtins::SUB(X, A), + [=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); }, false }, { // (X + A) - Y -> (X - Y) + A - {Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {X, A}}, Y}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; }, + Builtins::SUB(Builtins::ADD(X, A), Y), + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, false }, { // (A + X) - Y -> (X - Y) + A - {Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {A, X}}, Y}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; }, + Builtins::SUB(Builtins::ADD(A, X), Y), + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, false }, { // X - (Y + A) -> (X - Y) + (-A) - {Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {Y, A}}}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; }, + Builtins::SUB(X, Builtins::ADD(Y, A)), + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, false }, { // X - (A + Y) -> (X - Y) + (-A) - {Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {A, Y}}}}, - [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; }, + Builtins::SUB(X, Builtins::ADD(A, Y)), + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, false } }; @@ -591,30 +606,31 @@ std::vector> simplificationRuleListPart9( ) { using Word = typename Pattern::Word; + using Builtins = typename Pattern::Builtins; std::vector> rules; assertThrow(Pattern::WordSize > 160, OptimizerException, ""); Word const mask = (Word(1) << 160) - 1; // CREATE rules.push_back({ - {Pattern::Builtins::AND, {{Pattern::Builtins::CREATE, {W, X, Y}}, mask}}, - [=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; }, + Builtins::AND(Builtins::CREATE(W, X, Y), mask), + [=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, false }); rules.push_back({ - {Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE, {W, X, Y}}}}}, - [=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; }, + Builtins::AND(mask, Builtins::CREATE(W, X, Y)), + [=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, false }); // CREATE2 rules.push_back({ - {Pattern::Builtins::AND, {{Pattern::Builtins::CREATE2, {W, X, Y, Z}}, mask}}, - [=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; }, + Builtins::AND(Builtins::CREATE2(W, X, Y, Z), mask), + [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, false }); rules.push_back({ - {Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE2, {W, X, Y, Z}}}}}, - [=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; }, + Builtins::AND(mask, Builtins::CREATE2(W, X, Y, Z)), + [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, false }); diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index b35b89551..f12b859cd 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include namespace dev @@ -55,84 +56,105 @@ struct SimplificationRule std::function feasible; }; +template struct EVMBuiltins { using InstrType = Instruction; - static auto constexpr STOP = Instruction::STOP; - static auto constexpr ADD = Instruction::ADD; - static auto constexpr SUB = Instruction::SUB; - static auto constexpr MUL = Instruction::MUL; - static auto constexpr DIV = Instruction::DIV; - static auto constexpr SDIV = Instruction::SDIV; - static auto constexpr MOD = Instruction::MOD; - static auto constexpr SMOD = Instruction::SMOD; - static auto constexpr EXP = Instruction::EXP; - static auto constexpr NOT = Instruction::NOT; - static auto constexpr LT = Instruction::LT; - static auto constexpr GT = Instruction::GT; - static auto constexpr SLT = Instruction::SLT; - static auto constexpr SGT = Instruction::SGT; - static auto constexpr EQ = Instruction::EQ; - static auto constexpr ISZERO = Instruction::ISZERO; - static auto constexpr AND = Instruction::AND; - static auto constexpr OR = Instruction::OR; - static auto constexpr XOR = Instruction::XOR; - static auto constexpr BYTE = Instruction::BYTE; - static auto constexpr SHL = Instruction::SHL; - static auto constexpr SHR = Instruction::SHR; - static auto constexpr SAR = Instruction::SAR; - static auto constexpr ADDMOD = Instruction::ADDMOD; - static auto constexpr MULMOD = Instruction::MULMOD; - static auto constexpr SIGNEXTEND = Instruction::SIGNEXTEND; - static auto constexpr KECCAK256 = Instruction::KECCAK256; - static auto constexpr ADDRESS = Instruction::ADDRESS; - static auto constexpr BALANCE = Instruction::BALANCE; - static auto constexpr ORIGIN = Instruction::ORIGIN; - static auto constexpr CALLER = Instruction::CALLER; - static auto constexpr CALLVALUE = Instruction::CALLVALUE; - static auto constexpr CALLDATALOAD = Instruction::CALLDATALOAD; - static auto constexpr CALLDATASIZE = Instruction::CALLDATASIZE; - static auto constexpr CALLDATACOPY = Instruction::CALLDATACOPY; - static auto constexpr CODESIZE = Instruction::CODESIZE; - static auto constexpr CODECOPY = Instruction::CODECOPY; - static auto constexpr GASPRICE = Instruction::GASPRICE; - static auto constexpr EXTCODESIZE = Instruction::EXTCODESIZE; - static auto constexpr EXTCODECOPY = Instruction::EXTCODECOPY; - static auto constexpr RETURNDATASIZE = Instruction::RETURNDATASIZE; - static auto constexpr RETURNDATACOPY = Instruction::RETURNDATACOPY; - static auto constexpr EXTCODEHASH = Instruction::EXTCODEHASH; - static auto constexpr BLOCKHASH = Instruction::BLOCKHASH; - static auto constexpr COINBASE = Instruction::COINBASE; - static auto constexpr TIMESTAMP = Instruction::TIMESTAMP; - static auto constexpr NUMBER = Instruction::NUMBER; - static auto constexpr DIFFICULTY = Instruction::DIFFICULTY; - static auto constexpr GASLIMIT = Instruction::GASLIMIT; - static auto constexpr CHAINID = Instruction::CHAINID; - static auto constexpr SELFBALANCE = Instruction::SELFBALANCE; - static auto constexpr POP = Instruction::POP; - static auto constexpr MLOAD = Instruction::MLOAD; - static auto constexpr MSTORE = Instruction::MSTORE; - static auto constexpr MSTORE8 = Instruction::MSTORE8; - static auto constexpr SLOAD = Instruction::SLOAD; - static auto constexpr SSTORE = Instruction::SSTORE; - static auto constexpr PC = Instruction::PC; - static auto constexpr MSIZE = Instruction::MSIZE; - static auto constexpr GAS = Instruction::GAS; - static auto constexpr LOG0 = Instruction::LOG0; - static auto constexpr LOG1 = Instruction::LOG1; - static auto constexpr LOG2 = Instruction::LOG2; - static auto constexpr LOG3 = Instruction::LOG3; - static auto constexpr LOG4 = Instruction::LOG4; - static auto constexpr CREATE = Instruction::CREATE; - static auto constexpr CALL = Instruction::CALL; - static auto constexpr CALLCODE = Instruction::CALLCODE; - static auto constexpr STATICCALL = Instruction::STATICCALL; - static auto constexpr RETURN = Instruction::RETURN; - static auto constexpr DELEGATECALL = Instruction::DELEGATECALL; - static auto constexpr CREATE2 = Instruction::CREATE2; - static auto constexpr REVERT = Instruction::REVERT; - static auto constexpr INVALID = Instruction::INVALID; - static auto constexpr SELFDESTRUCT = Instruction::SELFDESTRUCT; + + template + struct PatternGenerator + { + template constexpr Pattern operator()(Args&&... _args) const + { + return {inst, {std::forward(_args)...}}; + }; + }; + + struct PatternGeneratorInstance + { + Instruction instruction; + template constexpr Pattern operator()(Args&&... _args) const + { + return {instruction, {std::forward(_args)...}}; + }; + }; + + + static auto constexpr STOP = PatternGenerator{}; + static auto constexpr ADD = PatternGenerator{}; + static auto constexpr SUB = PatternGenerator{}; + static auto constexpr MUL = PatternGenerator{}; + static auto constexpr DIV = PatternGenerator{}; + static auto constexpr SDIV = PatternGenerator{}; + static auto constexpr MOD = PatternGenerator{}; + static auto constexpr SMOD = PatternGenerator{}; + static auto constexpr EXP = PatternGenerator{}; + static auto constexpr NOT = PatternGenerator{}; + static auto constexpr LT = PatternGenerator{}; + static auto constexpr GT = PatternGenerator{}; + static auto constexpr SLT = PatternGenerator{}; + static auto constexpr SGT = PatternGenerator{}; + static auto constexpr EQ = PatternGenerator{}; + static auto constexpr ISZERO = PatternGenerator{}; + static auto constexpr AND = PatternGenerator{}; + static auto constexpr OR = PatternGenerator{}; + static auto constexpr XOR = PatternGenerator{}; + static auto constexpr BYTE = PatternGenerator{}; + static auto constexpr SHL = PatternGenerator{}; + static auto constexpr SHR = PatternGenerator{}; + static auto constexpr SAR = PatternGenerator{}; + static auto constexpr ADDMOD = PatternGenerator{}; + static auto constexpr MULMOD = PatternGenerator{}; + static auto constexpr SIGNEXTEND = PatternGenerator{}; + static auto constexpr KECCAK256 = PatternGenerator{}; + static auto constexpr ADDRESS = PatternGenerator{}; + static auto constexpr BALANCE = PatternGenerator{}; + static auto constexpr ORIGIN = PatternGenerator{}; + static auto constexpr CALLER = PatternGenerator{}; + static auto constexpr CALLVALUE = PatternGenerator{}; + static auto constexpr CALLDATALOAD = PatternGenerator{}; + static auto constexpr CALLDATASIZE = PatternGenerator{}; + static auto constexpr CALLDATACOPY = PatternGenerator{}; + static auto constexpr CODESIZE = PatternGenerator{}; + static auto constexpr CODECOPY = PatternGenerator{}; + static auto constexpr GASPRICE = PatternGenerator{}; + static auto constexpr EXTCODESIZE = PatternGenerator{}; + static auto constexpr EXTCODECOPY = PatternGenerator{}; + static auto constexpr RETURNDATASIZE = PatternGenerator{}; + static auto constexpr RETURNDATACOPY = PatternGenerator{}; + static auto constexpr EXTCODEHASH = PatternGenerator{}; + static auto constexpr BLOCKHASH = PatternGenerator{}; + static auto constexpr COINBASE = PatternGenerator{}; + static auto constexpr TIMESTAMP = PatternGenerator{}; + static auto constexpr NUMBER = PatternGenerator{}; + static auto constexpr DIFFICULTY = PatternGenerator{}; + static auto constexpr GASLIMIT = PatternGenerator{}; + static auto constexpr CHAINID = PatternGenerator{}; + static auto constexpr SELFBALANCE = PatternGenerator{}; + static auto constexpr POP = PatternGenerator{}; + static auto constexpr MLOAD = PatternGenerator{}; + static auto constexpr MSTORE = PatternGenerator{}; + static auto constexpr MSTORE8 = PatternGenerator{}; + static auto constexpr SLOAD = PatternGenerator{}; + static auto constexpr SSTORE = PatternGenerator{}; + static auto constexpr PC = PatternGenerator{}; + static auto constexpr MSIZE = PatternGenerator{}; + static auto constexpr GAS = PatternGenerator{}; + static auto constexpr LOG0 = PatternGenerator{}; + static auto constexpr LOG1 = PatternGenerator{}; + static auto constexpr LOG2 = PatternGenerator{}; + static auto constexpr LOG3 = PatternGenerator{}; + static auto constexpr LOG4 = PatternGenerator{}; + static auto constexpr CREATE = PatternGenerator{}; + static auto constexpr CALL = PatternGenerator{}; + static auto constexpr CALLCODE = PatternGenerator{}; + static auto constexpr STATICCALL = PatternGenerator{}; + static auto constexpr RETURN = PatternGenerator{}; + static auto constexpr DELEGATECALL = PatternGenerator{}; + static auto constexpr CREATE2 = PatternGenerator{}; + static auto constexpr REVERT = PatternGenerator{}; + static auto constexpr INVALID = PatternGenerator{}; + static auto constexpr SELFDESTRUCT = PatternGenerator{}; }; } diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index ca201862a..28b64078a 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -99,7 +99,7 @@ Rules::Rules() assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized."); } -Pattern::Pattern(Instruction _instruction, std::vector const& _arguments): +Pattern::Pattern(Instruction _instruction, std::initializer_list _arguments): m_type(Operation), m_instruction(_instruction), m_arguments(_arguments) diff --git a/libevmasm/SimplificationRules.h b/libevmasm/SimplificationRules.h index a8c782b26..a01507411 100644 --- a/libevmasm/SimplificationRules.h +++ b/libevmasm/SimplificationRules.h @@ -26,6 +26,8 @@ #include #include +#include + #include #include @@ -87,18 +89,20 @@ public: using Expression = ExpressionClasses::Expression; using Id = ExpressionClasses::Id; - using Builtins = dev::eth::EVMBuiltins; + using Builtins = dev::eth::EVMBuiltins; static constexpr size_t WordSize = 256; using Word = u256; // Matches a specific constant value. Pattern(unsigned _value): Pattern(u256(_value)) {} + Pattern(int _value): Pattern(u256(_value)) {} + Pattern(long unsigned _value): Pattern(u256(_value)) {} // Matches a specific constant value. Pattern(u256 const& _value): m_type(Push), m_requireDataMatch(true), m_data(std::make_shared(_value)) {} // Matches a specific assembly item type or anything if not given. Pattern(AssemblyItemType _type = UndefinedItem): m_type(_type) {} // Matches a given instruction with given arguments - Pattern(Instruction _instruction, std::vector const& _arguments = {}); + Pattern(Instruction _instruction, std::initializer_list _arguments = {}); /// Sets this pattern to be part of the match group with the identifier @a _group. /// Inside one rule, all patterns in the same match group have to match expressions from the /// same expression equivalence class. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index da4a207a0..b0a5920c0 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -209,17 +209,14 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl) solAssert(symbTuple, ""); auto const& components = symbTuple->components(); auto const& declarations = _varDecl.declarations(); - if (!components.empty()) - { - solAssert(components.size() == declarations.size(), ""); - for (unsigned i = 0; i < declarations.size(); ++i) - if ( - components.at(i) && - declarations.at(i) && - m_context.knownVariable(*declarations.at(i)) - ) - assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type())); - } + solAssert(components.size() == declarations.size(), ""); + for (unsigned i = 0; i < declarations.size(); ++i) + if ( + components.at(i) && + declarations.at(i) && + m_context.knownVariable(*declarations.at(i)) + ) + assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type())); } } else if (m_context.knownVariable(*_varDecl.declarations().front())) @@ -321,24 +318,23 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) { auto const& symbTuple = dynamic_pointer_cast(m_context.expression(_tuple)); solAssert(symbTuple, ""); - if (symbTuple->components().empty()) + auto const& symbComponents = symbTuple->components(); + auto const& tupleComponents = _tuple.components(); + solAssert(symbComponents.size() == _tuple.components().size(), ""); + for (unsigned i = 0; i < symbComponents.size(); ++i) { - vector> components; - for (auto const& component: _tuple.components()) - if (component) - { - if (auto varDecl = identifierToVariable(*component)) - components.push_back(m_context.variable(*varDecl)); - else - { - solAssert(m_context.knownExpression(*component), ""); - components.push_back(m_context.expression(*component)); - } - } + auto sComponent = symbComponents.at(i); + auto tComponent = tupleComponents.at(i); + if (sComponent && tComponent) + { + if (auto varDecl = identifierToVariable(*tComponent)) + m_context.addAssertion(sComponent->currentValue() == currentValue(*varDecl)); else - components.push_back(nullptr); - solAssert(components.size() == _tuple.components().size(), ""); - symbTuple->setComponents(move(components)); + { + solAssert(m_context.knownExpression(*tComponent), ""); + m_context.addAssertion(sComponent->currentValue() == expr(*tComponent)); + } + } } } else @@ -610,12 +606,22 @@ void SMTEncoder::endVisit(Identifier const& _identifier) defineExpr(_identifier, m_context.thisAddress()); m_uninterpretedTerms.insert(&_identifier); } - else if (smt::isSupportedType(_identifier.annotation().type->category())) - // TODO: handle MagicVariableDeclaration here - m_errorReporter.warning( - _identifier.location(), - "Assertion checker does not yet support the type of this variable." - ); + else if ( + _identifier.annotation().type->category() != Type::Category::Modifier + ) + createExpr(_identifier); +} + +void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName) +{ + auto const& typeType = dynamic_cast(*_typeName.annotation().type); + auto result = smt::newSymbolicVariable( + *TypeProvider::uint256(), + typeType.actualType()->toString(false), + m_context + ); + solAssert(!result.first && result.second, ""); + m_context.createExpression(_typeName, result.second); } void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) @@ -1513,15 +1519,18 @@ void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall) { auto const& symbTuple = dynamic_pointer_cast(m_context.expression(_funCall)); solAssert(symbTuple, ""); - if (symbTuple->components().empty()) + auto const& symbComponents = symbTuple->components(); + solAssert(symbComponents.size() == returnParams.size(), ""); + for (unsigned i = 0; i < symbComponents.size(); ++i) { - vector> components; - for (auto param: returnParams) + auto sComponent = symbComponents.at(i); + auto param = returnParams.at(i); + solAssert(param, ""); + if (sComponent) { solAssert(m_context.knownVariable(*param), ""); - components.push_back(m_context.variable(*param)); + m_context.addAssertion(sComponent->currentValue() == currentValue(*param)); } - symbTuple->setComponents(move(components)); } } else if (returnParams.size() == 1) diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index b8305e3bd..843f771e4 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -82,6 +83,7 @@ protected: void endVisit(BinaryOperation const& _node) override; void endVisit(FunctionCall const& _node) override; void endVisit(Identifier const& _node) override; + void endVisit(ElementaryTypeNameExpression const& _node) override; void endVisit(Literal const& _node) override; void endVisit(Return const& _node) override; bool visit(MemberAccess const& _node) override; diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index 3a814ec7d..d0163d381 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -248,12 +248,16 @@ SymbolicTupleVariable::SymbolicTupleVariable( SymbolicVariable(_type, _type, move(_uniqueName), _context) { solAssert(isTuple(m_type->category()), ""); -} - -void SymbolicTupleVariable::setComponents(vector> _components) -{ - solAssert(m_components.empty(), ""); - auto const& tupleType = dynamic_cast(m_type); - solAssert(_components.size() == tupleType->components().size(), ""); - m_components = move(_components); + auto const& tupleType = dynamic_cast(*m_type); + auto const& componentsTypes = tupleType.components(); + for (unsigned i = 0; i < componentsTypes.size(); ++i) + if (componentsTypes.at(i)) + { + string componentName = m_uniqueName + "_component_" + to_string(i); + auto result = smt::newSymbolicVariable(*componentsTypes.at(i), componentName, m_context); + solAssert(result.second, ""); + m_components.emplace_back(move(result.second)); + } + else + m_components.emplace_back(nullptr); } diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index 9ae637490..4d9fcc2f7 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -250,8 +250,6 @@ public: return m_components; } - void setComponents(std::vector> _components); - private: std::vector> m_components; }; diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index b62fb9e6f..1fdbd09e2 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -111,7 +111,7 @@ SimplificationRules::SimplificationRules() assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized."); } -yul::Pattern::Pattern(dev::eth::Instruction _instruction, vector const& _arguments): +yul::Pattern::Pattern(dev::eth::Instruction _instruction, initializer_list _arguments): m_kind(PatternKind::Operation), m_instruction(_instruction), m_arguments(_arguments) diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index 1caf323d8..d07807d89 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -25,6 +25,8 @@ #include #include +#include + #include #include @@ -85,7 +87,7 @@ enum class PatternKind class Pattern { public: - using Builtins = dev::eth::EVMBuiltins; + using Builtins = dev::eth::EVMBuiltins; static constexpr size_t WordSize = 256; using Word = dev::u256; @@ -93,10 +95,12 @@ public: Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {} // Matches a specific constant value. Pattern(unsigned _value): Pattern(dev::u256(_value)) {} + Pattern(int _value): Pattern(dev::u256(_value)) {} + Pattern(long unsigned _value): Pattern(dev::u256(_value)) {} // Matches a specific constant value. Pattern(dev::u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared(_value)) {} // Matches a given instruction with given arguments - Pattern(dev::eth::Instruction _instruction, std::vector const& _arguments = {}); + Pattern(dev::eth::Instruction _instruction, std::initializer_list _arguments = {}); /// Sets this pattern to be part of the match group with the identifier @a _group. /// Inside one rule, all patterns in the same match group have to match expressions from the /// same expression equivalence class. diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index b7755f28f..d2852614e 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -35,17 +35,17 @@ using namespace dev; using namespace dev::test; -evmc::vm* EVMHost::getVM(string const& _path) +evmc::VM* EVMHost::getVM(string const& _path) { - static unique_ptr theVM; + static unique_ptr theVM; if (!theVM && !_path.empty()) { evmc_loader_error_code errorCode = {}; - evmc_instance* vm = evmc_load_and_configure(_path.c_str(), &errorCode); + evmc_vm* vm = evmc_load_and_configure(_path.c_str(), &errorCode); if (vm && errorCode == EVMC_LOADER_SUCCESS) { if (evmc_vm_has_capability(vm, EVMC_CAPABILITY_EVM1)) - theVM = make_unique(vm); + theVM = make_unique(vm); else { evmc_destroy(vm); @@ -63,7 +63,7 @@ evmc::vm* EVMHost::getVM(string const& _path) return theVM.get(); } -EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm): +EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm): m_vm(_vm) { if (!m_vm) @@ -83,7 +83,7 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm): else if (_evmVersion == langutil::EVMVersion::constantinople()) m_evmVersion = EVMC_CONSTANTINOPLE; else if (_evmVersion == langutil::EVMVersion::istanbul()) - assertThrow(false, Exception, "Istanbul is not supported yet."); + m_evmVersion = EVMC_ISTANBUL; else if (_evmVersion == langutil::EVMVersion::berlin()) assertThrow(false, Exception, "Berlin is not supported yet."); else //if (_evmVersion == langutil::EVMVersion::petersburg()) @@ -227,6 +227,8 @@ evmc_tx_context EVMHost::get_tx_context() noexcept ctx.block_gas_limit = 20000000; ctx.tx_gas_price = convertToEVMC(u256("3000000000")); ctx.tx_origin = convertToEVMC(Address("0x9292929292929292929292929292929292929292")); + // Mainnet according to EIP-155 + ctx.chain_id = convertToEVMC(u256(1)); return ctx; } diff --git a/test/EVMHost.h b/test/EVMHost.h index c01d27ad0..2ffcabcfa 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -40,9 +40,9 @@ public: /// Tries to dynamically load libevmone. @returns nullptr on failure. /// The path has to be provided for the first successful run and will be ignored /// afterwards. - static evmc::vm* getVM(std::string const& _path = {}); + static evmc::VM* getVM(std::string const& _path = {}); - explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm = getVM()); + explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm = getVM()); struct Account { @@ -179,7 +179,7 @@ private: /// @note The return value is only valid as long as @a _data is alive! static evmc::result resultWithGas(evmc_message const& _message, bytes const& _data) noexcept; - evmc::vm* m_vm = nullptr; + evmc::VM* m_vm = nullptr; evmc_revision m_evmVersion; }; diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index c826bfeae..fe950d379 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -44,7 +44,7 @@ enum * * @see @ref versioning */ - EVMC_ABI_VERSION = 6 + EVMC_ABI_VERSION = 7 }; @@ -153,9 +153,15 @@ struct evmc_tx_context int64_t block_timestamp; /**< The block timestamp. */ int64_t block_gas_limit; /**< The block gas limit. */ evmc_uint256be block_difficulty; /**< The block difficulty. */ + evmc_uint256be chain_id; /**< The blockchain's ChainID. */ }; -struct evmc_context; +/** + * @struct evmc_host_context + * The opaque data type representing the Host execution context. + * @see evmc_execute_fn(). + */ +struct evmc_host_context; /** * Get transaction context callback function. @@ -166,7 +172,7 @@ struct evmc_context; * @param context The pointer to the Host execution context. * @return The transaction context. */ -typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* context); +typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_host_context* context); /** * Get block hash callback function. @@ -180,7 +186,7 @@ typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* co * @return The block hash or null bytes * if the information about the block is not available. */ -typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_context* context, int64_t number); +typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_host_context* context, int64_t number); /** * The execution status code. @@ -420,7 +426,8 @@ struct evmc_result * @param address The address of the account the query is about. * @return true if exists, false otherwise. */ -typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_address* address); +typedef bool (*evmc_account_exists_fn)(struct evmc_host_context* context, + const evmc_address* address); /** * Get storage callback function. @@ -433,7 +440,7 @@ typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_ * @return The storage value at the given storage key or null bytes * if the account does not exist. */ -typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_context* context, +typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_bytes32* key); @@ -492,7 +499,7 @@ enum evmc_storage_status * @param value The value to be stored. * @return The effect on the storage item. */ -typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_context* context, +typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_bytes32* key, const evmc_bytes32* value); @@ -506,7 +513,7 @@ typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_context* con * @param address The address of the account. * @return The balance of the given account or 0 if the account does not exist. */ -typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context, +typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_host_context* context, const evmc_address* address); /** @@ -519,7 +526,8 @@ typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context, * @param address The address of the account. * @return The size of the code in the account or 0 if the account does not exist. */ -typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc_address* address); +typedef size_t (*evmc_get_code_size_fn)(struct evmc_host_context* context, + const evmc_address* address); /** * Get code size callback function. @@ -532,28 +540,27 @@ typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc * @param address The address of the account. * @return The hash of the code in the account or null bytes if the account does not exist. */ -typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_context* context, +typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_host_context* context, const evmc_address* address); /** * Copy code callback function. * - * This callback function is used by an EVM to request a copy of the code - * of the given account to the memory buffer provided by the EVM. - * The Client MUST copy the requested code, starting with the given offset, - * to the provided memory buffer up to the size of the buffer or the size of - * the code, whichever is smaller. + * This callback function is used by an EVM to request a copy of the code + * of the given account to the memory buffer provided by the EVM. + * The Client MUST copy the requested code, starting with the given offset, + * to the provided memory buffer up to the size of the buffer or the size of + * the code, whichever is smaller. * - * @param context The pointer to the Client execution context. - * @see ::evmc_context. - * @param address The address of the account. - * @param code_offset The offset of the code to copy. - * @param buffer_data The pointer to the memory buffer allocated by the EVM - * to store a copy of the requested code. - * @param buffer_size The size of the memory buffer. - * @return The number of bytes copied to the buffer by the Client. + * @param context The pointer to the Host execution context. See ::evmc_host_context. + * @param address The address of the account. + * @param code_offset The offset of the code to copy. + * @param buffer_data The pointer to the memory buffer allocated by the EVM + * to store a copy of the requested code. + * @param buffer_size The size of the memory buffer. + * @return The number of bytes copied to the buffer by the Client. */ -typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context, +typedef size_t (*evmc_copy_code_fn)(struct evmc_host_context* context, const evmc_address* address, size_t code_offset, uint8_t* buffer_data, @@ -562,34 +569,31 @@ typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context, /** * Selfdestruct callback function. * - * This callback function is used by an EVM to SELFDESTRUCT given contract. - * The execution of the contract will not be stopped, that is up to the EVM. + * This callback function is used by an EVM to SELFDESTRUCT given contract. + * The execution of the contract will not be stopped, that is up to the EVM. * - * @param context The pointer to the Host execution context. - * @see ::evmc_context. - * @param address The address of the contract to be selfdestructed. - * @param beneficiary The address where the remaining ETH is going to be - * transferred. + * @param context The pointer to the Host execution context. See ::evmc_host_context. + * @param address The address of the contract to be selfdestructed. + * @param beneficiary The address where the remaining ETH is going to be transferred. */ -typedef void (*evmc_selfdestruct_fn)(struct evmc_context* context, +typedef void (*evmc_selfdestruct_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_address* beneficiary); /** * Log callback function. * - * This callback function is used by an EVM to inform about a LOG that happened - * during an EVM bytecode execution. - * @param context The pointer to the Host execution context. - * @see ::evmc_context. - * @param address The address of the contract that generated the log. - * @param data The pointer to unindexed data attached to the log. - * @param data_size The length of the data. - * @param topics The pointer to the array of topics attached to the log. - * @param topics_count The number of the topics. Valid values are between - * 0 and 4 inclusively. + * This callback function is used by an EVM to inform about a LOG that happened + * during an EVM bytecode execution. + * + * @param context The pointer to the Host execution context. See ::evmc_host_context. + * @param address The address of the contract that generated the log. + * @param data The pointer to unindexed data attached to the log. + * @param data_size The length of the data. + * @param topics The pointer to the array of topics attached to the log. + * @param topics_count The number of the topics. Valid values are between 0 and 4 inclusively. */ -typedef void (*evmc_emit_log_fn)(struct evmc_context* context, +typedef void (*evmc_emit_log_fn)(struct evmc_host_context* context, const evmc_address* address, const uint8_t* data, size_t data_size, @@ -599,11 +603,11 @@ typedef void (*evmc_emit_log_fn)(struct evmc_context* context, /** * Pointer to the callback function supporting EVM calls. * - * @param context The pointer to the Host execution context. - * @param msg The call parameters. + * @param context The pointer to the Host execution context. + * @param msg The call parameters. * @return The result of the call. */ -typedef struct evmc_result (*evmc_call_fn)(struct evmc_context* context, +typedef struct evmc_result (*evmc_call_fn)(struct evmc_host_context* context, const struct evmc_message* msg); /** @@ -654,31 +658,15 @@ struct evmc_host_interface }; -/** - * Execution context managed by the Host. - * - * The Host MUST pass the pointer to the execution context to ::evmc_execute_fn. - * The VM MUST pass the same pointer back to the Host in every callback function. - * The context MUST contain at least the function table defining - * the context callback interface. - * Optionally, the Host MAY include in the context additional data. - */ -struct evmc_context -{ - /** The Host interface. */ - const struct evmc_host_interface* host; -}; - - /* Forward declaration. */ -struct evmc_instance; +struct evmc_vm; /** - * Destroys the EVM instance. + * Destroys the VM instance. * - * @param evm The EVM instance to be destroyed. + * @param vm The VM instance to be destroyed. */ -typedef void (*evmc_destroy_fn)(struct evmc_instance* evm); +typedef void (*evmc_destroy_fn)(struct evmc_vm* vm); /** * Possible outcomes of evmc_set_option. @@ -691,19 +679,19 @@ enum evmc_set_option_result }; /** - * Configures the EVM instance. + * Configures the VM instance. * - * Allows modifying options of the EVM instance. - * Options: - * - code cache behavior: on, off, read-only, ... - * - optimizations, + * Allows modifying options of the VM instance. + * Options: + * - code cache behavior: on, off, read-only, ... + * - optimizations, * - * @param evm The EVM instance to be configured. - * @param name The option name. NULL-terminated string. Cannot be NULL. - * @param value The new option value. NULL-terminated string. Cannot be NULL. - * @return The outcome of the operation. + * @param vm The VM instance to be configured. + * @param name The option name. NULL-terminated string. Cannot be NULL. + * @param value The new option value. NULL-terminated string. Cannot be NULL. + * @return The outcome of the operation. */ -typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_instance* evm, +typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_vm* vm, char const* name, char const* value); @@ -762,6 +750,7 @@ enum evmc_revision * The Petersburg revision. * * Other names: Constantinople2, ConstantinopleFix. + * * https://eips.ethereum.org/EIPS/eip-1716 */ EVMC_PETERSBURG = 6, @@ -773,23 +762,15 @@ enum evmc_revision */ EVMC_ISTANBUL = 7, + /** + * The Berlin revision. + * + * The spec draft: https://eips.ethereum.org/EIPS/eip-2070. + */ + EVMC_BERLIN = 8, + /** The maximum EVM revision supported. */ - EVMC_MAX_REVISION = EVMC_ISTANBUL, - - - /** - * Reserved for the post-Constantinople upgrade. - * - * @deprecated Replaced with ::EVMC_PETERSBURG. - */ - EVMC_CONSTANTINOPLE2 EVMC_DEPRECATED = EVMC_PETERSBURG, - - /** - * The latests EVM revision supported. - * - * @deprecated Replaced with ::EVMC_MAX_REVISION. - */ - EVMC_LATEST_REVISION EVMC_DEPRECATED = EVMC_MAX_REVISION + EVMC_MAX_REVISION = EVMC_BERLIN }; @@ -798,19 +779,22 @@ enum evmc_revision * * This function MAY be invoked multiple times for a single VM instance. * - * @param instance The VM instance. This argument MUST NOT be NULL. - * @param context The pointer to the Host execution context to be passed - * to the Host interface methods (::evmc_host_interface). - * This argument MUST NOT be NULL unless - * the @p instance has the ::EVMC_CAPABILITY_PRECOMPILES capability. + * @param vm The VM instance. This argument MUST NOT be NULL. + * @param host The Host interface. This argument MUST NOT be NULL unless + * the @p vm has the ::EVMC_CAPABILITY_PRECOMPILES capability. + * @param context The opaque pointer to the Host execution context. + * This argument MAY be NULL. The VM MUST pass the same + * pointer to the methods of the @p host interface. + * The VM MUST NOT dereference the pointer. * @param rev The requested EVM specification revision. * @param msg The call parameters. See ::evmc_message. This argument MUST NOT be NULL. * @param code The reference to the code to be executed. This argument MAY be NULL. * @param code_size The length of the code. If @p code is NULL this argument MUST be 0. * @return The execution result. */ -typedef struct evmc_result (*evmc_execute_fn)(struct evmc_instance* instance, - struct evmc_context* context, +typedef struct evmc_result (*evmc_execute_fn)(struct evmc_vm* vm, + const struct evmc_host_interface* host, + struct evmc_host_context* context, enum evmc_revision rev, const struct evmc_message* msg, uint8_t const* code, @@ -855,98 +839,20 @@ typedef uint32_t evmc_capabilities_flagset; * Return the supported capabilities of the VM instance. * * This function MAY be invoked multiple times for a single VM instance, - * and its value MAY be influenced by calls to evmc_instance::set_option. + * and its value MAY be influenced by calls to evmc_vm::set_option. * - * @param instance The EVM instance. - * @return The supported capabilities of the VM. @see evmc_capabilities. + * @param vm The VM instance. + * @return The supported capabilities of the VM. @see evmc_capabilities. */ -typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_instance* instance); - -/** - * The opaque type representing a Client-side tracer object. - * - * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). - */ -struct evmc_tracer_context; - -/** - * The callback to trace instructions execution in an EVM. - * - * This function informs the Client what instruction has been executed in the EVM implementation - * and what are the results of executing this particular instruction. - * The message level information (like call depth, destination address, etc.) are not provided here. - * This piece of information can be acquired by inspecting messages being sent to the EVM in - * ::evmc_execute_fn and the results of the messages execution. - * - * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). - * - * @param context The pointer to the Client-side tracing context. This allows to - * implement the tracer in OOP manner. - * @param code_offset The current instruction position in the code. - * @param status_code The status code of the instruction execution. - * @param gas_left The amount of the gas left after the instruction execution. - * @param stack_num_items The current EVM stack height after the instruction execution. - * @param pushed_stack_item The top EVM stack item pushed as the result of the instruction - * execution. This value is null when the instruction does not push - * anything to the stack. - * @param memory_size The size of the EVM memory after the instruction execution. - * @param changed_memory_offset The offset in number of bytes of the beginning of the memory area - * modified as the result of the instruction execution. - * The Client MAY use this information together with - * @p changed_memory_size and @p changed_memory to incrementally - * update the copy of the full VM's memory. - * @param changed_memory_size The size of the memory area modified as the result of - * the instruction execution. - * @param changed_memory The pointer to the memory area modified as the result of - * the instruction execution. - * The Client MAY access the pointed memory area - * (limited by the @p changed_memory_size) only during the current - * execution of the evmc_trace_callback(). - * The pointer MUST NOT be stored by the Client. - * The Client MUST NOT assume that - * `changed_memory - changed_memory_offset` is a valid base pointer - * of the VM memory. - */ -typedef void (*evmc_trace_callback)(struct evmc_tracer_context* context, - size_t code_offset, - enum evmc_status_code status_code, - int64_t gas_left, - size_t stack_num_items, - const evmc_uint256be* pushed_stack_item, - size_t memory_size, - size_t changed_memory_offset, - size_t changed_memory_size, - const uint8_t* changed_memory); - -/** - * Sets the EVM instruction tracer. - * - * When the tracer is set in the EVM instance, the EVM SHOULD call back the tracer with information - * about instructions execution in the EVM. - * @see ::evmc_trace_callback. - * - * This will overwrite the previous settings (the callback and the context). - * - * @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer(). - * - * @param instance The EVM instance. - * @param callback The tracer callback function. This argument MAY be NULL to disable previously - * set tracer. - * @param context The Client-side tracer context. This argument MAY be NULL in case the tracer - * does not require any context. This argument MUST be NULL if the callback - * argument is NULL. - */ -typedef void (*evmc_set_tracer_fn)(struct evmc_instance* instance, - evmc_trace_callback callback, - struct evmc_tracer_context* context); +typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_vm* vm); /** - * The EVM instance. + * The VM instance. * * Defines the base struct of the VM implementation. */ -struct evmc_instance +struct evmc_vm { /** * EVMC ABI version implemented by the VM instance. @@ -973,14 +879,14 @@ struct evmc_instance const char* version; /** - * Pointer to function destroying the EVM instance. + * Pointer to function destroying the VM instance. * * This is a mandatory method and MUST NOT be set to NULL. */ evmc_destroy_fn destroy; /** - * Pointer to function executing a code by the EVM instance. + * Pointer to function executing a code by the VM instance. * * This is a mandatory method and MUST NOT be set to NULL. */ @@ -998,17 +904,6 @@ struct evmc_instance */ evmc_get_capabilities_fn get_capabilities; - /** - * Optional pointer to function setting the EVM instruction tracer. - * - * If the EVM does not support this feature the pointer can be NULL. - * - * @deprecated - * Since EVMC 6.3, the tracing API has been deprecated as there have been some - * design flaws discovered. New API is expected to be introduced in future. - */ - evmc_set_tracer_fn set_tracer; - /** * Optional pointer to function modifying VM's options. * @@ -1033,9 +928,9 @@ struct evmc_instance * For example, the shared library with the "beta-interpreter" implementation may be named * `libbeta-interpreter.so`. * - * @return EVM instance or NULL indicating instance creation failure. + * @return The VM instance or NULL indicating instance creation failure. */ -struct evmc_instance* evmc_create_example_vm(void); +struct evmc_vm* evmc_create_example_vm(void); #endif #if __cplusplus diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp index ff486a232..1b52ae8d0 100644 --- a/test/evmc/evmc.hpp +++ b/test/evmc/evmc.hpp @@ -334,49 +334,46 @@ public: } }; -/// @copybrief evmc_instance +class Host; + +/// @copybrief evmc_vm /// -/// This is a RAII wrapper for evmc_instance and objects of this type +/// This is a RAII wrapper for evmc_vm and objects of this type /// automatically destroys the VM instance. -class vm +class VM { public: - vm() noexcept = default; + VM() noexcept = default; - /// Converting constructor from evmc_instance. - explicit vm(evmc_instance* instance) noexcept : m_instance{instance} {} + /// Converting constructor from evmc_vm. + explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {} /// Destructor responsible for automatically destroying the VM instance. - ~vm() noexcept + ~VM() noexcept { if (m_instance) m_instance->destroy(m_instance); } - vm(const vm&) = delete; - vm& operator=(const vm&) = delete; + VM(const VM&) = delete; + VM& operator=(const VM&) = delete; /// Move constructor. - vm(vm&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; } + VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; } /// Move assignment operator. - vm& operator=(vm&& other) noexcept + VM& operator=(VM&& other) noexcept { - this->~vm(); + this->~VM(); m_instance = other.m_instance; other.m_instance = nullptr; return *this; } /// The constructor that captures a VM instance and configures the instance - /// with provided list of options. - vm(evmc_instance* instance, - std::initializer_list> options) noexcept - : m_instance{instance} - { - for (auto option : options) - set_option(option.first, option.second); - } + /// with the provided list of options. + inline VM(evmc_vm* vm, + std::initializer_list> options) noexcept; /// Checks if contains a valid pointer to the VM instance. explicit operator bool() const noexcept { return m_instance != nullptr; } @@ -384,13 +381,13 @@ public: /// Checks whenever the VM instance is ABI compatible with the current EVMC API. bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; } - /// @copydoc evmc_instance::name + /// @copydoc evmc_vm::name char const* name() const noexcept { return m_instance->name; } - /// @copydoc evmc_instance::version + /// @copydoc evmc_vm::version char const* version() const noexcept { return m_instance->version; } - /// @copydoc evmc::instance::get_capabilities + /// @copydoc evmc::vm::get_capabilities evmc_capabilities_flagset get_capabilities() const noexcept { return m_instance->get_capabilities(m_instance); @@ -403,19 +400,53 @@ public: } /// @copydoc evmc_execute() - result execute(evmc_context& ctx, + result execute(const evmc_host_interface& host, + evmc_host_context* ctx, evmc_revision rev, const evmc_message& msg, const uint8_t* code, size_t code_size) noexcept { - return result{m_instance->execute(m_instance, &ctx, rev, &msg, code, code_size)}; + return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)}; + } + + /// Convenient variant of the VM::execute() that takes reference to evmc::Host class. + inline result execute(Host& host, + evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept; + + /// Executes code without the Host context. + /// + /// The same as + /// execute(const evmc_host_interface&, evmc_host_context*, evmc_revision, + /// const evmc_message&, const uint8_t*, size_t), + /// but without providing the Host context and interface. + /// This method is for experimental precompiles support where execution is + /// guaranteed not to require any Host access. + result execute(evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept + { + return result{ + m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)}; } private: - evmc_instance* m_instance = nullptr; + evmc_vm* m_instance = nullptr; }; +inline VM::VM(evmc_vm* vm, + std::initializer_list> options) noexcept + : m_instance{vm} +{ + for (const auto& option : options) + set_option(option.first, option.second); +} + + /// The EVMC Host interface class HostInterface { @@ -471,46 +502,52 @@ public: /// Wrapper around EVMC host context / host interface. /// -/// To be used by VM implementations as better alternative to using ::evmc_context directly. +/// To be used by VM implementations as better alternative to using ::evmc_host_context directly. class HostContext : public HostInterface { - evmc_context* context = nullptr; + const evmc_host_interface* host = nullptr; + evmc_host_context* context = nullptr; evmc_tx_context tx_context = {}; public: - /// Implicit converting constructor from evmc_context. - HostContext(evmc_context* ctx) noexcept : context{ctx} {} // NOLINT + /// Default constructor for null Host context. + HostContext() = default; + + /// Constructor from the EVMC Host primitives. + HostContext(const evmc_host_interface* interface, evmc_host_context* ctx) noexcept + : host{interface}, context{ctx} + {} bool account_exists(const address& address) noexcept final { - return context->host->account_exists(context, &address); + return host->account_exists(context, &address); } bytes32 get_storage(const address& address, const bytes32& key) noexcept final { - return context->host->get_storage(context, &address, &key); + return host->get_storage(context, &address, &key); } evmc_storage_status set_storage(const address& address, const bytes32& key, const bytes32& value) noexcept final { - return context->host->set_storage(context, &address, &key, &value); + return host->set_storage(context, &address, &key, &value); } uint256be get_balance(const address& address) noexcept final { - return context->host->get_balance(context, &address); + return host->get_balance(context, &address); } size_t get_code_size(const address& address) noexcept final { - return context->host->get_code_size(context, &address); + return host->get_code_size(context, &address); } bytes32 get_code_hash(const address& address) noexcept final { - return context->host->get_code_hash(context, &address); + return host->get_code_hash(context, &address); } size_t copy_code(const address& address, @@ -518,17 +555,17 @@ public: uint8_t* buffer_data, size_t buffer_size) noexcept final { - return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size); + return host->copy_code(context, &address, code_offset, buffer_data, buffer_size); } void selfdestruct(const address& addr, const address& beneficiary) noexcept final { - context->host->selfdestruct(context, &addr, &beneficiary); + host->selfdestruct(context, &addr, &beneficiary); } result call(const evmc_message& message) noexcept final { - return result{context->host->call(context, &message)}; + return result{host->call(context, &message)}; } /// @copydoc HostInterface::get_tx_context() @@ -540,13 +577,13 @@ public: evmc_tx_context get_tx_context() noexcept final { if (tx_context.block_timestamp == 0) - tx_context = context->host->get_tx_context(context); + tx_context = host->get_tx_context(context); return tx_context; } bytes32 get_block_hash(int64_t number) noexcept final { - return context->host->get_block_hash(context, number); + return host->get_block_hash(context, number); } void emit_log(const address& addr, @@ -555,7 +592,7 @@ public: const bytes32 topics[], size_t topics_count) noexcept final { - context->host->emit_log(context, &addr, data, data_size, topics, topics_count); + host->emit_log(context, &addr, data, data_size, topics, topics_count); } }; @@ -563,89 +600,135 @@ public: /// /// When implementing EVMC Host, you can directly inherit from the evmc::Host class. /// This way your implementation will be simpler by avoiding manual handling -/// of the ::evmc_context and the ::evmc_context::host. -class Host : public HostInterface, public evmc_context +/// of the ::evmc_host_context and the ::evmc_host_interface. +class Host : public HostInterface { public: - inline Host() noexcept; + /// Provides access to the global host interface. + /// @returns Reference to the host interface object. + static const evmc_host_interface& get_interface() noexcept; + + /// Converts the Host object to the opaque host context pointer. + /// @returns Pointer to evmc_host_context. + evmc_host_context* to_context() noexcept { return reinterpret_cast(this); } + + /// Converts the opaque host context pointer back to the original Host object. + /// @tparam DerivedClass The class derived from the Host class. + /// @param context The opaque host context pointer. + /// @returns The pointer to DerivedClass. + template + static DerivedClass* from_context(evmc_host_context* context) noexcept + { + // Get pointer of the Host base class. + auto* h = reinterpret_cast(context); + + // Additional downcast, only possible if DerivedClass inherits from Host. + return static_cast(h); + } }; + +inline result VM::execute(Host& host, + evmc_revision rev, + const evmc_message& msg, + const uint8_t* code, + size_t code_size) noexcept +{ + return execute(Host::get_interface(), host.to_context(), rev, msg, code, code_size); +} + + namespace internal { -inline bool account_exists(evmc_context* h, const evmc_address* addr) noexcept +inline bool account_exists(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->account_exists(*addr); + return Host::from_context(h)->account_exists(*addr); } -inline evmc_bytes32 get_storage(evmc_context* h, + +inline evmc_bytes32 get_storage(evmc_host_context* h, const evmc_address* addr, const evmc_bytes32* key) noexcept { - return static_cast(h)->get_storage(*addr, *key); + return Host::from_context(h)->get_storage(*addr, *key); } -inline evmc_storage_status set_storage(evmc_context* h, + +inline evmc_storage_status set_storage(evmc_host_context* h, const evmc_address* addr, const evmc_bytes32* key, const evmc_bytes32* value) noexcept { - return static_cast(h)->set_storage(*addr, *key, *value); + return Host::from_context(h)->set_storage(*addr, *key, *value); } -inline evmc_uint256be get_balance(evmc_context* h, const evmc_address* addr) noexcept + +inline evmc_uint256be get_balance(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->get_balance(*addr); + return Host::from_context(h)->get_balance(*addr); } -inline size_t get_code_size(evmc_context* h, const evmc_address* addr) noexcept + +inline size_t get_code_size(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->get_code_size(*addr); + return Host::from_context(h)->get_code_size(*addr); } -inline evmc_bytes32 get_code_hash(evmc_context* h, const evmc_address* addr) noexcept + +inline evmc_bytes32 get_code_hash(evmc_host_context* h, const evmc_address* addr) noexcept { - return static_cast(h)->get_code_hash(*addr); + return Host::from_context(h)->get_code_hash(*addr); } -inline size_t copy_code(evmc_context* h, + +inline size_t copy_code(evmc_host_context* h, const evmc_address* addr, size_t code_offset, uint8_t* buffer_data, size_t buffer_size) noexcept { - return static_cast(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); + return Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size); } -inline void selfdestruct(evmc_context* h, + +inline void selfdestruct(evmc_host_context* h, const evmc_address* addr, const evmc_address* beneficiary) noexcept { - static_cast(h)->selfdestruct(*addr, *beneficiary); + Host::from_context(h)->selfdestruct(*addr, *beneficiary); } -inline evmc_result call(evmc_context* h, const evmc_message* msg) noexcept + +inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept { - return static_cast(h)->call(*msg).release_raw(); + return Host::from_context(h)->call(*msg).release_raw(); } -inline evmc_tx_context get_tx_context(evmc_context* h) noexcept + +inline evmc_tx_context get_tx_context(evmc_host_context* h) noexcept { - return static_cast(h)->get_tx_context(); + return Host::from_context(h)->get_tx_context(); } -inline evmc_bytes32 get_block_hash(evmc_context* h, int64_t block_number) noexcept + +inline evmc_bytes32 get_block_hash(evmc_host_context* h, int64_t block_number) noexcept { - return static_cast(h)->get_block_hash(block_number); + return Host::from_context(h)->get_block_hash(block_number); } -inline void emit_log(evmc_context* h, + +inline void emit_log(evmc_host_context* h, const evmc_address* addr, const uint8_t* data, size_t data_size, const evmc_bytes32 topics[], size_t num_topics) noexcept { - static_cast(h)->emit_log(*addr, data, data_size, static_cast(topics), + Host::from_context(h)->emit_log(*addr, data, data_size, static_cast(topics), num_topics); } - -constexpr evmc_host_interface interface{ - account_exists, get_storage, set_storage, get_balance, get_code_size, get_code_hash, - copy_code, selfdestruct, call, get_tx_context, get_block_hash, emit_log, -}; } // namespace internal -inline Host::Host() noexcept : evmc_context{&evmc::internal::interface} {} - +inline const evmc_host_interface& Host::get_interface() noexcept +{ + static constexpr evmc_host_interface interface{ + ::evmc::internal::account_exists, ::evmc::internal::get_storage, + ::evmc::internal::set_storage, ::evmc::internal::get_balance, + ::evmc::internal::get_code_size, ::evmc::internal::get_code_hash, + ::evmc::internal::copy_code, ::evmc::internal::selfdestruct, + ::evmc::internal::call, ::evmc::internal::get_tx_context, + ::evmc::internal::get_block_hash, ::evmc::internal::emit_log}; + return interface; +} } // namespace evmc diff --git a/test/evmc/helpers.h b/test/evmc/helpers.h index 2c4dbceae..36febfd72 100644 --- a/test/evmc/helpers.h +++ b/test/evmc/helpers.h @@ -22,36 +22,35 @@ #include /** - * Returns true if the VM instance has a compatible ABI version. + * Returns true if the VM has a compatible ABI version. */ -static inline int evmc_is_abi_compatible(struct evmc_instance* instance) +static inline bool evmc_is_abi_compatible(struct evmc_vm* vm) { - return instance->abi_version == EVMC_ABI_VERSION; + return vm->abi_version == EVMC_ABI_VERSION; } /** - * Returns the name of the VM instance. + * Returns the name of the VM. */ -static inline const char* evmc_vm_name(struct evmc_instance* instance) +static inline const char* evmc_vm_name(struct evmc_vm* vm) { - return instance->name; + return vm->name; } /** - * Returns the version of the VM instance. + * Returns the version of the VM. */ -static inline const char* evmc_vm_version(struct evmc_instance* instance) +static inline const char* evmc_vm_version(struct evmc_vm* vm) { - return instance->version; + return vm->version; } /** - * Checks if the VM instance has the given capability. + * Checks if the VM has the given capability. * * @see evmc_get_capabilities_fn */ -static inline bool evmc_vm_has_capability(struct evmc_instance* vm, - enum evmc_capabilities capability) +static inline bool evmc_vm_has_capability(struct evmc_vm* vm, enum evmc_capabilities capability) { return (vm->get_capabilities(vm) & (evmc_capabilities_flagset)capability) != 0; } @@ -61,52 +60,39 @@ static inline bool evmc_vm_has_capability(struct evmc_instance* vm, * * @see evmc_destroy_fn */ -static inline void evmc_destroy(struct evmc_instance* instance) +static inline void evmc_destroy(struct evmc_vm* vm) { - instance->destroy(instance); + vm->destroy(vm); } /** - * Sets the option for the VM instance, if the feature is supported by the VM. + * Sets the option for the VM, if the feature is supported by the VM. * * @see evmc_set_option_fn */ -static inline enum evmc_set_option_result evmc_set_option(struct evmc_instance* instance, +static inline enum evmc_set_option_result evmc_set_option(struct evmc_vm* vm, char const* name, char const* value) { - if (instance->set_option) - return instance->set_option(instance, name, value); + if (vm->set_option) + return vm->set_option(vm, name, value); return EVMC_SET_OPTION_INVALID_NAME; } -/** - * Sets the tracer callback for the VM instance, if the feature is supported by the VM. - * - * @see evmc_set_tracer_fn - */ -EVMC_DEPRECATED -static inline void evmc_set_tracer(struct evmc_instance* instance, - evmc_trace_callback callback, - struct evmc_tracer_context* context) -{ - if (instance->set_tracer) - instance->set_tracer(instance, callback, context); -} - /** * Executes code in the VM instance. * * @see evmc_execute_fn. */ -static inline struct evmc_result evmc_execute(struct evmc_instance* instance, - struct evmc_context* context, +static inline struct evmc_result evmc_execute(struct evmc_vm* vm, + const struct evmc_host_interface* host, + struct evmc_host_context* context, enum evmc_revision rev, const struct evmc_message* msg, uint8_t const* code, size_t code_size) { - return instance->execute(instance, context, rev, msg, code, code_size); + return vm->execute(vm, host, context, rev, msg, code, code_size); } /// The evmc_result release function using free() for releasing the memory. diff --git a/test/evmc/loader.c b/test/evmc/loader.c index 30bb39023..d82005438 100644 --- a/test/evmc/loader.c +++ b/test/evmc/loader.c @@ -152,8 +152,8 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro char* base_name = prefixed_name + prefix_length; strcpy_sx(base_name, PATH_MAX_LENGTH, name_pos); - // Trim the file extension. - char* ext_pos = strrchr(prefixed_name, '.'); + // Trim all file extensions. + char* ext_pos = strchr(prefixed_name, '.'); if (ext_pos) *ext_pos = 0; @@ -163,15 +163,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro *dash_pos++ = '_'; // Search for the built function name. - while ((create_fn = DLL_GET_CREATE_FN(handle, prefixed_name)) == NULL) - { - // Shorten the base name by skipping the `word_` segment. - const char* shorter_name_pos = strchr(base_name, '_'); - if (!shorter_name_pos) - break; - - memmove(base_name, shorter_name_pos + 1, strlen(shorter_name_pos) + 1); - } + create_fn = DLL_GET_CREATE_FN(handle, prefixed_name); if (!create_fn) create_fn = DLL_GET_CREATE_FN(handle, "evmc_create"); @@ -196,8 +188,7 @@ const char* evmc_last_error_msg() return m; } -struct evmc_instance* evmc_load_and_create(const char* filename, - enum evmc_loader_error_code* error_code) +struct evmc_vm* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code) { // First load the DLL. This also resets the last_error_msg; evmc_create_fn create_fn = evmc_load(filename, error_code); @@ -207,21 +198,21 @@ struct evmc_instance* evmc_load_and_create(const char* filename, enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS; - struct evmc_instance* instance = create_fn(); - if (!instance) + struct evmc_vm* vm = create_fn(); + if (!vm) { - ec = set_error(EVMC_LOADER_INSTANCE_CREATION_FAILURE, - "creating EVMC instance of %s has failed", filename); + ec = set_error(EVMC_LOADER_VM_CREATION_FAILURE, "creating EVMC VM of %s has failed", + filename); goto exit; } - if (!evmc_is_abi_compatible(instance)) + if (!evmc_is_abi_compatible(vm)) { ec = set_error(EVMC_LOADER_ABI_VERSION_MISMATCH, "EVMC ABI version %d of %s mismatches the expected version %d", - instance->abi_version, filename, EVMC_ABI_VERSION); - evmc_destroy(instance); - instance = NULL; + vm->abi_version, filename, EVMC_ABI_VERSION); + evmc_destroy(vm); + vm = NULL; goto exit; } @@ -229,7 +220,7 @@ exit: if (error_code) *error_code = ec; - return instance; + return vm; } /// Gets the token delimited by @p delim character of the string pointed by the @p str_ptr. @@ -255,11 +246,10 @@ static char* get_token(char** str_ptr, char delim) return str; } -struct evmc_instance* evmc_load_and_configure(const char* config, - enum evmc_loader_error_code* error_code) +struct evmc_vm* evmc_load_and_configure(const char* config, enum evmc_loader_error_code* error_code) { enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS; - struct evmc_instance* instance = NULL; + struct evmc_vm* vm = NULL; char config_copy_buffer[PATH_MAX_LENGTH]; if (strcpy_sx(config_copy_buffer, sizeof(config_copy_buffer), config) != 0) @@ -273,14 +263,14 @@ struct evmc_instance* evmc_load_and_configure(const char* config, char* options = config_copy_buffer; const char* path = get_token(&options, ','); - instance = evmc_load_and_create(path, error_code); - if (!instance) + vm = evmc_load_and_create(path, error_code); + if (!vm) return NULL; - if (instance->set_option == NULL && strlen(options) != 0) + if (vm->set_option == NULL && strlen(options) != 0) { ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options", - instance->name, path); + vm->name, path); goto exit; } @@ -293,18 +283,18 @@ struct evmc_instance* evmc_load_and_configure(const char* config, // The option variable will have the value, can be empty. const char* name = get_token(&option, '='); - enum evmc_set_option_result r = instance->set_option(instance, name, option); + enum evmc_set_option_result r = vm->set_option(vm, name, option); switch (r) { case EVMC_SET_OPTION_SUCCESS: break; case EVMC_SET_OPTION_INVALID_NAME: ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s): unknown option '%s'", - instance->name, path, name); + vm->name, path, name); goto exit; case EVMC_SET_OPTION_INVALID_VALUE: ec = set_error(EVMC_LOADER_INVALID_OPTION_VALUE, - "%s (%s): unsupported value '%s' for option '%s'", instance->name, path, + "%s (%s): unsupported value '%s' for option '%s'", vm->name, path, option, name); goto exit; } @@ -315,9 +305,9 @@ exit: *error_code = ec; if (ec == EVMC_LOADER_SUCCESS) - return instance; + return vm; - if (instance) - evmc_destroy(instance); + if (vm) + evmc_destroy(vm); return NULL; } diff --git a/test/evmc/loader.h b/test/evmc/loader.h index 0c50a81f4..523139e31 100644 --- a/test/evmc/loader.h +++ b/test/evmc/loader.h @@ -19,7 +19,7 @@ extern "C" { #endif /** The function pointer type for EVMC create functions. */ -typedef struct evmc_instance* (*evmc_create_fn)(void); +typedef struct evmc_vm* (*evmc_create_fn)(void); /** Error codes for the EVMC loader. */ enum evmc_loader_error_code @@ -37,7 +37,7 @@ enum evmc_loader_error_code EVMC_LOADER_INVALID_ARGUMENT = 3, /** The creation of a VM instance has failed. */ - EVMC_LOADER_INSTANCE_CREATION_FAILURE = 4, + EVMC_LOADER_VM_CREATION_FAILURE = 4, /** The ABI version of the VM instance has mismatched. */ EVMC_LOADER_ABI_VERSION_MISMATCH = 5, @@ -61,21 +61,16 @@ enum evmc_loader_error_code * After the DLL is successfully loaded the function tries to find the EVM create function in the * library. The `filename` is used to guess the EVM name and the name of the create function. * The create function name is constructed by the following rules. Consider example path: - * "/ethereum/libexample-interpreter.so". + * "/ethereum/libexample-interpreter.so.1.0". * - the filename is taken from the path: - * "libexample-interpreter.so", - * - the "lib" prefix and file extension are stripped from the name: + * "libexample-interpreter.so.1.0", + * - the "lib" prefix and all file extensions are stripped from the name: * "example-interpreter" * - all "-" are replaced with "_" to construct _base name_: * "example_interpreter", * - the function name "evmc_create_" + _base name_ is searched in the library: * "evmc_create_example_interpreter", - * - if function not found, the _base name_ is shorten by skipping the first word separated by "_": - * "interpreter", - * - then, the function of the shorter name "evmc_create_" + _base name_ is searched in the library: - * "evmc_create_interpreter", - * - the name shortening continues until a function is found or the name cannot be shorten more, - * - lastly, when no function found, the function name "evmc_create" is searched in the library. + * - if the function is not found, the function name "evmc_create" is searched in the library. * * If the create function is found in the library, the pointer to the function is returned. * Otherwise, the ::EVMC_LOADER_SYMBOL_NOT_FOUND error code is signaled and NULL is returned. @@ -98,7 +93,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro * * This is a macro for creating the VM instance with the function returned from evmc_load(). * The function signals the same errors as evmc_load() and additionally: - * - ::EVMC_LOADER_INSTANCE_CREATION_FAILURE when the create function returns NULL, + * - ::EVMC_LOADER_VM_CREATION_FAILURE when the create function returns NULL, * - ::EVMC_LOADER_ABI_VERSION_MISMATCH when the created VM instance has ABI version different * from the ABI version of this library (::EVMC_ABI_VERSION). * @@ -114,8 +109,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro * ::EVMC_LOADER_SUCCESS on success or any other error code as described above. * @return The pointer to the created VM or NULL in case of error. */ -struct evmc_instance* evmc_load_and_create(const char* filename, - enum evmc_loader_error_code* error_code); +struct evmc_vm* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code); /** * Dynamically loads the EVMC module, then creates and configures the VM instance. @@ -151,8 +145,8 @@ struct evmc_instance* evmc_load_and_create(const char* filename, * ::EVMC_LOADER_SUCCESS on success or any other error code as described above. * @return The pointer to the created VM or NULL in case of error. */ -struct evmc_instance* evmc_load_and_configure(const char* config, - enum evmc_loader_error_code* error_code); +struct evmc_vm* evmc_load_and_configure(const char* config, + enum evmc_loader_error_code* error_code); /** * Returns the human-readable message describing the most recent error diff --git a/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol b/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol index e4d8f4cfe..955d5fd1e 100644 --- a/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol +++ b/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol @@ -35,6 +35,8 @@ library MerkleProof { // ---- // Warning: (755-767): Assertion checker does not yet support this expression. +// Warning: (988-991): Assertion checker does not yet implement type abi // Warning: (988-1032): Assertion checker does not yet implement this type of function call. +// Warning: (1175-1178): Assertion checker does not yet implement type abi // Warning: (1175-1219): Assertion checker does not yet implement this type of function call. // Warning: (755-767): Assertion checker does not yet support this expression. diff --git a/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol b/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol index 6db44a5ef..43bd10cf7 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol @@ -83,7 +83,7 @@ contract InternalCall { // Warning: (1144-1206): Function state mutability can be restricted to pure // Warning: (1212-1274): Function state mutability can be restricted to pure // Warning: (1280-1342): Function state mutability can be restricted to pure +// Warning: (771-774): Assertion checker does not yet implement type abi // Warning: (782-813): Type conversion is not yet fully supported and might yield false positives. // Warning: (771-814): Assertion checker does not yet implement this type of function call. -// Warning: (825-830): Assertion checker does not yet support the type of this variable. // Warning: (1403-1408): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol b/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol index f6c6f89c2..33cf71e81 100644 --- a/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol +++ b/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol @@ -9,5 +9,6 @@ contract C { // ---- // Warning: (133-143): Unused local variable. // Warning: (133-143): Assertion checker does not yet support the type of this variable. +// Warning: (146-147): Assertion checker does not yet implement type type(struct C.A storage pointer) // Warning: (146-163): Assertion checker does not yet implement type struct C.A memory // Warning: (146-163): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol b/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol index 8c3ef4aff..bed409b90 100644 --- a/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol +++ b/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol @@ -6,5 +6,7 @@ contract C { } // ---- // Warning: (31-64): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning: (162-165): Assertion checker does not yet implement type abi // Warning: (162-176): Assertion checker does not yet implement this type of function call. +// Warning: (178-181): Assertion checker does not yet implement type abi // Warning: (178-203): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol b/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol index 2ceb9e607..85387017b 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol @@ -16,3 +16,5 @@ contract C assert(y < 10000); } } +// ---- +// Warning: (228-229): Assertion checker does not yet implement type type(library L) diff --git a/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol index 324197004..5eda70213 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol @@ -17,4 +17,5 @@ contract C } } // ---- +// Warning: (228-229): Assertion checker does not yet implement type type(library L) // Warning: (245-261): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol new file mode 100644 index 000000000..e469f06ba --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +pragma experimental "ABIEncoderV2"; + +contract C { + struct S { uint x; uint[] b; } + function f() public pure returns (S memory, bytes memory) { + return abi.decode("abc", (S, bytes)); + } +} +// ---- +// Warning: (32-67): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning: (151-159): Assertion checker does not yet support the type of this variable. +// Warning: (188-191): Assertion checker does not yet implement type abi +// Warning: (207-208): Assertion checker does not yet implement type type(struct C.S storage pointer) +// Warning: (188-217): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol new file mode 100644 index 000000000..cf86e8e78 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; +pragma experimental "ABIEncoderV2"; + +contract C { + function f() public pure { + (uint x1, bool b1) = abi.decode("abc", (uint, bool)); + (uint x2, bool b2) = abi.decode("abc", (uint, bool)); + // False positive until abi.* are implemented as uninterpreted functions. + assert(x1 == x2); + } +} +// ---- +// Warning: (32-67): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning: (125-132): Unused local variable. +// Warning: (183-190): Unused local variable. +// Warning: (136-139): Assertion checker does not yet implement type abi +// Warning: (136-167): Assertion checker does not yet implement this type of function call. +// Warning: (194-197): Assertion checker does not yet implement type abi +// Warning: (194-225): Assertion checker does not yet implement this type of function call. +// Warning: (303-319): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol new file mode 100644 index 000000000..c06577e0f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure { + (uint a1, bytes32 b1, C c1) = abi.decode("abc", (uint, bytes32, C)); + (uint a2, bytes32 b2, C c2) = abi.decode("abc", (uint, bytes32, C)); + // False positive until abi.* are implemented as uninterpreted functions. + assert(a1 == a2); + assert(a1 != a2); + } + +} +// ---- +// Warning: (88-98): Unused local variable. +// Warning: (100-104): Unused local variable. +// Warning: (161-171): Unused local variable. +// Warning: (173-177): Unused local variable. +// Warning: (108-111): Assertion checker does not yet implement type abi +// Warning: (142-143): Assertion checker does not yet implement type type(contract C) +// Warning: (108-145): Assertion checker does not yet implement this type of function call. +// Warning: (181-184): Assertion checker does not yet implement type abi +// Warning: (215-216): Assertion checker does not yet implement type type(contract C) +// Warning: (181-218): Assertion checker does not yet implement this type of function call. +// Warning: (296-312): Assertion violation happens here +// Warning: (315-331): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol b/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol index 44c252868..33a7d972b 100644 --- a/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol +++ b/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol @@ -10,5 +10,6 @@ contract C } } // ---- +// Warning: (132-133): Assertion checker does not yet implement type type(enum C.D) // Warning: (132-136): Type conversion is not yet fully supported and might yield false positives. // Warning: (140-160): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/address_call.sol b/test/libsolidity/smtCheckerTests/types/address_call.sol index d6d165591..da87778e6 100644 --- a/test/libsolidity/smtCheckerTests/types/address_call.sol +++ b/test/libsolidity/smtCheckerTests/types/address_call.sol @@ -20,3 +20,6 @@ contract C // ---- // Warning: (224-240): Unused local variable. // Warning: (260-275): Assertion violation happens here +// Warning: (279-293): Assertion violation happens here +// Warning: (297-316): Assertion violation happens here +// Warning: (320-344): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol b/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol index 06925a66b..b9d3bcaea 100644 --- a/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol +++ b/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol @@ -20,3 +20,6 @@ contract C // ---- // Warning: (224-240): Unused local variable. // Warning: (268-283): Assertion violation happens here +// Warning: (287-301): Assertion violation happens here +// Warning: (305-324): Assertion violation happens here +// Warning: (328-352): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/address_staticcall.sol b/test/libsolidity/smtCheckerTests/types/address_staticcall.sol index 529ce7ac0..32e4cbb9a 100644 --- a/test/libsolidity/smtCheckerTests/types/address_staticcall.sol +++ b/test/libsolidity/smtCheckerTests/types/address_staticcall.sol @@ -20,3 +20,6 @@ contract C // ---- // Warning: (224-240): Unused local variable. // Warning: (266-281): Assertion violation happens here +// Warning: (285-299): Assertion violation happens here +// Warning: (303-322): Assertion violation happens here +// Warning: (326-350): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/struct_1.sol b/test/libsolidity/smtCheckerTests/types/struct_1.sol index 38aa311a6..89c10c73c 100644 --- a/test/libsolidity/smtCheckerTests/types/struct_1.sol +++ b/test/libsolidity/smtCheckerTests/types/struct_1.sol @@ -17,8 +17,10 @@ contract C // Warning: (157-170): Unused local variable. // Warning: (157-170): Assertion checker does not yet support the type of this variable. // Warning: (139-146): Assertion checker does not yet implement type struct C.S storage ref +// Warning: (149-150): Assertion checker does not yet implement type type(struct C.S storage pointer) // Warning: (149-153): Assertion checker does not yet implement type struct C.S memory // Warning: (149-153): Assertion checker does not yet implement this expression. // Warning: (139-153): Assertion checker does not yet implement type struct C.S storage ref +// Warning: (173-174): Assertion checker does not yet implement type type(struct C.S storage pointer) // Warning: (173-177): Assertion checker does not yet implement type struct C.S memory // Warning: (173-177): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol b/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol index 99db33948..1a266aaa7 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol @@ -15,9 +15,11 @@ contract C { } // ---- // Warning: (112-120): Assertion checker does not yet support the type of this variable. +// Warning: (137-138): Assertion checker does not yet implement type type(struct C.S storage pointer) // Warning: (137-141): Assertion checker does not yet implement type struct C.S memory // Warning: (137-141): Assertion checker does not yet implement this expression. // Warning: (193-203): Assertion checker does not yet support the type of this variable. +// Warning: (137-138): Assertion checker does not yet implement type type(struct C.S storage pointer) // Warning: (137-141): Assertion checker does not yet implement type struct C.S memory // Warning: (137-141): Assertion checker does not yet implement this expression. // Warning: (227-228): Assertion checker does not yet implement type struct C.S memory diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 2481c63c3..83543b9f0 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -71,7 +71,8 @@ if (OSSFUZZ) /usr/include/libprotobuf-mutator ) target_link_libraries(abiv2_proto_ossfuzz PRIVATE solidity - evmc evmone intx ethash evmc-instructions + evmc + evmone-standalone protobuf-mutator-libfuzzer.a protobuf-mutator.a protobuf.a diff --git a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp index 91bd4b8e2..9d7308299 100644 --- a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp +++ b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp @@ -24,7 +24,7 @@ #include -static evmc::vm evmone = evmc::vm{evmc_create_evmone()}; +static evmc::VM evmone = evmc::VM{evmc_create_evmone()}; using namespace dev::test::abiv2fuzzer; using namespace dev::test; diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 559bfb1ba..3f2217883 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -437,6 +437,12 @@ void ProtoConverter::visit(NullaryOp const& _x) case NullaryOp::GASLIMIT: m_output << "gaslimit()"; break; + case NullaryOp::SELFBALANCE: + m_output << "selfbalance()"; + break; + case NullaryOp::CHAINID: + m_output << "chainid()"; + break; } } diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 736799371..491a2b329 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -237,6 +237,8 @@ message NullaryOp { NUMBER = 14; DIFFICULTY = 15; GASLIMIT = 16; + SELFBALANCE = 17; + CHAINID = 18; } required NOp op = 1; } diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 557073766..57441e942 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -70,7 +70,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) // AssemblyStack entry point AssemblyStack stack( - langutil::EVMVersion(), + langutil::EVMVersion(langutil::EVMVersion::istanbul()), AssemblyStack::Language::StrictAssembly, dev::solidity::OptimiserSettings::full() ); @@ -95,7 +95,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret( os1, stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()) + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(langutil::EVMVersion::istanbul())) ); if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached) @@ -105,7 +105,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) termReason = yulFuzzerUtil::interpret( os2, stack.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), + EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(langutil::EVMVersion::istanbul())), (yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 4) );