Merge pull request #7745 from ethereum/develop

Merge develop into develop_060
This commit is contained in:
Erik K 2019-11-19 15:30:31 +01:00 committed by GitHub
commit 94272d44aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 831 additions and 778 deletions

View File

@ -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-<revision> -f Dockerfile.ubuntu1904 .
docker push ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
```
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-<revision>
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-<revision> /bin/bash
cd /src/solidity
<commands_to_test_build_with_new_docker_image>
```
```

View File

@ -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:

View File

@ -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
COPY --from=libraries /usr/include /usr/include

View File

@ -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"; \

View File

@ -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; \

17
.github/workflows/stale.yml vendored Normal file
View File

@ -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

View File

@ -42,6 +42,7 @@ Compiler Features:
Bugfixes:
* SMTChecker: Fix internal error when using ``abi.decode``.

View File

@ -67,47 +67,48 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// 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<SimplificationRule<Pattern>> simplificationRuleListPart1(
};
}
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
Pattern,
@ -125,50 +127,51 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// 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<SimplificationRule<Pattern>> simplificationRuleListPart3(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// 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<SimplificationRule<Pattern>> simplificationRuleListPart4(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// 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<SimplificationRule<Pattern>> simplificationRuleListPart5(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
@ -248,15 +254,15 @@ std::vector<SimplificationRule<Pattern>> 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<SimplificationRule<Pattern>> 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<SimplificationRule<Pattern>> 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<Instruction>{
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<SimplificationRule<Pattern>> simplificationRuleListPart6(
Pattern Y
)
{
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
// Double negation of opcodes with boolean result
for (auto const& op: std::vector<Instruction>{
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<SimplificationRule<Pattern>> simplificationRuleListPart7(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
// Associative operations
for (auto const& opFun: std::vector<std::pair<Instruction,std::function<Word(Word const&,Word const&)>>>{
{Pattern::Builtins::ADD, std::plus<Word>()},
{Pattern::Builtins::MUL, std::multiplies<Word>()},
{Pattern::Builtins::AND, std::bit_and<Word>()},
{Pattern::Builtins::OR, std::bit_or<Word>()},
{Pattern::Builtins::XOR, std::bit_xor<Word>()}
for (auto&& instrAndFunc: std::vector<std::pair<Instruction, std::function<Word(Word, Word)>>>{
{Instruction::ADD, std::plus<Word>()},
{Instruction::MUL, std::multiplies<Word>()},
{Instruction::AND, std::bit_and<Word>()},
{Instruction::OR, std::bit_or<Word>()},
{Instruction::XOR, std::bit_xor<Word>()}
})
{
auto op = opFun.first;
auto fun = opFun.second;
typename Builtins::PatternGeneratorInstance op{instrAndFunc.first};
std::function<Word(Word, Word)> 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<Pattern>{X, A}, std::vector<Pattern>{A, X}})
for (auto const& opXA: {op(X, A), op(A, X)})
{
rules += std::vector<SimplificationRule<Pattern>>{{
// (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<SimplificationRule<Pattern>> 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<SimplificationRule<Pattern>> 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<SimplificationRule<Pattern>> 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<SimplificationRule<Pattern>> 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<SimplificationRule<Pattern>> 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<SimplificationRule<Pattern>> 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<SimplificationRule<Pattern>> simplificationRuleListPart8(
Pattern Y
)
{
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
// move constants across subtractions
rules += std::vector<SimplificationRule<Pattern>>{
{
// 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<SimplificationRule<Pattern>> simplificationRuleListPart9(
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> 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
});

View File

@ -21,6 +21,7 @@
#pragma once
#include <libevmasm/Instruction.h>
#include <libdevcore/CommonData.h>
#include <functional>
namespace dev
@ -55,84 +56,105 @@ struct SimplificationRule
std::function<bool()> feasible;
};
template <typename Pattern>
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<Instruction inst>
struct PatternGenerator
{
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
{
return {inst, {std::forward<Args>(_args)...}};
};
};
struct PatternGeneratorInstance
{
Instruction instruction;
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
{
return {instruction, {std::forward<Args>(_args)...}};
};
};
static auto constexpr STOP = PatternGenerator<Instruction::STOP>{};
static auto constexpr ADD = PatternGenerator<Instruction::ADD>{};
static auto constexpr SUB = PatternGenerator<Instruction::SUB>{};
static auto constexpr MUL = PatternGenerator<Instruction::MUL>{};
static auto constexpr DIV = PatternGenerator<Instruction::DIV>{};
static auto constexpr SDIV = PatternGenerator<Instruction::SDIV>{};
static auto constexpr MOD = PatternGenerator<Instruction::MOD>{};
static auto constexpr SMOD = PatternGenerator<Instruction::SMOD>{};
static auto constexpr EXP = PatternGenerator<Instruction::EXP>{};
static auto constexpr NOT = PatternGenerator<Instruction::NOT>{};
static auto constexpr LT = PatternGenerator<Instruction::LT>{};
static auto constexpr GT = PatternGenerator<Instruction::GT>{};
static auto constexpr SLT = PatternGenerator<Instruction::SLT>{};
static auto constexpr SGT = PatternGenerator<Instruction::SGT>{};
static auto constexpr EQ = PatternGenerator<Instruction::EQ>{};
static auto constexpr ISZERO = PatternGenerator<Instruction::ISZERO>{};
static auto constexpr AND = PatternGenerator<Instruction::AND>{};
static auto constexpr OR = PatternGenerator<Instruction::OR>{};
static auto constexpr XOR = PatternGenerator<Instruction::XOR>{};
static auto constexpr BYTE = PatternGenerator<Instruction::BYTE>{};
static auto constexpr SHL = PatternGenerator<Instruction::SHL>{};
static auto constexpr SHR = PatternGenerator<Instruction::SHR>{};
static auto constexpr SAR = PatternGenerator<Instruction::SAR>{};
static auto constexpr ADDMOD = PatternGenerator<Instruction::ADDMOD>{};
static auto constexpr MULMOD = PatternGenerator<Instruction::MULMOD>{};
static auto constexpr SIGNEXTEND = PatternGenerator<Instruction::SIGNEXTEND>{};
static auto constexpr KECCAK256 = PatternGenerator<Instruction::KECCAK256>{};
static auto constexpr ADDRESS = PatternGenerator<Instruction::ADDRESS>{};
static auto constexpr BALANCE = PatternGenerator<Instruction::BALANCE>{};
static auto constexpr ORIGIN = PatternGenerator<Instruction::ORIGIN>{};
static auto constexpr CALLER = PatternGenerator<Instruction::CALLER>{};
static auto constexpr CALLVALUE = PatternGenerator<Instruction::CALLVALUE>{};
static auto constexpr CALLDATALOAD = PatternGenerator<Instruction::CALLDATALOAD>{};
static auto constexpr CALLDATASIZE = PatternGenerator<Instruction::CALLDATASIZE>{};
static auto constexpr CALLDATACOPY = PatternGenerator<Instruction::CALLDATACOPY>{};
static auto constexpr CODESIZE = PatternGenerator<Instruction::CODESIZE>{};
static auto constexpr CODECOPY = PatternGenerator<Instruction::CODECOPY>{};
static auto constexpr GASPRICE = PatternGenerator<Instruction::GASPRICE>{};
static auto constexpr EXTCODESIZE = PatternGenerator<Instruction::EXTCODESIZE>{};
static auto constexpr EXTCODECOPY = PatternGenerator<Instruction::EXTCODECOPY>{};
static auto constexpr RETURNDATASIZE = PatternGenerator<Instruction::RETURNDATASIZE>{};
static auto constexpr RETURNDATACOPY = PatternGenerator<Instruction::RETURNDATACOPY>{};
static auto constexpr EXTCODEHASH = PatternGenerator<Instruction::EXTCODEHASH>{};
static auto constexpr BLOCKHASH = PatternGenerator<Instruction::BLOCKHASH>{};
static auto constexpr COINBASE = PatternGenerator<Instruction::COINBASE>{};
static auto constexpr TIMESTAMP = PatternGenerator<Instruction::TIMESTAMP>{};
static auto constexpr NUMBER = PatternGenerator<Instruction::NUMBER>{};
static auto constexpr DIFFICULTY = PatternGenerator<Instruction::DIFFICULTY>{};
static auto constexpr GASLIMIT = PatternGenerator<Instruction::GASLIMIT>{};
static auto constexpr CHAINID = PatternGenerator<Instruction::CHAINID>{};
static auto constexpr SELFBALANCE = PatternGenerator<Instruction::SELFBALANCE>{};
static auto constexpr POP = PatternGenerator<Instruction::POP>{};
static auto constexpr MLOAD = PatternGenerator<Instruction::MLOAD>{};
static auto constexpr MSTORE = PatternGenerator<Instruction::MSTORE>{};
static auto constexpr MSTORE8 = PatternGenerator<Instruction::MSTORE8>{};
static auto constexpr SLOAD = PatternGenerator<Instruction::SLOAD>{};
static auto constexpr SSTORE = PatternGenerator<Instruction::SSTORE>{};
static auto constexpr PC = PatternGenerator<Instruction::PC>{};
static auto constexpr MSIZE = PatternGenerator<Instruction::MSIZE>{};
static auto constexpr GAS = PatternGenerator<Instruction::GAS>{};
static auto constexpr LOG0 = PatternGenerator<Instruction::LOG0>{};
static auto constexpr LOG1 = PatternGenerator<Instruction::LOG1>{};
static auto constexpr LOG2 = PatternGenerator<Instruction::LOG2>{};
static auto constexpr LOG3 = PatternGenerator<Instruction::LOG3>{};
static auto constexpr LOG4 = PatternGenerator<Instruction::LOG4>{};
static auto constexpr CREATE = PatternGenerator<Instruction::CREATE>{};
static auto constexpr CALL = PatternGenerator<Instruction::CALL>{};
static auto constexpr CALLCODE = PatternGenerator<Instruction::CALLCODE>{};
static auto constexpr STATICCALL = PatternGenerator<Instruction::STATICCALL>{};
static auto constexpr RETURN = PatternGenerator<Instruction::RETURN>{};
static auto constexpr DELEGATECALL = PatternGenerator<Instruction::DELEGATECALL>{};
static auto constexpr CREATE2 = PatternGenerator<Instruction::CREATE2>{};
static auto constexpr REVERT = PatternGenerator<Instruction::REVERT>{};
static auto constexpr INVALID = PatternGenerator<Instruction::INVALID>{};
static auto constexpr SELFDESTRUCT = PatternGenerator<Instruction::SELFDESTRUCT>{};
};
}

View File

@ -99,7 +99,7 @@ Rules::Rules()
assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized.");
}
Pattern::Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments):
Pattern::Pattern(Instruction _instruction, std::initializer_list<Pattern> _arguments):
m_type(Operation),
m_instruction(_instruction),
m_arguments(_arguments)

View File

@ -26,6 +26,8 @@
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/SimplificationRule.h>
#include <libdevcore/CommonData.h>
#include <boost/noncopyable.hpp>
#include <functional>
@ -87,18 +89,20 @@ public:
using Expression = ExpressionClasses::Expression;
using Id = ExpressionClasses::Id;
using Builtins = dev::eth::EVMBuiltins;
using Builtins = dev::eth::EVMBuiltins<Pattern>;
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<u256>(_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<Pattern> const& _arguments = {});
Pattern(Instruction _instruction, std::initializer_list<Pattern> _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.

View File

@ -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<smt::SymbolicTupleVariable>(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<shared_ptr<smt::SymbolicVariable>> 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<TypeType const&>(*_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<smt::SymbolicTupleVariable>(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<shared_ptr<smt::SymbolicVariable>> 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)

View File

@ -27,6 +27,7 @@
#include <libsolidity/formal/SymbolicVariables.h>
#include <libsolidity/formal/VariableUsage.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/ReadFile.h>
#include <liblangutil/ErrorReporter.h>
@ -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;

View File

@ -248,12 +248,16 @@ SymbolicTupleVariable::SymbolicTupleVariable(
SymbolicVariable(_type, _type, move(_uniqueName), _context)
{
solAssert(isTuple(m_type->category()), "");
}
void SymbolicTupleVariable::setComponents(vector<shared_ptr<SymbolicVariable>> _components)
{
solAssert(m_components.empty(), "");
auto const& tupleType = dynamic_cast<solidity::TupleType const*>(m_type);
solAssert(_components.size() == tupleType->components().size(), "");
m_components = move(_components);
auto const& tupleType = dynamic_cast<TupleType const&>(*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);
}

View File

@ -250,8 +250,6 @@ public:
return m_components;
}
void setComponents(std::vector<std::shared_ptr<SymbolicVariable>> _components);
private:
std::vector<std::shared_ptr<SymbolicVariable>> m_components;
};

View File

@ -111,7 +111,7 @@ SimplificationRules::SimplificationRules()
assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized.");
}
yul::Pattern::Pattern(dev::eth::Instruction _instruction, vector<Pattern> const& _arguments):
yul::Pattern::Pattern(dev::eth::Instruction _instruction, initializer_list<Pattern> _arguments):
m_kind(PatternKind::Operation),
m_instruction(_instruction),
m_arguments(_arguments)

View File

@ -25,6 +25,8 @@
#include <libyul/AsmDataForward.h>
#include <libyul/AsmData.h>
#include <libdevcore/CommonData.h>
#include <boost/noncopyable.hpp>
#include <functional>
@ -85,7 +87,7 @@ enum class PatternKind
class Pattern
{
public:
using Builtins = dev::eth::EVMBuiltins;
using Builtins = dev::eth::EVMBuiltins<Pattern>;
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<dev::u256>(_value)) {}
// Matches a given instruction with given arguments
Pattern(dev::eth::Instruction _instruction, std::vector<Pattern> const& _arguments = {});
Pattern(dev::eth::Instruction _instruction, std::initializer_list<Pattern> _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.

View File

@ -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<evmc::vm> theVM;
static unique_ptr<evmc::VM> 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<evmc::vm>(vm);
theVM = make_unique<evmc::VM>(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;
}

View File

@ -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;
};

View File

@ -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

View File

@ -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<std::pair<const char*, const char*>> 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<std::pair<const char*, const char*>> 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<std::pair<const char*, const char*>> 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<evmc_host_context*>(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 <typename DerivedClass = Host>
static DerivedClass* from_context(evmc_host_context* context) noexcept
{
// Get pointer of the Host base class.
auto* h = reinterpret_cast<Host*>(context);
// Additional downcast, only possible if DerivedClass inherits from Host.
return static_cast<DerivedClass*>(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<Host*>(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<Host*>(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<Host*>(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<Host*>(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<Host*>(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<Host*>(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<Host*>(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<Host*>(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<Host*>(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<Host*>(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<Host*>(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<Host*>(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(topics),
Host::from_context(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(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

View File

@ -22,36 +22,35 @@
#include <string.h>
/**
* 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.

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -16,3 +16,5 @@ contract C
assert(y < 10000);
}
}
// ----
// Warning: (228-229): Assertion checker does not yet implement type type(library L)

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -24,7 +24,7 @@
#include <fstream>
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;

View File

@ -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;
}
}

View File

@ -237,6 +237,8 @@ message NullaryOp {
NUMBER = 14;
DIFFICULTY = 15;
GASLIMIT = 16;
SELFBALANCE = 17;
CHAINID = 18;
}
required NOp op = 1;
}

View File

@ -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)
);