mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7681 from ethereum/develop
Merge develop into develop_060
This commit is contained in:
commit
2e5a42836c
@ -427,21 +427,44 @@ jobs:
|
||||
environment:
|
||||
TERM: xterm
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_OPTIONS: -DLLL=ON
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}
|
||||
- run:
|
||||
name: Install build dependencies
|
||||
command: |
|
||||
brew unlink python
|
||||
brew install z3
|
||||
brew install boost
|
||||
brew install cmake
|
||||
brew install wget
|
||||
./scripts/install_obsolete_jsoncpp_1_7_4.sh
|
||||
command: ./.circleci/osx_install_dependencies.sh
|
||||
- save_cache:
|
||||
key: dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}
|
||||
paths:
|
||||
- /usr/local/bin
|
||||
- /usr/local/sbin
|
||||
- /usr/local/lib
|
||||
- /usr/local/include
|
||||
- /usr/local/Cellar
|
||||
- /usr/local/Homebrew
|
||||
- run: *run_build
|
||||
- store_artifacts: *artifacts_solc
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
- persist_to_workspace: *artifacts_build_dir
|
||||
|
||||
t_osx_soltest:
|
||||
macos:
|
||||
xcode: "11.0.0"
|
||||
environment:
|
||||
EVM: constantinople
|
||||
OPTIMIZE: 0
|
||||
TERM: xterm
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}
|
||||
- attach_workspace:
|
||||
at: build
|
||||
- run: *run_soltest
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
|
||||
t_osx_cli:
|
||||
macos:
|
||||
@ -450,13 +473,11 @@ jobs:
|
||||
TERM: xterm
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}
|
||||
- attach_workspace:
|
||||
at: build
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
brew unlink python
|
||||
brew install z3
|
||||
- run: *run_cmdline_tests
|
||||
- store_artifacts: *artifacts_test_results
|
||||
|
||||
@ -660,6 +681,7 @@ workflows:
|
||||
# OS/X build and tests
|
||||
- b_osx: *workflow_trigger_on_tags
|
||||
- t_osx_cli: *workflow_osx
|
||||
- t_osx_soltest: *workflow_osx
|
||||
|
||||
# Ubuntu build and tests
|
||||
- b_ubu: *workflow_trigger_on_tags
|
||||
|
59
.circleci/osx_install_dependencies.sh
Executable file
59
.circleci/osx_install_dependencies.sh
Executable file
@ -0,0 +1,59 @@
|
||||
#! /bin/bash
|
||||
#------------------------------------------------------------------------------
|
||||
# Bash script to install osx dependencies
|
||||
#
|
||||
# The documentation for solidity is hosted at:
|
||||
#
|
||||
# https://solidity.readthedocs.org
|
||||
#
|
||||
# ------------------------------------------------------------------------------
|
||||
# This file is part of solidity.
|
||||
#
|
||||
# solidity is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# solidity is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with solidity. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
# (c) 2016-2019 solidity contributors.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# note that the following directories may be cached by circleci:
|
||||
# - /usr/local/bin
|
||||
# - /usr/local/sbin
|
||||
# - /usr/local/lib
|
||||
# - /usr/local/include
|
||||
# - /usr/local/Cellar
|
||||
# - /usr/local/Homebrew
|
||||
|
||||
if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not restored), rebuild dependencies
|
||||
then
|
||||
brew unlink python
|
||||
brew install boost
|
||||
brew install cmake
|
||||
brew install wget
|
||||
brew install coreutils
|
||||
./scripts/install_obsolete_jsoncpp_1_7_4.sh
|
||||
|
||||
# z3
|
||||
wget https://github.com/Z3Prover/z3/releases/download/Z3-4.8.5/z3-4.8.5-x64-osx-10.14.2.zip
|
||||
unzip z3-4.8.5-x64-osx-10.14.2.zip
|
||||
rm -f z3-4.8.5-x64-osx-10.14.2.zip
|
||||
cp z3-4.8.5-x64-osx-10.14.2/bin/libz3.a /usr/local/lib
|
||||
cp z3-4.8.5-x64-osx-10.14.2/bin/z3 /usr/local/bin
|
||||
cp z3-4.8.5-x64-osx-10.14.2/include/* /usr/local/include
|
||||
rm -rf z3-4.8.5-x64-osx-10.14.2
|
||||
|
||||
# evmone
|
||||
wget https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-darwin-x86_64.tar.gz
|
||||
tar xzpf evmone-0.1.0-darwin-x86_64.tar.gz -C /usr/local
|
||||
rm -f evmone-0.1.0-darwin-x86_64.tar.gz
|
||||
fi
|
||||
|
@ -55,7 +55,7 @@ get_logfile_basename() {
|
||||
}
|
||||
|
||||
BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml"
|
||||
SOLTEST_ARGS="--evm-version=$EVM --evmonepath /usr/lib/libevmone.so $SOLTEST_FLAGS"
|
||||
SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS"
|
||||
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize"
|
||||
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2 --optimize-yul"
|
||||
|
||||
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1 +0,0 @@
|
||||
blank_issues_enabled: false
|
@ -135,12 +135,12 @@ enum class Accuracy
|
||||
};
|
||||
struct MeanSigma
|
||||
{
|
||||
float mean;
|
||||
float standardDeviation;
|
||||
float mean = 0.0f;
|
||||
float standardDeviation = 1.0f;
|
||||
};
|
||||
double const d = 0;
|
||||
int i;
|
||||
int j;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
char* s;
|
||||
MeanAndSigma ms meanAndSigma(std::vector<float> const& _v, Accuracy _a);
|
||||
Derived* x = dynamic_cast<Derived*>(base);
|
||||
|
@ -52,6 +52,7 @@ Bugfixes:
|
||||
* Type Checker: Disallow constructor of the same class to be used as modifier
|
||||
* Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer
|
||||
* Type Checker: Treat magic variables as unknown identifiers in inline assembly
|
||||
* SMTChecker: Fix internal error when accessing indices of fixed bytes.
|
||||
|
||||
|
||||
### 0.5.12 (2019-10-01)
|
||||
|
@ -34,3 +34,17 @@ macro(eth_default_option O DEF)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
function(detect_stray_source_files FILELIST DIRECTORY)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.12)
|
||||
file(GLOB sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${DIRECTORY}/*.cpp" "${DIRECTORY}/*.h")
|
||||
else()
|
||||
file(GLOB sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CONFIGURE_DEPENDS "${DIRECTORY}/*.cpp" "${DIRECTORY}/*.h")
|
||||
endif()
|
||||
foreach(path IN LISTS FILELIST)
|
||||
list(REMOVE_ITEM sources ${path})
|
||||
endforeach()
|
||||
list(LENGTH sources leftover_sources)
|
||||
if (leftover_sources)
|
||||
message(SEND_ERROR "The following source files are present but are not compiled: ${sources}")
|
||||
endif()
|
||||
endfunction(detect_stray_source_files)
|
||||
|
@ -49,7 +49,7 @@ template <class S> S modWorkaround(S const& _a, S const& _b)
|
||||
|
||||
// This works around a bug fixed with Boost 1.64.
|
||||
// https://www.boost.org/doc/libs/1_68_0/libs/multiprecision/doc/html/boost_multiprecision/map/hist.html#boost_multiprecision.map.hist.multiprecision_2_3_1_boost_1_64
|
||||
inline u256 shlWorkaround(u256 const& _x, unsigned _amount)
|
||||
template <class S> S shlWorkaround(S const& _x, unsigned _amount)
|
||||
{
|
||||
return u256((bigint(_x) << _amount) & u256(-1));
|
||||
}
|
||||
@ -66,44 +66,50 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
|
||||
Pattern
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
return std::vector<SimplificationRule<Pattern>> {
|
||||
// arithmetic on constants
|
||||
{{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
|
||||
{{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
|
||||
{{Instruction::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false},
|
||||
{{Instruction::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
|
||||
{{Instruction::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
||||
{{Instruction::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
|
||||
{{Instruction::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
||||
{{Instruction::EXP, {A, B}}, [=]{ return u256(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << 256)); }, false},
|
||||
{{Instruction::NOT, {A}}, [=]{ return ~A.d(); }, false},
|
||||
{{Instruction::LT, {A, B}}, [=]() -> u256 { return A.d() < B.d() ? 1 : 0; }, false},
|
||||
{{Instruction::GT, {A, B}}, [=]() -> u256 { return A.d() > B.d() ? 1 : 0; }, false},
|
||||
{{Instruction::SLT, {A, B}}, [=]() -> u256 { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false},
|
||||
{{Instruction::SGT, {A, B}}, [=]() -> u256 { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false},
|
||||
{{Instruction::EQ, {A, B}}, [=]() -> u256 { return A.d() == B.d() ? 1 : 0; }, false},
|
||||
{{Instruction::ISZERO, {A}}, [=]() -> u256 { return A.d() == 0 ? 1 : 0; }, false},
|
||||
{{Instruction::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false},
|
||||
{{Instruction::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false},
|
||||
{{Instruction::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false},
|
||||
{{Instruction::BYTE, {A, B}}, [=]{ return A.d() >= 32 ? 0 : (B.d() >> unsigned(8 * (31 - A.d()))) & 0xff; }, false},
|
||||
{{Instruction::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) + bigint(B.d())) % C.d()); }, false},
|
||||
{{Instruction::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) * bigint(B.d())) % C.d()); }, false},
|
||||
{{Instruction::SIGNEXTEND, {A, B}}, [=]() -> u256 {
|
||||
if (A.d() >= 31)
|
||||
{{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}}, [=]{
|
||||
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 {
|
||||
if (A.d() >= Pattern::WordSize / 8 - 1)
|
||||
return B.d();
|
||||
unsigned testBit = unsigned(A.d()) * 8 + 7;
|
||||
u256 mask = (u256(1) << testBit) - 1;
|
||||
Word mask = (Word(1) << testBit) - 1;
|
||||
return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask;
|
||||
}, false},
|
||||
{{Instruction::SHL, {A, B}}, [=]{
|
||||
if (A.d() > 255)
|
||||
return u256(0);
|
||||
{{Pattern::Builtins::SHL, {A, B}}, [=]{
|
||||
if (A.d() >= Pattern::WordSize)
|
||||
return Word(0);
|
||||
return shlWorkaround(B.d(), unsigned(A.d()));
|
||||
}, false},
|
||||
{{Instruction::SHR, {A, B}}, [=]{
|
||||
if (A.d() > 255)
|
||||
return u256(0);
|
||||
{{Pattern::Builtins::SHR, {A, B}}, [=]{
|
||||
if (A.d() >= Pattern::WordSize)
|
||||
return Word(0);
|
||||
return B.d() >> unsigned(A.d());
|
||||
}, false}
|
||||
};
|
||||
@ -118,50 +124,51 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
|
||||
Pattern Y
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
return std::vector<SimplificationRule<Pattern>> {
|
||||
// invariants involving known constants
|
||||
{{Instruction::ADD, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Instruction::ADD, {0, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::SUB, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Instruction::SUB, {~u256(0), X}}, [=]() -> Pattern { return {Instruction::NOT, {X}}; }, false},
|
||||
{{Instruction::MUL, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::MUL, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::MUL, {X, 1}}, [=]{ return X; }, false},
|
||||
{{Instruction::MUL, {1, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::MUL, {X, u256(-1)}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false},
|
||||
{{Instruction::MUL, {u256(-1), X}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false},
|
||||
{{Instruction::DIV, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::DIV, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::DIV, {X, 1}}, [=]{ return X; }, false},
|
||||
{{Instruction::SDIV, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SDIV, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SDIV, {X, 1}}, [=]{ return X; }, false},
|
||||
{{Instruction::AND, {X, ~u256(0)}}, [=]{ return X; }, false},
|
||||
{{Instruction::AND, {~u256(0), X}}, [=]{ return X; }, false},
|
||||
{{Instruction::AND, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::AND, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::OR, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Instruction::OR, {0, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }, true},
|
||||
{{Instruction::OR, {~u256(0), X}}, [=]{ return ~u256(0); }, true},
|
||||
{{Instruction::XOR, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Instruction::XOR, {0, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::MOD, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
|
||||
{{Instruction::EQ, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
|
||||
{{Instruction::SHL, {0, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::SHR, {0, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::SHL, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SHR, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::GT, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}; }, false},
|
||||
{{Instruction::LT, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}; }, false},
|
||||
{{Instruction::GT, {X, ~u256(0)}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::LT, {~u256(0), X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::GT, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::LT, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::AND, {{Instruction::BYTE, {X, Y}}, {u256(0xff)}}}, [=]() -> Pattern { return {Instruction::BYTE, {X, Y}}; }, false},
|
||||
{{Instruction::BYTE, {31, X}}, [=]() -> Pattern { return {Instruction::AND, {X, u256(0xff)}}; }, false}
|
||||
{{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}
|
||||
};
|
||||
}
|
||||
|
||||
@ -174,18 +181,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart3(
|
||||
Pattern
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
return std::vector<SimplificationRule<Pattern>> {
|
||||
// operations involving an expression and itself
|
||||
{{Instruction::AND, {X, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::OR, {X, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::XOR, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SUB, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::EQ, {X, X}}, [=]{ return u256(1); }, true},
|
||||
{{Instruction::LT, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SLT, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::GT, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SGT, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true}
|
||||
{{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}
|
||||
};
|
||||
}
|
||||
|
||||
@ -198,25 +206,26 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4(
|
||||
Pattern Y
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
return std::vector<SimplificationRule<Pattern>> {
|
||||
// logical instruction combinations
|
||||
{{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }, false},
|
||||
{{Instruction::XOR, {X, {Instruction::XOR, {X, Y}}}}, [=]{ return Y; }, true},
|
||||
{{Instruction::XOR, {X, {Instruction::XOR, {Y, X}}}}, [=]{ return Y; }, true},
|
||||
{{Instruction::XOR, {{Instruction::XOR, {X, Y}}, X}}, [=]{ return Y; }, true},
|
||||
{{Instruction::XOR, {{Instruction::XOR, {Y, X}}, X}}, [=]{ return Y; }, true},
|
||||
{{Instruction::OR, {X, {Instruction::AND, {X, Y}}}}, [=]{ return X; }, true},
|
||||
{{Instruction::OR, {X, {Instruction::AND, {Y, X}}}}, [=]{ return X; }, true},
|
||||
{{Instruction::OR, {{Instruction::AND, {X, Y}}, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::OR, {{Instruction::AND, {Y, X}}, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {X, {Instruction::OR, {X, Y}}}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {X, {Instruction::OR, {Y, X}}}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {{Instruction::OR, {X, Y}}, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {{Instruction::OR, {Y, X}}, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {X, {Instruction::NOT, {X}}}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::AND, {{Instruction::NOT, {X}}, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::OR, {X, {Instruction::NOT, {X}}}}, [=]{ return ~u256(0); }, true},
|
||||
{{Instruction::OR, {{Instruction::NOT, {X}}, X}}, [=]{ return ~u256(0); }, true},
|
||||
{{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},
|
||||
};
|
||||
}
|
||||
|
||||
@ -230,58 +239,61 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
|
||||
Pattern
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
|
||||
// Replace MOD X, <power-of-two> with AND X, <power-of-two> - 1
|
||||
for (size_t i = 0; i < 256; ++i)
|
||||
for (size_t i = 0; i < Pattern::WordSize; ++i)
|
||||
{
|
||||
u256 value = u256(1) << i;
|
||||
Word value = Word(1) << i;
|
||||
rules.push_back({
|
||||
{Instruction::MOD, {X, value}},
|
||||
[=]() -> Pattern { return {Instruction::AND, {X, value - 1}}; },
|
||||
{Pattern::Builtins::MOD, {X, value}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::AND, {X, value - 1}}; },
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
// Replace SHL >=256, X with 0
|
||||
rules.push_back({
|
||||
{Instruction::SHL, {A, X}},
|
||||
[=]() -> Pattern { return u256(0); },
|
||||
{Pattern::Builtins::SHL, {A, X}},
|
||||
[=]() -> Pattern { return Word(0); },
|
||||
true,
|
||||
[=]() { return A.d() >= 256; }
|
||||
[=]() { return A.d() >= Pattern::WordSize; }
|
||||
});
|
||||
|
||||
// Replace SHR >=256, X with 0
|
||||
rules.push_back({
|
||||
{Instruction::SHR, {A, X}},
|
||||
[=]() -> Pattern { return u256(0); },
|
||||
{Pattern::Builtins::SHR, {A, X}},
|
||||
[=]() -> Pattern { return Word(0); },
|
||||
true,
|
||||
[=]() { return A.d() >= 256; }
|
||||
[=]() { return A.d() >= Pattern::WordSize; }
|
||||
});
|
||||
|
||||
// Replace BYTE(A, X), A >= 32 with 0
|
||||
rules.push_back({
|
||||
{Instruction::BYTE, {A, X}},
|
||||
[=]() -> Pattern { return u256(0); },
|
||||
{Pattern::Builtins::BYTE, {A, X}},
|
||||
[=]() -> Pattern { return Word(0); },
|
||||
true,
|
||||
[=]() { return A.d() >= 32; }
|
||||
[=]() { return A.d() >= Pattern::WordSize / 8; }
|
||||
});
|
||||
|
||||
for (auto const& op: std::vector<Instruction>{
|
||||
Instruction::ADDRESS,
|
||||
Instruction::CALLER,
|
||||
Instruction::ORIGIN,
|
||||
Instruction::COINBASE
|
||||
Pattern::Builtins::ADDRESS,
|
||||
Pattern::Builtins::CALLER,
|
||||
Pattern::Builtins::ORIGIN,
|
||||
Pattern::Builtins::COINBASE
|
||||
})
|
||||
{
|
||||
u256 const mask = (u256(1) << 160) - 1;
|
||||
assertThrow(Pattern::WordSize > 160, OptimizerException, "");
|
||||
Word const mask = (Word(1) << 160) - 1;
|
||||
rules.push_back({
|
||||
{Instruction::AND, {{op, mask}}},
|
||||
{Pattern::Builtins::AND, {{op, mask}}},
|
||||
[=]() -> Pattern { return op; },
|
||||
false
|
||||
});
|
||||
rules.push_back({
|
||||
{Instruction::AND, {{mask, op}}},
|
||||
{Pattern::Builtins::AND, {{mask, op}}},
|
||||
[=]() -> Pattern { return op; },
|
||||
false
|
||||
});
|
||||
@ -302,27 +314,27 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart6(
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
// Double negation of opcodes with boolean result
|
||||
for (auto const& op: std::vector<Instruction>{
|
||||
Instruction::EQ,
|
||||
Instruction::LT,
|
||||
Instruction::SLT,
|
||||
Instruction::GT,
|
||||
Instruction::SGT
|
||||
Pattern::Builtins::EQ,
|
||||
Pattern::Builtins::LT,
|
||||
Pattern::Builtins::SLT,
|
||||
Pattern::Builtins::GT,
|
||||
Pattern::Builtins::SGT
|
||||
})
|
||||
rules.push_back({
|
||||
{Instruction::ISZERO, {{Instruction::ISZERO, {{op, {X, Y}}}}}},
|
||||
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{op, {X, Y}}}}}},
|
||||
[=]() -> Pattern { return {op, {X, Y}}; },
|
||||
false
|
||||
});
|
||||
|
||||
rules.push_back({
|
||||
{Instruction::ISZERO, {{Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}}},
|
||||
[=]() -> Pattern { return {Instruction::ISZERO, {X}}; },
|
||||
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; },
|
||||
false
|
||||
});
|
||||
|
||||
rules.push_back({
|
||||
{Instruction::ISZERO, {{Instruction::XOR, {X, Y}}}},
|
||||
[=]() -> Pattern { return { Instruction::EQ, {X, Y} }; },
|
||||
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::XOR, {X, Y}}}},
|
||||
[=]() -> Pattern { return { Pattern::Builtins::EQ, {X, Y} }; },
|
||||
false
|
||||
});
|
||||
|
||||
@ -338,14 +350,15 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
||||
Pattern Y
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
// Associative operations
|
||||
for (auto const& opFun: std::vector<std::pair<Instruction,std::function<u256(u256 const&,u256 const&)>>>{
|
||||
{Instruction::ADD, std::plus<u256>()},
|
||||
{Instruction::MUL, std::multiplies<u256>()},
|
||||
{Instruction::AND, std::bit_and<u256>()},
|
||||
{Instruction::OR, std::bit_or<u256>()},
|
||||
{Instruction::XOR, std::bit_xor<u256>()}
|
||||
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>()}
|
||||
})
|
||||
{
|
||||
auto op = opFun.first;
|
||||
@ -382,13 +395,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)
|
||||
{Instruction::SHL, {{B}, {Instruction::SHL, {{A}, {X}}}}},
|
||||
{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
|
||||
[=]() -> Pattern {
|
||||
bigint sum = bigint(A.d()) + B.d();
|
||||
if (sum >= 256)
|
||||
return {Instruction::AND, {X, u256(0)}};
|
||||
if (sum >= Pattern::WordSize)
|
||||
return {Pattern::Builtins::AND, {X, Word(0)}};
|
||||
else
|
||||
return {Instruction::SHL, {u256(sum), X}};
|
||||
return {Pattern::Builtins::SHL, {Word(sum), X}};
|
||||
},
|
||||
false
|
||||
});
|
||||
@ -396,13 +409,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)
|
||||
{Instruction::SHR, {{B}, {Instruction::SHR, {{A}, {X}}}}},
|
||||
{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
|
||||
[=]() -> Pattern {
|
||||
bigint sum = bigint(A.d()) + B.d();
|
||||
if (sum >= 256)
|
||||
return {Instruction::AND, {X, u256(0)}};
|
||||
if (sum >= Pattern::WordSize)
|
||||
return {Pattern::Builtins::AND, {X, Word(0)}};
|
||||
else
|
||||
return {Instruction::SHR, {u256(sum), X}};
|
||||
return {Pattern::Builtins::SHR, {Word(sum), X}};
|
||||
},
|
||||
false
|
||||
});
|
||||
@ -410,112 +423,112 @@ 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)
|
||||
{Instruction::SHR, {{B}, {Instruction::SHL, {{A}, {X}}}}},
|
||||
{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
|
||||
[=]() -> Pattern {
|
||||
u256 mask = shlWorkaround(u256(-1), unsigned(A.d())) >> unsigned(B.d());
|
||||
Word mask = shlWorkaround(~Word(0), unsigned(A.d())) >> unsigned(B.d());
|
||||
|
||||
if (A.d() > B.d())
|
||||
return {Instruction::AND, {{Instruction::SHL, {A.d() - B.d(), X}}, mask}};
|
||||
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {A.d() - B.d(), X}}, mask}};
|
||||
else if (B.d() > A.d())
|
||||
return {Instruction::AND, {{Instruction::SHR, {B.d() - A.d(), X}}, mask}};
|
||||
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B.d() - A.d(), X}}, mask}};
|
||||
else
|
||||
return {Instruction::AND, {X, mask}};
|
||||
return {Pattern::Builtins::AND, {X, mask}};
|
||||
},
|
||||
false,
|
||||
[=] { return A.d() < 256 && B.d() < 256; }
|
||||
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
|
||||
});
|
||||
|
||||
// Combine SHR-SHL by constant
|
||||
rules.push_back({
|
||||
// SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
|
||||
{Instruction::SHL, {{B}, {Instruction::SHR, {{A}, {X}}}}},
|
||||
{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
|
||||
[=]() -> Pattern {
|
||||
u256 mask = shlWorkaround(u256(-1) >> unsigned(A.d()), unsigned(B.d()));
|
||||
Word mask = shlWorkaround((~Word(0)) >> unsigned(A.d()), unsigned(B.d()));
|
||||
|
||||
if (A.d() > B.d())
|
||||
return {Instruction::AND, {{Instruction::SHR, {A.d() - B.d(), X}}, mask}};
|
||||
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {A.d() - B.d(), X}}, mask}};
|
||||
else if (B.d() > A.d())
|
||||
return {Instruction::AND, {{Instruction::SHL, {B.d() - A.d(), X}}, mask}};
|
||||
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {B.d() - A.d(), X}}, mask}};
|
||||
else
|
||||
return {Instruction::AND, {X, mask}};
|
||||
return {Pattern::Builtins::AND, {X, mask}};
|
||||
},
|
||||
false,
|
||||
[=] { return A.d() < 256 && B.d() < 256; }
|
||||
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
|
||||
});
|
||||
|
||||
// Move AND with constant across SHL and SHR by constant
|
||||
for (auto shiftOp: {Instruction::SHL, Instruction::SHR})
|
||||
for (auto shiftOp: {Pattern::Builtins::SHL, Pattern::Builtins::SHR})
|
||||
{
|
||||
auto replacement = [=]() -> Pattern {
|
||||
u256 mask =
|
||||
shiftOp == Instruction::SHL ?
|
||||
Word mask =
|
||||
shiftOp == Pattern::Builtins::SHL ?
|
||||
shlWorkaround(A.d(), unsigned(B.d())) :
|
||||
A.d() >> unsigned(B.d());
|
||||
return {Instruction::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}};
|
||||
return {Pattern::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}, {Instruction::AND, {{X}, {A}}}}},
|
||||
{shiftOp, {{B}, {Pattern::Builtins::AND, {{X}, {A}}}}},
|
||||
replacement,
|
||||
false,
|
||||
[=] { return B.d() < 256; }
|
||||
[=] { 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}, {Instruction::AND, {{A}, {X}}}}},
|
||||
{shiftOp, {{B}, {Pattern::Builtins::AND, {{A}, {X}}}}},
|
||||
replacement,
|
||||
false,
|
||||
[=] { return B.d() < 256; }
|
||||
[=] { return B.d() < Pattern::WordSize; }
|
||||
});
|
||||
}
|
||||
|
||||
rules.push_back({
|
||||
// MUL(X, SHL(Y, 1)) -> SHL(Y, X)
|
||||
{Instruction::MUL, {X, {Instruction::SHL, {Y, u256(1)}}}},
|
||||
{Pattern::Builtins::MUL, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
|
||||
[=]() -> Pattern {
|
||||
return {Instruction::SHL, {Y, X}};
|
||||
return {Pattern::Builtins::SHL, {Y, X}};
|
||||
},
|
||||
// Actually only changes the order, does not remove.
|
||||
true
|
||||
});
|
||||
rules.push_back({
|
||||
// MUL(SHL(X, 1), Y) -> SHL(X, Y)
|
||||
{Instruction::MUL, {{Instruction::SHL, {X, u256(1)}}, Y}},
|
||||
{Pattern::Builtins::MUL, {{Pattern::Builtins::SHL, {X, Word(1)}}, Y}},
|
||||
[=]() -> Pattern {
|
||||
return {Instruction::SHL, {X, Y}};
|
||||
return {Pattern::Builtins::SHL, {X, Y}};
|
||||
},
|
||||
false
|
||||
});
|
||||
|
||||
rules.push_back({
|
||||
// DIV(X, SHL(Y, 1)) -> SHR(Y, X)
|
||||
{Instruction::DIV, {X, {Instruction::SHL, {Y, u256(1)}}}},
|
||||
{Pattern::Builtins::DIV, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
|
||||
[=]() -> Pattern {
|
||||
return {Instruction::SHR, {Y, X}};
|
||||
return {Pattern::Builtins::SHR, {Y, X}};
|
||||
},
|
||||
// Actually only changes the order, does not remove.
|
||||
true
|
||||
});
|
||||
|
||||
std::function<bool()> feasibilityFunction = [=]() {
|
||||
if (B.d() > 256)
|
||||
if (B.d() > Pattern::WordSize)
|
||||
return false;
|
||||
unsigned bAsUint = static_cast<unsigned>(B.d());
|
||||
return (A.d() & (u256(-1) >> bAsUint)) == (u256(-1) >> bAsUint);
|
||||
return (A.d() & ((~Word(0)) >> bAsUint)) == ((~Word(0)) >> bAsUint);
|
||||
};
|
||||
|
||||
rules.push_back({
|
||||
// AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B)
|
||||
{Instruction::AND, {A, {Instruction::SHR, {B, X}}}},
|
||||
[=]() -> Pattern { return {Instruction::SHR, {B, X}}; },
|
||||
{Pattern::Builtins::AND, {A, {Pattern::Builtins::SHR, {B, X}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
|
||||
false,
|
||||
feasibilityFunction
|
||||
});
|
||||
|
||||
rules.push_back({
|
||||
// AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B)
|
||||
{Instruction::AND, {{Instruction::SHR, {B, X}}, A}},
|
||||
[=]() -> Pattern { return {Instruction::SHR, {B, X}}; },
|
||||
{Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B, X}}, A}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
|
||||
false,
|
||||
feasibilityFunction
|
||||
});
|
||||
@ -538,28 +551,28 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart8(
|
||||
rules += std::vector<SimplificationRule<Pattern>>{
|
||||
{
|
||||
// X - A -> X + (-A)
|
||||
{Instruction::SUB, {X, A}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; },
|
||||
{Pattern::Builtins::SUB, {X, A}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {X, 0 - A.d()}}; },
|
||||
false
|
||||
}, {
|
||||
// (X + A) - Y -> (X - Y) + A
|
||||
{Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; },
|
||||
{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {X, A}}, Y}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
|
||||
false
|
||||
}, {
|
||||
// (A + X) - Y -> (X - Y) + A
|
||||
{Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; },
|
||||
{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {A, X}}, Y}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
|
||||
false
|
||||
}, {
|
||||
// X - (Y + A) -> (X - Y) + (-A)
|
||||
{Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||
{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {Y, A}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||
false
|
||||
}, {
|
||||
// X - (A + Y) -> (X - Y) + (-A)
|
||||
{Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||
{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {A, Y}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||
false
|
||||
}
|
||||
};
|
||||
@ -577,29 +590,31 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart9(
|
||||
Pattern Z
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
|
||||
u256 const mask = (u256(1) << 160) - 1;
|
||||
assertThrow(Pattern::WordSize > 160, OptimizerException, "");
|
||||
Word const mask = (Word(1) << 160) - 1;
|
||||
// CREATE
|
||||
rules.push_back({
|
||||
{Instruction::AND, {{Instruction::CREATE, {W, X, Y}}, mask}},
|
||||
[=]() -> Pattern { return {Instruction::CREATE, {W, X, Y}}; },
|
||||
{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE, {W, X, Y}}, mask}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
|
||||
false
|
||||
});
|
||||
rules.push_back({
|
||||
{Instruction::AND, {{mask, {Instruction::CREATE, {W, X, Y}}}}},
|
||||
[=]() -> Pattern { return {Instruction::CREATE, {W, X, Y}}; },
|
||||
{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE, {W, X, Y}}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
|
||||
false
|
||||
});
|
||||
// CREATE2
|
||||
rules.push_back({
|
||||
{Instruction::AND, {{Instruction::CREATE2, {W, X, Y, Z}}, mask}},
|
||||
[=]() -> Pattern { return {Instruction::CREATE2, {W, X, Y, Z}}; },
|
||||
{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE2, {W, X, Y, Z}}, mask}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
|
||||
false
|
||||
});
|
||||
rules.push_back({
|
||||
{Instruction::AND, {{mask, {Instruction::CREATE2, {W, X, Y, Z}}}}},
|
||||
[=]() -> Pattern { return {Instruction::CREATE2, {W, X, Y, Z}}; },
|
||||
{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE2, {W, X, Y, Z}}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
|
||||
false
|
||||
});
|
||||
|
||||
@ -621,6 +636,14 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
|
||||
Pattern Z
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
// Some sanity checks
|
||||
assertThrow(Pattern::WordSize % 8 == 0, OptimizerException, "");
|
||||
assertThrow(Pattern::WordSize >= 8, OptimizerException, "");
|
||||
assertThrow(Pattern::WordSize <= 256, OptimizerException, "");
|
||||
assertThrow(Word(-1) == ~Word(0), OptimizerException, "");
|
||||
assertThrow(Word(-1) + 1 == Word(0), OptimizerException, "");
|
||||
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
rules += simplificationRuleListPart1(A, B, C, W, X);
|
||||
rules += simplificationRuleListPart2(A, B, C, W, X);
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <functional>
|
||||
|
||||
namespace dev
|
||||
@ -54,5 +55,85 @@ struct SimplificationRule
|
||||
std::function<bool()> feasible;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,10 @@ public:
|
||||
using Expression = ExpressionClasses::Expression;
|
||||
using Id = ExpressionClasses::Id;
|
||||
|
||||
using Builtins = dev::eth::EVMBuiltins;
|
||||
static constexpr size_t WordSize = 256;
|
||||
using Word = u256;
|
||||
|
||||
// Matches a specific constant value.
|
||||
Pattern(unsigned _value): Pattern(u256(_value)) {}
|
||||
// Matches a specific constant value.
|
||||
|
@ -77,6 +77,8 @@ set(sources
|
||||
formal/BMC.h
|
||||
formal/CHC.cpp
|
||||
formal/CHC.h
|
||||
formal/CHCSmtLib2Interface.cpp
|
||||
formal/CHCSmtLib2Interface.h
|
||||
formal/CHCSolverInterface.h
|
||||
formal/EncodingContext.cpp
|
||||
formal/EncodingContext.h
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
#include <libsolidity/formal/CHC.h>
|
||||
|
||||
#include <libsolidity/formal/CHCSmtLib2Interface.h>
|
||||
|
||||
#ifdef HAVE_Z3
|
||||
#include <libsolidity/formal/Z3CHCInterface.h>
|
||||
#endif
|
||||
@ -30,13 +32,20 @@ using namespace dev;
|
||||
using namespace langutil;
|
||||
using namespace dev::solidity;
|
||||
|
||||
CHC::CHC(smt::EncodingContext& _context, ErrorReporter& _errorReporter):
|
||||
CHC::CHC(
|
||||
smt::EncodingContext& _context,
|
||||
ErrorReporter& _errorReporter,
|
||||
map<h256, string> const& _smtlib2Responses
|
||||
):
|
||||
SMTEncoder(_context),
|
||||
#ifdef HAVE_Z3
|
||||
m_interface(make_shared<smt::Z3CHCInterface>()),
|
||||
#else
|
||||
m_interface(make_shared<smt::CHCSmtLib2Interface>(_smtlib2Responses)),
|
||||
#endif
|
||||
m_outerErrorReporter(_errorReporter)
|
||||
{
|
||||
(void)_smtlib2Responses;
|
||||
}
|
||||
|
||||
void CHC::analyze(SourceUnit const& _source)
|
||||
@ -47,12 +56,24 @@ void CHC::analyze(SourceUnit const& _source)
|
||||
auto z3Interface = dynamic_pointer_cast<smt::Z3CHCInterface>(m_interface);
|
||||
solAssert(z3Interface, "");
|
||||
m_context.setSolver(z3Interface->z3Interface());
|
||||
#else
|
||||
auto smtlib2Interface = dynamic_pointer_cast<smt::CHCSmtLib2Interface>(m_interface);
|
||||
solAssert(smtlib2Interface, "");
|
||||
m_context.setSolver(smtlib2Interface->smtlib2Interface());
|
||||
#endif
|
||||
m_context.clear();
|
||||
m_context.setAssertionAccumulation(false);
|
||||
m_variableUsage.setFunctionInlining(false);
|
||||
|
||||
_source.accept(*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
vector<string> CHC::unhandledQueries() const
|
||||
{
|
||||
if (auto smtlib2 = dynamic_pointer_cast<smt::CHCSmtLib2Interface>(m_interface))
|
||||
return smtlib2->unhandledQueries();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CHC::visit(ContractDefinition const& _contract)
|
||||
@ -99,7 +120,7 @@ bool CHC::visit(ContractDefinition const& _contract)
|
||||
{
|
||||
auto const& symbVar = m_context.variable(*var);
|
||||
symbVar->increaseIndex();
|
||||
m_interface->declareVariable(symbVar->currentName(), *symbVar->sort());
|
||||
m_interface->declareVariable(symbVar->currentName(), symbVar->sort());
|
||||
m_context.setZeroValue(*symbVar);
|
||||
}
|
||||
|
||||
|
@ -44,12 +44,21 @@ namespace solidity
|
||||
class CHC: public SMTEncoder
|
||||
{
|
||||
public:
|
||||
CHC(smt::EncodingContext& _context, langutil::ErrorReporter& _errorReporter);
|
||||
CHC(
|
||||
smt::EncodingContext& _context,
|
||||
langutil::ErrorReporter& _errorReporter,
|
||||
std::map<h256, std::string> const& _smtlib2Responses
|
||||
);
|
||||
|
||||
void analyze(SourceUnit const& _sources);
|
||||
|
||||
std::set<Expression const*> const& safeAssertions() const { return m_safeAssertions; }
|
||||
|
||||
/// This is used if the Horn solver is not directly linked into this binary.
|
||||
/// @returns a list of inputs to the Horn solver that were not part of the argument to
|
||||
/// the constructor.
|
||||
std::vector<std::string> unhandledQueries() const;
|
||||
|
||||
private:
|
||||
/// Visitor functions.
|
||||
//@{
|
||||
|
160
libsolidity/formal/CHCSmtLib2Interface.cpp
Normal file
160
libsolidity/formal/CHCSmtLib2Interface.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libsolidity/formal/CHCSmtLib2Interface.h>
|
||||
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
using namespace dev::solidity::smt;
|
||||
|
||||
CHCSmtLib2Interface::CHCSmtLib2Interface(map<h256, string> const& _queryResponses):
|
||||
m_smtlib2(make_shared<SMTLib2Interface>(_queryResponses)),
|
||||
m_queryResponses(_queryResponses)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::reset()
|
||||
{
|
||||
m_accumulatedOutput.clear();
|
||||
m_variables.clear();
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::registerRelation(smt::Expression const& _expr)
|
||||
{
|
||||
solAssert(_expr.sort, "");
|
||||
solAssert(_expr.sort->kind == smt::Kind::Function, "");
|
||||
if (!m_variables.count(_expr.name))
|
||||
{
|
||||
auto fSort = dynamic_pointer_cast<FunctionSort>(_expr.sort);
|
||||
string domain = m_smtlib2->toSmtLibSort(fSort->domain);
|
||||
// Relations are predicates which have implicit codomain Bool.
|
||||
m_variables.insert(_expr.name);
|
||||
write(
|
||||
"(declare-rel |" +
|
||||
_expr.name +
|
||||
"| " +
|
||||
domain +
|
||||
")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::addRule(smt::Expression const& _expr, std::string const& _name)
|
||||
{
|
||||
write(
|
||||
"(rule (! " +
|
||||
m_smtlib2->toSExpr(_expr) +
|
||||
" :named " +
|
||||
_name +
|
||||
"))"
|
||||
);
|
||||
}
|
||||
|
||||
pair<CheckResult, vector<string>> CHCSmtLib2Interface::query(smt::Expression const& _block)
|
||||
{
|
||||
string accumulated{};
|
||||
swap(m_accumulatedOutput, accumulated);
|
||||
for (auto const& var: m_smtlib2->variables())
|
||||
declareVariable(var.first, var.second);
|
||||
m_accumulatedOutput += accumulated;
|
||||
|
||||
string response = querySolver(
|
||||
m_accumulatedOutput +
|
||||
"\n(query " + _block.name + " :print-certificate true)"
|
||||
);
|
||||
|
||||
CheckResult result;
|
||||
// TODO proper parsing
|
||||
if (boost::starts_with(response, "sat\n"))
|
||||
result = CheckResult::SATISFIABLE;
|
||||
else if (boost::starts_with(response, "unsat\n"))
|
||||
result = CheckResult::UNSATISFIABLE;
|
||||
else if (boost::starts_with(response, "unknown\n"))
|
||||
result = CheckResult::UNKNOWN;
|
||||
else
|
||||
result = CheckResult::ERROR;
|
||||
|
||||
// TODO collect invariants or counterexamples.
|
||||
return make_pair(result, vector<string>{});
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
if (_sort->kind == Kind::Function)
|
||||
declareFunction(_name, _sort);
|
||||
else if (!m_variables.count(_name))
|
||||
{
|
||||
m_variables.insert(_name);
|
||||
write("(declare-var |" + _name + "| " + m_smtlib2->toSmtLibSort(*_sort) + ')');
|
||||
}
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
solAssert(_sort->kind == smt::Kind::Function, "");
|
||||
// TODO Use domain and codomain as key as well
|
||||
if (!m_variables.count(_name))
|
||||
{
|
||||
auto fSort = dynamic_pointer_cast<FunctionSort>(_sort);
|
||||
solAssert(fSort->codomain, "");
|
||||
string domain = m_smtlib2->toSmtLibSort(fSort->domain);
|
||||
string codomain = m_smtlib2->toSmtLibSort(*fSort->codomain);
|
||||
m_variables.insert(_name);
|
||||
write(
|
||||
"(declare-fun |" +
|
||||
_name +
|
||||
"| " +
|
||||
domain +
|
||||
" " +
|
||||
codomain +
|
||||
")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::write(string _data)
|
||||
{
|
||||
m_accumulatedOutput += move(_data) + "\n";
|
||||
}
|
||||
|
||||
string CHCSmtLib2Interface::querySolver(string const& _input)
|
||||
{
|
||||
h256 inputHash = dev::keccak256(_input);
|
||||
if (m_queryResponses.count(inputHash))
|
||||
return m_queryResponses.at(inputHash);
|
||||
else
|
||||
{
|
||||
m_unhandledQueries.push_back(_input);
|
||||
return "unknown\n";
|
||||
}
|
||||
}
|
75
libsolidity/formal/CHCSmtLib2Interface.h
Normal file
75
libsolidity/formal/CHCSmtLib2Interface.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for solving Horn systems via smtlib2.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/formal/CHCSolverInterface.h>
|
||||
|
||||
#include <libsolidity/formal/SMTLib2Interface.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
namespace smt
|
||||
{
|
||||
|
||||
class CHCSmtLib2Interface: public CHCSolverInterface
|
||||
{
|
||||
public:
|
||||
explicit CHCSmtLib2Interface(std::map<h256, std::string> const& _queryResponses);
|
||||
|
||||
void reset();
|
||||
|
||||
void registerRelation(Expression const& _expr) override;
|
||||
|
||||
void addRule(Expression const& _expr, std::string const& _name) override;
|
||||
|
||||
std::pair<CheckResult, std::vector<std::string>> query(Expression const& _expr) override;
|
||||
|
||||
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
|
||||
|
||||
std::vector<std::string> unhandledQueries() const { return m_unhandledQueries; }
|
||||
|
||||
std::shared_ptr<SMTLib2Interface> smtlib2Interface() { return m_smtlib2; }
|
||||
|
||||
private:
|
||||
void declareFunction(std::string const& _name, SortPointer const& _sort);
|
||||
|
||||
void write(std::string _data);
|
||||
|
||||
/// Communicates with the solver via the callback. Throws SMTSolverError on error.
|
||||
std::string querySolver(std::string const& _input);
|
||||
|
||||
/// Used to access toSmtLibSort, SExpr, and handle variables.
|
||||
/// Needs to be a shared_ptr since it's also passed to EncodingContext.
|
||||
std::shared_ptr<SMTLib2Interface> m_smtlib2;
|
||||
|
||||
std::string m_accumulatedOutput;
|
||||
std::set<std::string> m_variables;
|
||||
|
||||
std::map<h256, std::string> const& m_queryResponses;
|
||||
std::vector<std::string> m_unhandledQueries;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ class CHCSolverInterface
|
||||
public:
|
||||
virtual ~CHCSolverInterface() = default;
|
||||
|
||||
virtual void declareVariable(std::string const& _name, Sort const& _sort) = 0;
|
||||
virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0;
|
||||
|
||||
/// Takes a function declaration as a relation.
|
||||
virtual void registerRelation(Expression const& _expr) = 0;
|
||||
|
@ -48,9 +48,10 @@ void CVC4Interface::pop()
|
||||
m_solver.pop();
|
||||
}
|
||||
|
||||
void CVC4Interface::declareVariable(string const& _name, Sort const& _sort)
|
||||
void CVC4Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
m_variables[_name] = m_context.mkVar(_name.c_str(), cvc4Sort(_sort));
|
||||
solAssert(_sort, "");
|
||||
m_variables[_name] = m_context.mkVar(_name.c_str(), cvc4Sort(*_sort));
|
||||
}
|
||||
|
||||
void CVC4Interface::addAssertion(Expression const& _expr)
|
||||
|
@ -50,7 +50,7 @@ public:
|
||||
void push() override;
|
||||
void pop() override;
|
||||
|
||||
void declareVariable(std::string const&, Sort const&) override;
|
||||
void declareVariable(std::string const&, SortPointer const&) override;
|
||||
|
||||
void addAssertion(Expression const& _expr) override;
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
||||
|
@ -24,7 +24,7 @@ using namespace dev::solidity;
|
||||
|
||||
ModelChecker::ModelChecker(ErrorReporter& _errorReporter, map<h256, string> const& _smtlib2Responses):
|
||||
m_bmc(m_context, _errorReporter, _smtlib2Responses),
|
||||
m_chc(m_context, _errorReporter),
|
||||
m_chc(m_context, _errorReporter, _smtlib2Responses),
|
||||
m_context()
|
||||
{
|
||||
}
|
||||
@ -40,5 +40,5 @@ void ModelChecker::analyze(SourceUnit const& _source)
|
||||
|
||||
vector<string> ModelChecker::unhandledQueries()
|
||||
{
|
||||
return m_bmc.unhandledQueries();
|
||||
return m_bmc.unhandledQueries() + m_chc.unhandledQueries();
|
||||
}
|
||||
|
@ -767,6 +767,15 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
||||
auto varDecl = identifierToVariable(*id);
|
||||
solAssert(varDecl, "");
|
||||
array = m_context.variable(*varDecl);
|
||||
|
||||
if (varDecl->type()->category() == Type::Category::FixedBytes)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
_indexAccess.location(),
|
||||
"Assertion checker does not yet support index accessing fixed bytes."
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (auto const& innerAccess = dynamic_cast<IndexAccess const*>(&_indexAccess.baseExpression()))
|
||||
{
|
||||
|
@ -60,27 +60,29 @@ void SMTLib2Interface::pop()
|
||||
m_accumulatedOutput.pop_back();
|
||||
}
|
||||
|
||||
void SMTLib2Interface::declareVariable(string const& _name, Sort const& _sort)
|
||||
void SMTLib2Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
if (_sort.kind == Kind::Function)
|
||||
solAssert(_sort, "");
|
||||
if (_sort->kind == Kind::Function)
|
||||
declareFunction(_name, _sort);
|
||||
else if (!m_variables.count(_name))
|
||||
{
|
||||
m_variables.insert(_name);
|
||||
write("(declare-fun |" + _name + "| () " + toSmtLibSort(_sort) + ')');
|
||||
m_variables.emplace(_name, _sort);
|
||||
write("(declare-fun |" + _name + "| () " + toSmtLibSort(*_sort) + ')');
|
||||
}
|
||||
}
|
||||
|
||||
void SMTLib2Interface::declareFunction(string const& _name, Sort const& _sort)
|
||||
void SMTLib2Interface::declareFunction(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort.kind == smt::Kind::Function, "");
|
||||
solAssert(_sort, "");
|
||||
solAssert(_sort->kind == smt::Kind::Function, "");
|
||||
// TODO Use domain and codomain as key as well
|
||||
if (!m_variables.count(_name))
|
||||
{
|
||||
FunctionSort fSort = dynamic_cast<FunctionSort const&>(_sort);
|
||||
string domain = toSmtLibSort(fSort.domain);
|
||||
string codomain = toSmtLibSort(*fSort.codomain);
|
||||
m_variables.insert(_name);
|
||||
auto const& fSort = dynamic_pointer_cast<FunctionSort>(_sort);
|
||||
string domain = toSmtLibSort(fSort->domain);
|
||||
string codomain = toSmtLibSort(*fSort->codomain);
|
||||
m_variables.emplace(_name, _sort);
|
||||
write(
|
||||
"(declare-fun |" +
|
||||
_name +
|
||||
@ -159,6 +161,7 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort)
|
||||
case Kind::Array:
|
||||
{
|
||||
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
|
||||
solAssert(arraySort.domain && arraySort.range, "");
|
||||
return "(Array " + toSmtLibSort(*arraySort.domain) + ' ' + toSmtLibSort(*arraySort.range) + ')';
|
||||
}
|
||||
default:
|
||||
|
@ -48,20 +48,23 @@ public:
|
||||
void push() override;
|
||||
void pop() override;
|
||||
|
||||
void declareVariable(std::string const&, Sort const&) override;
|
||||
void declareVariable(std::string const&, SortPointer const&) override;
|
||||
|
||||
void addAssertion(smt::Expression const& _expr) override;
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<smt::Expression> const& _expressionsToEvaluate) override;
|
||||
|
||||
std::vector<std::string> unhandledQueries() override { return m_unhandledQueries; }
|
||||
|
||||
private:
|
||||
void declareFunction(std::string const&, Sort const&);
|
||||
|
||||
// Used by CHCSmtLib2Interface
|
||||
std::string toSExpr(smt::Expression const& _expr);
|
||||
std::string toSmtLibSort(Sort const& _sort);
|
||||
std::string toSmtLibSort(std::vector<SortPointer> const& _sort);
|
||||
|
||||
std::map<std::string, SortPointer> variables() { return m_variables; }
|
||||
|
||||
private:
|
||||
void declareFunction(std::string const& _name, SortPointer const& _sort);
|
||||
|
||||
void write(std::string _data);
|
||||
|
||||
std::string checkSatAndGetValuesCommand(std::vector<smt::Expression> const& _expressionsToEvaluate);
|
||||
@ -71,7 +74,7 @@ private:
|
||||
std::string querySolver(std::string const& _input);
|
||||
|
||||
std::vector<std::string> m_accumulatedOutput;
|
||||
std::set<std::string> m_variables;
|
||||
std::map<std::string, SortPointer> m_variables;
|
||||
|
||||
std::map<h256, std::string> const& m_queryResponses;
|
||||
std::vector<std::string> m_unhandledQueries;
|
||||
|
@ -59,8 +59,9 @@ void SMTPortfolio::pop()
|
||||
s->pop();
|
||||
}
|
||||
|
||||
void SMTPortfolio::declareVariable(string const& _name, Sort const& _sort)
|
||||
void SMTPortfolio::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
for (auto const& s: m_solvers)
|
||||
s->declareVariable(_name, _sort);
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
void push() override;
|
||||
void pop() override;
|
||||
|
||||
void declareVariable(std::string const&, Sort const&) override;
|
||||
void declareVariable(std::string const&, SortPointer const&) override;
|
||||
|
||||
void addAssertion(smt::Expression const& _expr) override;
|
||||
|
||||
|
@ -338,13 +338,13 @@ public:
|
||||
virtual void push() = 0;
|
||||
virtual void pop() = 0;
|
||||
|
||||
virtual void declareVariable(std::string const& _name, Sort const& _sort) = 0;
|
||||
Expression newVariable(std::string _name, SortPointer _sort)
|
||||
virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0;
|
||||
Expression newVariable(std::string _name, SortPointer const& _sort)
|
||||
{
|
||||
// Subclasses should do something here
|
||||
solAssert(_sort, "");
|
||||
declareVariable(_name, *_sort);
|
||||
return Expression(std::move(_name), {}, std::move(_sort));
|
||||
declareVariable(_name, _sort);
|
||||
return Expression(std::move(_name), {}, _sort);
|
||||
}
|
||||
|
||||
virtual void addAssertion(Expression const& _expr) = 0;
|
||||
|
@ -47,8 +47,9 @@ Z3CHCInterface::Z3CHCInterface():
|
||||
m_solver.set(p);
|
||||
}
|
||||
|
||||
void Z3CHCInterface::declareVariable(string const& _name, Sort const& _sort)
|
||||
void Z3CHCInterface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
solAssert(_sort, "");
|
||||
m_z3Interface->declareVariable(_name, _sort);
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
Z3CHCInterface();
|
||||
|
||||
/// Forwards variable declaration to Z3Interface.
|
||||
void declareVariable(std::string const& _name, Sort const& _sort) override;
|
||||
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
|
||||
|
||||
void registerRelation(Expression const& _expr) override;
|
||||
|
||||
|
@ -50,14 +50,15 @@ void Z3Interface::pop()
|
||||
m_solver.pop();
|
||||
}
|
||||
|
||||
void Z3Interface::declareVariable(string const& _name, Sort const& _sort)
|
||||
void Z3Interface::declareVariable(string const& _name, SortPointer const& _sort)
|
||||
{
|
||||
if (_sort.kind == Kind::Function)
|
||||
declareFunction(_name, _sort);
|
||||
solAssert(_sort, "");
|
||||
if (_sort->kind == Kind::Function)
|
||||
declareFunction(_name, *_sort);
|
||||
else if (m_constants.count(_name))
|
||||
m_constants.at(_name) = m_context.constant(_name.c_str(), z3Sort(_sort));
|
||||
m_constants.at(_name) = m_context.constant(_name.c_str(), z3Sort(*_sort));
|
||||
else
|
||||
m_constants.emplace(_name, m_context.constant(_name.c_str(), z3Sort(_sort)));
|
||||
m_constants.emplace(_name, m_context.constant(_name.c_str(), z3Sort(*_sort)));
|
||||
}
|
||||
|
||||
void Z3Interface::declareFunction(string const& _name, Sort const& _sort)
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
void push() override;
|
||||
void pop() override;
|
||||
|
||||
void declareVariable(std::string const& _name, Sort const& _sort) override;
|
||||
void declareVariable(std::string const& _name, SortPointer const& _sort) override;
|
||||
|
||||
void addAssertion(Expression const& _expr) override;
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
||||
|
@ -85,6 +85,10 @@ enum class PatternKind
|
||||
class Pattern
|
||||
{
|
||||
public:
|
||||
using Builtins = dev::eth::EVMBuiltins;
|
||||
static constexpr size_t WordSize = 256;
|
||||
using Word = dev::u256;
|
||||
|
||||
/// Matches any expression.
|
||||
Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {}
|
||||
// Matches a specific constant value.
|
||||
|
@ -1,34 +1,159 @@
|
||||
file(GLOB sources "*.cpp")
|
||||
file(GLOB headers "*.h")
|
||||
set(sources
|
||||
boostTest.cpp
|
||||
Common.cpp
|
||||
Common.h
|
||||
EVMHost.cpp
|
||||
EVMHost.h
|
||||
ExecutionFramework.cpp
|
||||
ExecutionFramework.h
|
||||
InteractiveTests.h
|
||||
Metadata.cpp
|
||||
Metadata.h
|
||||
Options.cpp
|
||||
Options.h
|
||||
TestCase.cpp
|
||||
TestCase.h
|
||||
)
|
||||
detect_stray_source_files("${sources}" ".")
|
||||
|
||||
file(GLOB contracts_sources "contracts/*.cpp")
|
||||
file(GLOB contracts_headers "contracts/*.h")
|
||||
file(GLOB libdevcore_sources "libdevcore/*.cpp")
|
||||
file(GLOB libdevcore_headers "libdevcore/*.h")
|
||||
file(GLOB liblangutil_sources "liblangutil/*.cpp")
|
||||
file(GLOB liblangutil_headers "liblangutil/*.h")
|
||||
file(GLOB libevmasm_sources "libevmasm/*.cpp")
|
||||
file(GLOB libevmasm_headers "libevmasm/*.h")
|
||||
file(GLOB libyul_sources "libyul/*.cpp")
|
||||
file(GLOB libyul_headers "libyul/*.h")
|
||||
if (LLL)
|
||||
file(GLOB liblll_sources "liblll/*.cpp")
|
||||
file(GLOB liblll_headers "liblll/*.h")
|
||||
endif()
|
||||
file(GLOB libsolidity_sources "libsolidity/*.cpp")
|
||||
file(GLOB libsolidity_headers "libsolidity/*.h")
|
||||
file(GLOB libsolidity_util_sources "libsolidity/util/*.cpp")
|
||||
file(GLOB libsolidity_util_headers "libsolidity/util/*.h")
|
||||
set(contracts_sources
|
||||
contracts/AuctionRegistrar.cpp
|
||||
contracts/ContractInterface.h
|
||||
contracts/FixedFeeRegistrar.cpp
|
||||
contracts/Wallet.cpp
|
||||
)
|
||||
detect_stray_source_files("${contracts_sources}" "contracts/")
|
||||
|
||||
add_executable(soltest ${sources} ${headers}
|
||||
${contracts_sources} ${contracts_headers}
|
||||
${libdevcore_sources} ${libdevcore_headers}
|
||||
${liblangutil_sources} ${liblangutil_headers}
|
||||
${libevmasm_sources} ${libevmasm_headers}
|
||||
${libyul_sources} ${libyul_headers}
|
||||
${liblll_sources} ${liblll_headers}
|
||||
${libsolidity_sources} ${libsolidity_headers}
|
||||
${libsolidity_util_sources} ${libsolidity_util_headers}
|
||||
set(libdevcore_sources
|
||||
libdevcore/Checksum.cpp
|
||||
libdevcore/CommonData.cpp
|
||||
libdevcore/IndentedWriter.cpp
|
||||
libdevcore/IpfsHash.cpp
|
||||
libdevcore/IterateReplacing.cpp
|
||||
libdevcore/JSON.cpp
|
||||
libdevcore/Keccak256.cpp
|
||||
libdevcore/StringUtils.cpp
|
||||
libdevcore/SwarmHash.cpp
|
||||
libdevcore/UTF8.cpp
|
||||
libdevcore/Whiskers.cpp
|
||||
)
|
||||
detect_stray_source_files("${libdevcore_sources}" "libdevcore/")
|
||||
|
||||
set(libevmasm_sources
|
||||
libevmasm/Assembler.cpp
|
||||
libevmasm/Optimiser.cpp
|
||||
)
|
||||
detect_stray_source_files("${libevmasm_sources}" "libevmasm/")
|
||||
|
||||
set(liblangutil_sources
|
||||
liblangutil/CharStream.cpp
|
||||
liblangutil/SourceLocation.cpp
|
||||
)
|
||||
detect_stray_source_files("${liblangutil_sources}" "liblangutil/")
|
||||
|
||||
if(LLL)
|
||||
set (liblll_sources
|
||||
liblll/Compiler.cpp
|
||||
liblll/EndToEndTest.cpp
|
||||
liblll/ExecutionFramework.cpp
|
||||
liblll/ExecutionFramework.h
|
||||
liblll/LLL_ENS.cpp
|
||||
liblll/LLL_ERC20.cpp
|
||||
liblll/Parser.cpp
|
||||
)
|
||||
detect_stray_source_files("${liblll_sources}" "liblll/")
|
||||
endif(LLL)
|
||||
|
||||
set(libsolidity_sources
|
||||
libsolidity/ABIDecoderTests.cpp
|
||||
libsolidity/ABIEncoderTests.cpp
|
||||
libsolidity/ABIJsonTest.cpp
|
||||
libsolidity/ABIJsonTest.h
|
||||
libsolidity/ABITestsCommon.h
|
||||
libsolidity/AnalysisFramework.cpp
|
||||
libsolidity/AnalysisFramework.h
|
||||
libsolidity/Assembly.cpp
|
||||
libsolidity/ASTJSONTest.cpp
|
||||
libsolidity/ASTJSONTest.h
|
||||
libsolidity/ErrorCheck.cpp
|
||||
libsolidity/ErrorCheck.h
|
||||
libsolidity/GasCosts.cpp
|
||||
libsolidity/GasMeter.cpp
|
||||
libsolidity/GasTest.cpp
|
||||
libsolidity/GasTest.h
|
||||
libsolidity/Imports.cpp
|
||||
libsolidity/InlineAssembly.cpp
|
||||
libsolidity/LibSolc.cpp
|
||||
libsolidity/Metadata.cpp
|
||||
libsolidity/SemanticTest.cpp
|
||||
libsolidity/SemanticTest.h
|
||||
libsolidity/SemVerMatcher.cpp
|
||||
libsolidity/SMTChecker.cpp
|
||||
libsolidity/SMTCheckerJSONTest.cpp
|
||||
libsolidity/SMTCheckerJSONTest.h
|
||||
libsolidity/SolidityCompiler.cpp
|
||||
libsolidity/SolidityEndToEndTest.cpp
|
||||
libsolidity/SolidityExecutionFramework.cpp
|
||||
libsolidity/SolidityExecutionFramework.h
|
||||
libsolidity/SolidityExpressionCompiler.cpp
|
||||
libsolidity/SolidityNameAndTypeResolution.cpp
|
||||
libsolidity/SolidityNatspecJSON.cpp
|
||||
libsolidity/SolidityOptimizer.cpp
|
||||
libsolidity/SolidityParser.cpp
|
||||
libsolidity/SolidityScanner.cpp
|
||||
libsolidity/SolidityTypes.cpp
|
||||
libsolidity/StandardCompiler.cpp
|
||||
libsolidity/SyntaxTest.cpp
|
||||
libsolidity/SyntaxTest.h
|
||||
libsolidity/ViewPureChecker.cpp
|
||||
)
|
||||
detect_stray_source_files("${libsolidity_sources}" "libsolidity/")
|
||||
|
||||
set(libsolidity_util_sources
|
||||
libsolidity/util/BytesUtils.cpp
|
||||
libsolidity/util/BytesUtils.h
|
||||
libsolidity/util/ContractABIUtils.cpp
|
||||
libsolidity/util/ContractABIUtils.h
|
||||
libsolidity/util/SoltestErrors.h
|
||||
libsolidity/util/SoltestTypes.h
|
||||
libsolidity/util/TestFileParser.cpp
|
||||
libsolidity/util/TestFileParser.h
|
||||
libsolidity/util/TestFileParserTests.cpp
|
||||
libsolidity/util/TestFunctionCall.cpp
|
||||
libsolidity/util/TestFunctionCall.h
|
||||
libsolidity/util/TestFunctionCallTests.cpp
|
||||
)
|
||||
detect_stray_source_files("${libsolidity_util_sources}" "libsolidity/util/")
|
||||
|
||||
set(libyul_sources
|
||||
libyul/Common.cpp
|
||||
libyul/Common.h
|
||||
libyul/CompilabilityChecker.cpp
|
||||
libyul/FunctionSideEffects.cpp
|
||||
libyul/FunctionSideEffects.h
|
||||
libyul/Inliner.cpp
|
||||
libyul/Metrics.cpp
|
||||
libyul/ObjectCompilerTest.cpp
|
||||
libyul/ObjectCompilerTest.h
|
||||
libyul/ObjectParser.cpp
|
||||
libyul/Parser.cpp
|
||||
libyul/StackReuseCodegen.cpp
|
||||
libyul/YulInterpreterTest.cpp
|
||||
libyul/YulInterpreterTest.h
|
||||
libyul/YulOptimizerTest.cpp
|
||||
libyul/YulOptimizerTest.h
|
||||
)
|
||||
detect_stray_source_files("${libyul_sources}" "libyul/")
|
||||
|
||||
add_executable(soltest ${sources}
|
||||
${contracts_sources}
|
||||
${libdevcore_sources}
|
||||
${liblangutil_sources}
|
||||
${libevmasm_sources}
|
||||
${libyul_sources}
|
||||
${liblll_sources}
|
||||
${libsolidity_sources}
|
||||
${libsolidity_util_sources}
|
||||
)
|
||||
target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm devcore Boost::boost Boost::program_options Boost::unit_test_framework evmc)
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include <test/EVMHost.h>
|
||||
|
||||
#include <test/evmc/helpers.hpp>
|
||||
#include <test/evmc/loader.h>
|
||||
|
||||
#include <libevmasm/GasMeter.h>
|
||||
@ -42,9 +41,17 @@ evmc::vm* EVMHost::getVM(string const& _path)
|
||||
if (!theVM && !_path.empty())
|
||||
{
|
||||
evmc_loader_error_code errorCode = {};
|
||||
evmc_instance* vm = evmc_load_and_create(_path.c_str(), &errorCode);
|
||||
evmc_instance* vm = evmc_load_and_configure(_path.c_str(), &errorCode);
|
||||
if (vm && errorCode == EVMC_LOADER_SUCCESS)
|
||||
theVM = make_unique<evmc::vm>(vm);
|
||||
{
|
||||
if (evmc_vm_has_capability(vm, EVMC_CAPABILITY_EVM1))
|
||||
theVM = make_unique<evmc::vm>(vm);
|
||||
else
|
||||
{
|
||||
evmc_destroy(vm);
|
||||
cerr << "VM loaded does not support EVM1" << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "Error loading VM from " << _path;
|
||||
@ -83,27 +90,27 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm):
|
||||
m_evmVersion = EVMC_PETERSBURG;
|
||||
}
|
||||
|
||||
evmc_storage_status EVMHost::set_storage(const evmc_address& _addr, const evmc_bytes32& _key, const evmc_bytes32& _value) noexcept
|
||||
evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept
|
||||
{
|
||||
evmc_bytes32 previousValue = m_state.accounts[_addr].storage[_key];
|
||||
evmc::bytes32 previousValue = m_state.accounts[_addr].storage[_key];
|
||||
m_state.accounts[_addr].storage[_key] = _value;
|
||||
|
||||
// TODO EVMC_STORAGE_MODIFIED_AGAIN should be also used
|
||||
if (previousValue == _value)
|
||||
return EVMC_STORAGE_UNCHANGED;
|
||||
else if (previousValue == evmc_bytes32{})
|
||||
else if (previousValue == evmc::bytes32{})
|
||||
return EVMC_STORAGE_ADDED;
|
||||
else if (_value == evmc_bytes32{})
|
||||
else if (_value == evmc::bytes32{})
|
||||
return EVMC_STORAGE_DELETED;
|
||||
else
|
||||
return EVMC_STORAGE_MODIFIED;
|
||||
|
||||
}
|
||||
|
||||
void EVMHost::selfdestruct(const evmc_address& _addr, const evmc_address& _beneficiary) noexcept
|
||||
void EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _beneficiary) noexcept
|
||||
{
|
||||
// TODO actual selfdestruct is even more complicated.
|
||||
evmc_uint256be balance = m_state.accounts[_addr].balance;
|
||||
evmc::uint256be balance = m_state.accounts[_addr].balance;
|
||||
m_state.accounts.erase(_addr);
|
||||
m_state.accounts[_beneficiary].balance = balance;
|
||||
}
|
||||
@ -182,7 +189,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
|
||||
destination.balance = convertToEVMC(u256(convertFromEVMC(destination.balance)) + value);
|
||||
}
|
||||
|
||||
evmc_address currentAddress = m_currentAddress;
|
||||
evmc::address currentAddress = m_currentAddress;
|
||||
m_currentAddress = message.destination;
|
||||
evmc::result result = m_vm->execute(*this, m_evmVersion, message, code.data(), code.size());
|
||||
m_currentAddress = currentAddress;
|
||||
@ -223,16 +230,16 @@ evmc_tx_context EVMHost::get_tx_context() noexcept
|
||||
return ctx;
|
||||
}
|
||||
|
||||
evmc_bytes32 EVMHost::get_block_hash(int64_t _number) noexcept
|
||||
evmc::bytes32 EVMHost::get_block_hash(int64_t _number) noexcept
|
||||
{
|
||||
return convertToEVMC(u256("0x3737373737373737373737373737373737373737373737373737373737373737") + _number);
|
||||
}
|
||||
|
||||
void EVMHost::emit_log(
|
||||
evmc_address const& _addr,
|
||||
evmc::address const& _addr,
|
||||
uint8_t const* _data,
|
||||
size_t _dataSize,
|
||||
evmc_bytes32 const _topics[],
|
||||
evmc::bytes32 const _topics[],
|
||||
size_t _topicsCount
|
||||
) noexcept
|
||||
{
|
||||
@ -245,27 +252,27 @@ void EVMHost::emit_log(
|
||||
}
|
||||
|
||||
|
||||
Address EVMHost::convertFromEVMC(evmc_address const& _addr)
|
||||
Address EVMHost::convertFromEVMC(evmc::address const& _addr)
|
||||
{
|
||||
return Address(bytes(begin(_addr.bytes), end(_addr.bytes)));
|
||||
}
|
||||
|
||||
evmc_address EVMHost::convertToEVMC(Address const& _addr)
|
||||
evmc::address EVMHost::convertToEVMC(Address const& _addr)
|
||||
{
|
||||
evmc_address a;
|
||||
evmc::address a;
|
||||
for (size_t i = 0; i < 20; ++i)
|
||||
a.bytes[i] = _addr[i];
|
||||
return a;
|
||||
}
|
||||
|
||||
h256 EVMHost::convertFromEVMC(evmc_bytes32 const& _data)
|
||||
h256 EVMHost::convertFromEVMC(evmc::bytes32 const& _data)
|
||||
{
|
||||
return h256(bytes(begin(_data.bytes), end(_data.bytes)));
|
||||
}
|
||||
|
||||
evmc_bytes32 EVMHost::convertToEVMC(h256 const& _data)
|
||||
evmc::bytes32 EVMHost::convertToEVMC(h256 const& _data)
|
||||
{
|
||||
evmc_bytes32 d;
|
||||
evmc::bytes32 d;
|
||||
for (size_t i = 0; i < 32; ++i)
|
||||
d.bytes[i] = _data[i];
|
||||
return d;
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
#include <test/evmc/evmc.hpp>
|
||||
#include <test/evmc/evmc.h>
|
||||
#include <test/evmc/helpers.hpp>
|
||||
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
@ -47,11 +46,11 @@ public:
|
||||
|
||||
struct Account
|
||||
{
|
||||
evmc_uint256be balance = {};
|
||||
evmc::uint256be balance = {};
|
||||
size_t nonce = 0;
|
||||
bytes code;
|
||||
evmc_bytes32 codeHash = {};
|
||||
std::map<evmc_bytes32, evmc_bytes32> storage;
|
||||
evmc::bytes32 codeHash = {};
|
||||
std::map<evmc::bytes32, evmc::bytes32> storage;
|
||||
};
|
||||
|
||||
struct LogEntry
|
||||
@ -65,11 +64,11 @@ public:
|
||||
{
|
||||
size_t blockNumber;
|
||||
uint64_t timestamp;
|
||||
std::map<evmc_address, Account> accounts;
|
||||
std::map<evmc::address, Account> accounts;
|
||||
std::vector<LogEntry> logs;
|
||||
};
|
||||
|
||||
Account* account(evmc_address const& _address)
|
||||
Account* account(evmc::address const& _address)
|
||||
{
|
||||
// Make all precompiled contracts exist.
|
||||
// Be future-proof and consider everything below 1024 as precompiled contract.
|
||||
@ -87,12 +86,12 @@ public:
|
||||
m_state.logs.clear();
|
||||
}
|
||||
|
||||
bool account_exists(evmc_address const& _addr) noexcept final
|
||||
bool account_exists(evmc::address const& _addr) noexcept final
|
||||
{
|
||||
return account(_addr) != nullptr;
|
||||
}
|
||||
|
||||
evmc_bytes32 get_storage(evmc_address const& _addr, evmc_bytes32 const& _key) noexcept final
|
||||
evmc::bytes32 get_storage(evmc::address const& _addr, evmc::bytes32 const& _key) noexcept final
|
||||
{
|
||||
if (Account* acc = account(_addr))
|
||||
return acc->storage[_key];
|
||||
@ -100,26 +99,26 @@ public:
|
||||
}
|
||||
|
||||
evmc_storage_status set_storage(
|
||||
evmc_address const& _addr,
|
||||
evmc_bytes32 const& _key,
|
||||
evmc_bytes32 const& _value
|
||||
evmc::address const& _addr,
|
||||
evmc::bytes32 const& _key,
|
||||
evmc::bytes32 const& _value
|
||||
) noexcept;
|
||||
|
||||
evmc_uint256be get_balance(evmc_address const& _addr) noexcept final
|
||||
evmc::uint256be get_balance(evmc::address const& _addr) noexcept final
|
||||
{
|
||||
if (Account const* acc = account(_addr))
|
||||
return acc->balance;
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t get_code_size(evmc_address const& _addr) noexcept final
|
||||
size_t get_code_size(evmc::address const& _addr) noexcept final
|
||||
{
|
||||
if (Account const* acc = account(_addr))
|
||||
return acc->code.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
evmc_bytes32 get_code_hash(evmc_address const& _addr) noexcept final
|
||||
evmc::bytes32 get_code_hash(evmc::address const& _addr) noexcept final
|
||||
{
|
||||
if (Account const* acc = account(_addr))
|
||||
return acc->codeHash;
|
||||
@ -127,7 +126,7 @@ public:
|
||||
}
|
||||
|
||||
size_t copy_code(
|
||||
evmc_address const& _addr,
|
||||
evmc::address const& _addr,
|
||||
size_t _codeOffset,
|
||||
uint8_t* _bufferData,
|
||||
size_t _bufferSize
|
||||
@ -140,31 +139,31 @@ public:
|
||||
return i;
|
||||
}
|
||||
|
||||
void selfdestruct(evmc_address const& _addr, evmc_address const& _beneficiary) noexcept;
|
||||
void selfdestruct(evmc::address const& _addr, evmc::address const& _beneficiary) noexcept;
|
||||
|
||||
evmc::result call(evmc_message const& _message) noexcept;
|
||||
|
||||
evmc_tx_context get_tx_context() noexcept;
|
||||
|
||||
evmc_bytes32 get_block_hash(int64_t number) noexcept;
|
||||
evmc::bytes32 get_block_hash(int64_t number) noexcept;
|
||||
|
||||
void emit_log(
|
||||
evmc_address const& _addr,
|
||||
evmc::address const& _addr,
|
||||
uint8_t const* _data,
|
||||
size_t _dataSize,
|
||||
evmc_bytes32 const _topics[],
|
||||
evmc::bytes32 const _topics[],
|
||||
size_t _topicsCount
|
||||
) noexcept;
|
||||
|
||||
static Address convertFromEVMC(evmc_address const& _addr);
|
||||
static evmc_address convertToEVMC(Address const& _addr);
|
||||
static h256 convertFromEVMC(evmc_bytes32 const& _data);
|
||||
static evmc_bytes32 convertToEVMC(h256 const& _data);
|
||||
static Address convertFromEVMC(evmc::address const& _addr);
|
||||
static evmc::address convertToEVMC(Address const& _addr);
|
||||
static h256 convertFromEVMC(evmc::bytes32 const& _data);
|
||||
static evmc::bytes32 convertToEVMC(h256 const& _data);
|
||||
|
||||
|
||||
State m_state;
|
||||
evmc_address m_currentAddress = {};
|
||||
evmc_address m_coinbase = convertToEVMC(Address("0x7878787878787878787878787878787878787878"));
|
||||
evmc::address m_currentAddress = {};
|
||||
evmc::address m_coinbase = convertToEVMC(Address("0x7878787878787878787878787878787878787878"));
|
||||
|
||||
private:
|
||||
evmc::result precompileECRecover(evmc_message const& _message) noexcept;
|
||||
|
@ -26,7 +26,6 @@
|
||||
|
||||
#include <test/evmc/evmc.hpp>
|
||||
#include <test/evmc/loader.h>
|
||||
#include <test/evmc/helpers.hpp>
|
||||
|
||||
#include <libdevcore/CommonIO.h>
|
||||
|
||||
@ -235,7 +234,7 @@ bool ExecutionFramework::storageEmpty(Address const& _addr)
|
||||
if (EVMHost::Account const* acc = m_evmHost->account(EVMHost::convertToEVMC(_addr)))
|
||||
{
|
||||
for (auto const& entry: acc->storage)
|
||||
if (!(entry.second == evmc_bytes32{}))
|
||||
if (!(entry.second == evmc::bytes32{}))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -303,7 +303,10 @@ enum evmc_status_code
|
||||
* For example, the Client tries running a code in the EVM 1.5. If the
|
||||
* code is not supported there, the execution falls back to the EVM 1.0.
|
||||
*/
|
||||
EVMC_REJECTED = -2
|
||||
EVMC_REJECTED = -2,
|
||||
|
||||
/** The VM failed to allocate the amount of memory needed for execution. */
|
||||
EVMC_OUT_OF_MEMORY = -3
|
||||
};
|
||||
|
||||
/* Forward declaration. */
|
||||
@ -795,13 +798,15 @@ enum evmc_revision
|
||||
*
|
||||
* This function MAY be invoked multiple times for a single VM instance.
|
||||
*
|
||||
* @param instance The VM instance.
|
||||
* @param context The pointer to the Client execution context to be passed
|
||||
* to the callback functions. See ::evmc_context.
|
||||
* @param rev Requested EVM specification revision.
|
||||
* @param msg Call parameters. See ::evmc_message.
|
||||
* @param code Reference to the code to be executed.
|
||||
* @param code_size The length of the code.
|
||||
* @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 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,
|
||||
@ -857,7 +862,11 @@ typedef uint32_t evmc_capabilities_flagset;
|
||||
*/
|
||||
typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_instance* instance);
|
||||
|
||||
/** The opaque type representing a Client-side tracer object. */
|
||||
/**
|
||||
* The opaque type representing a Client-side tracer object.
|
||||
*
|
||||
* @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer().
|
||||
*/
|
||||
struct evmc_tracer_context;
|
||||
|
||||
/**
|
||||
@ -869,6 +878,8 @@ struct evmc_tracer_context;
|
||||
* 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.
|
||||
@ -916,6 +927,8 @@ typedef void (*evmc_trace_callback)(struct evmc_tracer_context* context,
|
||||
*
|
||||
* 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.
|
||||
@ -989,6 +1002,10 @@ struct evmc_instance
|
||||
* 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;
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <evmc/evmc.h>
|
||||
#include <evmc/helpers.h>
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
@ -14,6 +15,250 @@
|
||||
/// @ingroup cpp
|
||||
namespace evmc
|
||||
{
|
||||
/// The big-endian 160-bit hash suitable for keeping an Ethereum address.
|
||||
///
|
||||
/// This type wraps C ::evmc_address to make sure objects of this type are always initialized.
|
||||
struct address : evmc_address
|
||||
{
|
||||
/// Default and converting constructor.
|
||||
///
|
||||
/// Initializes bytes to zeros if not other @p init value provided.
|
||||
constexpr address(evmc_address init = {}) noexcept : evmc_address{init} {}
|
||||
|
||||
/// Explicit operator converting to bool.
|
||||
constexpr inline explicit operator bool() const noexcept;
|
||||
};
|
||||
|
||||
/// The fixed size array of 32 bytes for storing 256-bit EVM values.
|
||||
///
|
||||
/// This type wraps C ::evmc_bytes32 to make sure objects of this type are always initialized.
|
||||
struct bytes32 : evmc_bytes32
|
||||
{
|
||||
/// Default and converting constructor.
|
||||
///
|
||||
/// Initializes bytes to zeros if not other @p init value provided.
|
||||
constexpr bytes32(evmc_bytes32 init = {}) noexcept : evmc_bytes32{init} {}
|
||||
|
||||
/// Explicit operator converting to bool.
|
||||
constexpr inline explicit operator bool() const noexcept;
|
||||
};
|
||||
|
||||
/// The alias for evmc::bytes32 to represent a big-endian 256-bit integer.
|
||||
using uint256be = bytes32;
|
||||
|
||||
|
||||
/// Loads 64 bits / 8 bytes of data from the given @p bytes array in big-endian order.
|
||||
constexpr inline uint64_t load64be(const uint8_t* bytes) noexcept
|
||||
{
|
||||
// TODO: Report bug in clang incorrectly optimizing this with AVX2 enabled.
|
||||
return (uint64_t{bytes[0]} << 56) | (uint64_t{bytes[1]} << 48) | (uint64_t{bytes[2]} << 40) |
|
||||
(uint64_t{bytes[3]} << 32) | (uint64_t{bytes[4]} << 24) | (uint64_t{bytes[5]} << 16) |
|
||||
(uint64_t{bytes[6]} << 8) | uint64_t{bytes[7]};
|
||||
}
|
||||
|
||||
/// Loads 32 bits / 4 bytes of data from the given @p bytes array in big-endian order.
|
||||
constexpr inline uint32_t load32be(const uint8_t* bytes) noexcept
|
||||
{
|
||||
return (uint32_t{bytes[0]} << 24) | (uint32_t{bytes[1]} << 16) | (uint32_t{bytes[2]} << 8) |
|
||||
uint32_t{bytes[3]};
|
||||
}
|
||||
|
||||
namespace fnv
|
||||
{
|
||||
constexpr auto prime = 0x100000001b3; ///< The 64-bit FNV prime number.
|
||||
constexpr auto offset_basis = 0xcbf29ce484222325; ///< The 64-bit FNV offset basis.
|
||||
|
||||
/// The hashing transformation for 64-bit inputs based on the FNV-1a formula.
|
||||
constexpr inline uint64_t fnv1a_by64(uint64_t h, uint64_t x) noexcept
|
||||
{
|
||||
return (h ^ x) * prime;
|
||||
}
|
||||
} // namespace fnv
|
||||
|
||||
|
||||
/// The "equal" comparison operator for the evmc::address type.
|
||||
constexpr bool operator==(const address& a, const address& b) noexcept
|
||||
{
|
||||
// TODO: Report bug in clang keeping unnecessary bswap.
|
||||
return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
|
||||
load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
|
||||
load32be(&a.bytes[16]) == load32be(&b.bytes[16]);
|
||||
}
|
||||
|
||||
/// The "not equal" comparison operator for the evmc::address type.
|
||||
constexpr bool operator!=(const address& a, const address& b) noexcept
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/// The "less" comparison operator for the evmc::address type.
|
||||
constexpr bool operator<(const address& a, const address& b) noexcept
|
||||
{
|
||||
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
|
||||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
|
||||
load64be(&a.bytes[8]) < load64be(&b.bytes[8])) ||
|
||||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
|
||||
load32be(&a.bytes[16]) < load32be(&b.bytes[16]));
|
||||
}
|
||||
|
||||
/// The "equal" comparison operator for the evmc::bytes32 type.
|
||||
constexpr bool operator==(const bytes32& a, const bytes32& b) noexcept
|
||||
{
|
||||
return load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
|
||||
load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
|
||||
load64be(&a.bytes[16]) == load64be(&b.bytes[16]) &&
|
||||
load64be(&a.bytes[24]) == load64be(&b.bytes[24]);
|
||||
}
|
||||
|
||||
/// The "not equal" comparison operator for the evmc::bytes32 type.
|
||||
constexpr bool operator!=(const bytes32& a, const bytes32& b) noexcept
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/// The "less" comparison operator for the evmc::bytes32 type.
|
||||
constexpr bool operator<(const bytes32& a, const bytes32& b) noexcept
|
||||
{
|
||||
return load64be(&a.bytes[0]) < load64be(&b.bytes[0]) ||
|
||||
(load64be(&a.bytes[0]) == load64be(&b.bytes[0]) &&
|
||||
load64be(&a.bytes[8]) < load64be(&b.bytes[8])) ||
|
||||
(load64be(&a.bytes[8]) == load64be(&b.bytes[8]) &&
|
||||
load64be(&a.bytes[16]) < load64be(&b.bytes[16])) ||
|
||||
(load64be(&a.bytes[16]) == load64be(&b.bytes[16]) &&
|
||||
load64be(&a.bytes[24]) < load64be(&b.bytes[24]));
|
||||
}
|
||||
|
||||
/// Checks if the given address is the zero address.
|
||||
constexpr inline bool is_zero(const address& a) noexcept
|
||||
{
|
||||
return a == address{};
|
||||
}
|
||||
|
||||
constexpr address::operator bool() const noexcept
|
||||
{
|
||||
return !is_zero(*this);
|
||||
}
|
||||
|
||||
/// Checks if the given bytes32 object has all zero bytes.
|
||||
constexpr inline bool is_zero(const bytes32& a) noexcept
|
||||
{
|
||||
return a == bytes32{};
|
||||
}
|
||||
|
||||
constexpr bytes32::operator bool() const noexcept
|
||||
{
|
||||
return !is_zero(*this);
|
||||
}
|
||||
|
||||
namespace literals
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
template <typename T, T... Ints>
|
||||
struct integer_sequence
|
||||
{
|
||||
};
|
||||
|
||||
template <uint8_t... Bytes>
|
||||
using byte_sequence = integer_sequence<uint8_t, Bytes...>;
|
||||
|
||||
template <char... Chars>
|
||||
using char_sequence = integer_sequence<char, Chars...>;
|
||||
|
||||
|
||||
template <typename, typename>
|
||||
struct concatenate;
|
||||
|
||||
template <uint8_t... Bytes1, uint8_t... Bytes2>
|
||||
struct concatenate<byte_sequence<Bytes1...>, byte_sequence<Bytes2...>>
|
||||
{
|
||||
using type = byte_sequence<Bytes1..., Bytes2...>;
|
||||
};
|
||||
|
||||
template <uint8_t D>
|
||||
constexpr uint8_t parse_hex_digit() noexcept
|
||||
{
|
||||
static_assert((D >= '0' && D <= '9') || (D >= 'a' && D <= 'f') || (D >= 'A' && D <= 'F'),
|
||||
"literal must be hexadecimal integer");
|
||||
return static_cast<uint8_t>(
|
||||
(D >= '0' && D <= '9') ? D - '0' : (D >= 'a' && D <= 'f') ? D - 'a' + 10 : D - 'A' + 10);
|
||||
}
|
||||
|
||||
|
||||
template <typename>
|
||||
struct parse_digits;
|
||||
|
||||
template <uint8_t Digit1, uint8_t Digit2>
|
||||
struct parse_digits<byte_sequence<Digit1, Digit2>>
|
||||
{
|
||||
using type = byte_sequence<static_cast<uint8_t>(parse_hex_digit<Digit1>() << 4) |
|
||||
parse_hex_digit<Digit2>()>;
|
||||
};
|
||||
|
||||
template <uint8_t Digit1, uint8_t Digit2, uint8_t... Rest>
|
||||
struct parse_digits<byte_sequence<Digit1, Digit2, Rest...>>
|
||||
{
|
||||
using type = typename concatenate<typename parse_digits<byte_sequence<Digit1, Digit2>>::type,
|
||||
typename parse_digits<byte_sequence<Rest...>>::type>::type;
|
||||
};
|
||||
|
||||
|
||||
template <typename, typename>
|
||||
struct parse_literal;
|
||||
|
||||
template <typename T, char Prefix1, char Prefix2, char... Literal>
|
||||
struct parse_literal<T, char_sequence<Prefix1, Prefix2, Literal...>>
|
||||
{
|
||||
static_assert(Prefix1 == '0' && Prefix2 == 'x', "literal must be in hexadecimal notation");
|
||||
static_assert(sizeof...(Literal) == sizeof(T) * 2, "literal must match the result type size");
|
||||
|
||||
template <uint8_t... Bytes>
|
||||
static constexpr T create_from(byte_sequence<Bytes...>) noexcept
|
||||
{
|
||||
return T{{{Bytes...}}};
|
||||
}
|
||||
|
||||
static constexpr T get() noexcept
|
||||
{
|
||||
return create_from(typename parse_digits<byte_sequence<Literal...>>::type{});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, char Digit>
|
||||
struct parse_literal<T, char_sequence<Digit>>
|
||||
{
|
||||
static_assert(Digit == '0', "only 0 is allowed as a single digit literal");
|
||||
static constexpr T get() noexcept { return {}; }
|
||||
};
|
||||
|
||||
template <typename T, char... Literal>
|
||||
constexpr T parse() noexcept
|
||||
{
|
||||
return parse_literal<T, char_sequence<Literal...>>::get();
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
/// Literal for evmc::address.
|
||||
template <char... Literal>
|
||||
constexpr address operator"" _address() noexcept
|
||||
{
|
||||
return internal::parse<address, Literal...>();
|
||||
}
|
||||
|
||||
/// Literal for evmc::bytes32.
|
||||
template <char... Literal>
|
||||
constexpr bytes32 operator"" _bytes32() noexcept
|
||||
{
|
||||
return internal::parse<bytes32, Literal...>();
|
||||
}
|
||||
} // namespace literals
|
||||
|
||||
using namespace literals;
|
||||
|
||||
|
||||
/// Alias for evmc_make_result().
|
||||
constexpr auto make_result = evmc_make_result;
|
||||
|
||||
/// @copydoc evmc_result
|
||||
///
|
||||
/// This is a RAII wrapper for evmc_result and objects of this type
|
||||
@ -27,6 +272,22 @@ public:
|
||||
using evmc_result::output_size;
|
||||
using evmc_result::status_code;
|
||||
|
||||
/// Creates the result from the provided arguments.
|
||||
///
|
||||
/// The provided output is copied to memory allocated with malloc()
|
||||
/// and the evmc_result::release function is set to one invoking free().
|
||||
///
|
||||
/// @param _status_code The status code.
|
||||
/// @param _gas_left The amount of gas left.
|
||||
/// @param _output_data The pointer to the output.
|
||||
/// @param _output_size The output size.
|
||||
result(evmc_status_code _status_code,
|
||||
int64_t _gas_left,
|
||||
const uint8_t* _output_data,
|
||||
size_t _output_size) noexcept
|
||||
: evmc_result{make_result(_status_code, _gas_left, _output_data, _output_size)}
|
||||
{}
|
||||
|
||||
/// Converting constructor from raw evmc_result.
|
||||
explicit result(evmc_result const& res) noexcept : evmc_result{res} {}
|
||||
|
||||
@ -80,11 +341,32 @@ public:
|
||||
class vm
|
||||
{
|
||||
public:
|
||||
vm() noexcept = default;
|
||||
|
||||
/// Converting constructor from evmc_instance.
|
||||
explicit vm(evmc_instance* instance) noexcept : m_instance{instance} {}
|
||||
|
||||
/// Destructor responsible for automatically destroying the VM instance.
|
||||
~vm() noexcept { m_instance->destroy(m_instance); }
|
||||
~vm() noexcept
|
||||
{
|
||||
if (m_instance)
|
||||
m_instance->destroy(m_instance);
|
||||
}
|
||||
|
||||
vm(const vm&) = delete;
|
||||
vm& operator=(const vm&) = delete;
|
||||
|
||||
/// Move constructor.
|
||||
vm(vm&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; }
|
||||
|
||||
/// Move assignment operator.
|
||||
vm& operator=(vm&& other) noexcept
|
||||
{
|
||||
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.
|
||||
@ -96,6 +378,9 @@ public:
|
||||
set_option(option.first, option.second);
|
||||
}
|
||||
|
||||
/// Checks if contains a valid pointer to the VM instance.
|
||||
explicit operator bool() const noexcept { return m_instance != nullptr; }
|
||||
|
||||
/// 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; }
|
||||
|
||||
@ -128,7 +413,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
evmc_instance* const m_instance = nullptr;
|
||||
evmc_instance* m_instance = nullptr;
|
||||
};
|
||||
|
||||
/// The EVMC Host interface
|
||||
@ -138,35 +423,33 @@ public:
|
||||
virtual ~HostInterface() noexcept = default;
|
||||
|
||||
/// @copydoc evmc_host_interface::account_exists
|
||||
virtual bool account_exists(const evmc_address& addr) noexcept = 0;
|
||||
virtual bool account_exists(const address& addr) noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::get_storage
|
||||
virtual evmc_bytes32 get_storage(const evmc_address& addr,
|
||||
const evmc_bytes32& key) noexcept = 0;
|
||||
virtual bytes32 get_storage(const address& addr, const bytes32& key) noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::set_storage
|
||||
virtual evmc_storage_status set_storage(const evmc_address& addr,
|
||||
const evmc_bytes32& key,
|
||||
const evmc_bytes32& value) noexcept = 0;
|
||||
virtual evmc_storage_status set_storage(const address& addr,
|
||||
const bytes32& key,
|
||||
const bytes32& value) noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::get_balance
|
||||
virtual evmc_uint256be get_balance(const evmc_address& addr) noexcept = 0;
|
||||
virtual uint256be get_balance(const address& addr) noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::get_code_size
|
||||
virtual size_t get_code_size(const evmc_address& addr) noexcept = 0;
|
||||
virtual size_t get_code_size(const address& addr) noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::get_code_hash
|
||||
virtual evmc_bytes32 get_code_hash(const evmc_address& addr) noexcept = 0;
|
||||
virtual bytes32 get_code_hash(const address& addr) noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::copy_code
|
||||
virtual size_t copy_code(const evmc_address& addr,
|
||||
virtual size_t copy_code(const address& addr,
|
||||
size_t code_offset,
|
||||
uint8_t* buffer_data,
|
||||
size_t buffer_size) noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::selfdestruct
|
||||
virtual void selfdestruct(const evmc_address& addr,
|
||||
const evmc_address& beneficiary) noexcept = 0;
|
||||
virtual void selfdestruct(const address& addr, const address& beneficiary) noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::call
|
||||
virtual result call(const evmc_message& msg) noexcept = 0;
|
||||
@ -175,13 +458,13 @@ public:
|
||||
virtual evmc_tx_context get_tx_context() noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::get_block_hash
|
||||
virtual evmc_bytes32 get_block_hash(int64_t block_number) noexcept = 0;
|
||||
virtual bytes32 get_block_hash(int64_t block_number) noexcept = 0;
|
||||
|
||||
/// @copydoc evmc_host_interface::emit_log
|
||||
virtual void emit_log(const evmc_address& addr,
|
||||
virtual void emit_log(const address& addr,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
const evmc_bytes32 topics[],
|
||||
const bytes32 topics[],
|
||||
size_t num_topics) noexcept = 0;
|
||||
};
|
||||
|
||||
@ -198,39 +481,39 @@ public:
|
||||
/// Implicit converting constructor from evmc_context.
|
||||
HostContext(evmc_context* ctx) noexcept : context{ctx} {} // NOLINT
|
||||
|
||||
bool account_exists(const evmc_address& address) noexcept final
|
||||
bool account_exists(const address& address) noexcept final
|
||||
{
|
||||
return context->host->account_exists(context, &address);
|
||||
}
|
||||
|
||||
evmc_bytes32 get_storage(const evmc_address& address, const evmc_bytes32& key) noexcept final
|
||||
bytes32 get_storage(const address& address, const bytes32& key) noexcept final
|
||||
{
|
||||
return context->host->get_storage(context, &address, &key);
|
||||
}
|
||||
|
||||
evmc_storage_status set_storage(const evmc_address& address,
|
||||
const evmc_bytes32& key,
|
||||
const evmc_bytes32& value) noexcept final
|
||||
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);
|
||||
}
|
||||
|
||||
evmc_uint256be get_balance(const evmc_address& address) noexcept final
|
||||
uint256be get_balance(const address& address) noexcept final
|
||||
{
|
||||
return context->host->get_balance(context, &address);
|
||||
}
|
||||
|
||||
size_t get_code_size(const evmc_address& address) noexcept final
|
||||
size_t get_code_size(const address& address) noexcept final
|
||||
{
|
||||
return context->host->get_code_size(context, &address);
|
||||
}
|
||||
|
||||
evmc_bytes32 get_code_hash(const evmc_address& address) noexcept final
|
||||
bytes32 get_code_hash(const address& address) noexcept final
|
||||
{
|
||||
return context->host->get_code_hash(context, &address);
|
||||
}
|
||||
|
||||
size_t copy_code(const evmc_address& address,
|
||||
size_t copy_code(const address& address,
|
||||
size_t code_offset,
|
||||
uint8_t* buffer_data,
|
||||
size_t buffer_size) noexcept final
|
||||
@ -238,9 +521,9 @@ public:
|
||||
return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
|
||||
}
|
||||
|
||||
void selfdestruct(const evmc_address& address, const evmc_address& beneficiary) noexcept final
|
||||
void selfdestruct(const address& addr, const address& beneficiary) noexcept final
|
||||
{
|
||||
context->host->selfdestruct(context, &address, &beneficiary);
|
||||
context->host->selfdestruct(context, &addr, &beneficiary);
|
||||
}
|
||||
|
||||
result call(const evmc_message& message) noexcept final
|
||||
@ -261,18 +544,18 @@ public:
|
||||
return tx_context;
|
||||
}
|
||||
|
||||
evmc_bytes32 get_block_hash(int64_t number) noexcept final
|
||||
bytes32 get_block_hash(int64_t number) noexcept final
|
||||
{
|
||||
return context->host->get_block_hash(context, number);
|
||||
}
|
||||
|
||||
void emit_log(const evmc_address& address,
|
||||
void emit_log(const address& addr,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
const evmc_bytes32 topics[],
|
||||
const bytes32 topics[],
|
||||
size_t topics_count) noexcept final
|
||||
{
|
||||
context->host->emit_log(context, &address, data, data_size, topics, topics_count);
|
||||
context->host->emit_log(context, &addr, data, data_size, topics, topics_count);
|
||||
}
|
||||
};
|
||||
|
||||
@ -351,7 +634,8 @@ inline void emit_log(evmc_context* h,
|
||||
const evmc_bytes32 topics[],
|
||||
size_t num_topics) noexcept
|
||||
{
|
||||
static_cast<Host*>(h)->emit_log(*addr, data, data_size, topics, num_topics);
|
||||
static_cast<Host*>(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(topics),
|
||||
num_topics);
|
||||
}
|
||||
|
||||
constexpr evmc_host_interface interface{
|
||||
@ -360,6 +644,42 @@ constexpr evmc_host_interface interface{
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
inline Host::Host() noexcept : evmc_context{&internal::interface} {}
|
||||
inline Host::Host() noexcept : evmc_context{&evmc::internal::interface} {}
|
||||
|
||||
} // namespace evmc
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// Hash operator template specialization for evmc::address. Needed for unordered containers.
|
||||
template <>
|
||||
struct hash<evmc::address>
|
||||
{
|
||||
/// Hash operator using FNV1a-based folding.
|
||||
constexpr size_t operator()(const evmc::address& s) const noexcept
|
||||
{
|
||||
using namespace evmc;
|
||||
using namespace fnv;
|
||||
return static_cast<size_t>(fnv1a_by64(
|
||||
fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])), load64be(&s.bytes[8])),
|
||||
load32be(&s.bytes[16])));
|
||||
}
|
||||
};
|
||||
|
||||
/// Hash operator template specialization for evmc::bytes32. Needed for unordered containers.
|
||||
template <>
|
||||
struct hash<evmc::bytes32>
|
||||
{
|
||||
/// Hash operator using FNV1a-based folding.
|
||||
constexpr size_t operator()(const evmc::bytes32& s) const noexcept
|
||||
{
|
||||
using namespace evmc;
|
||||
using namespace fnv;
|
||||
return static_cast<size_t>(
|
||||
fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv1a_by64(fnv::offset_basis, load64be(&s.bytes[0])),
|
||||
load64be(&s.bytes[8])),
|
||||
load64be(&s.bytes[16])),
|
||||
load64be(&s.bytes[24])));
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
@ -18,6 +18,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <evmc/evmc.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Returns true if the VM instance has a compatible ABI version.
|
||||
@ -83,6 +85,7 @@ static inline enum evmc_set_option_result evmc_set_option(struct evmc_instance*
|
||||
*
|
||||
* @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)
|
||||
@ -106,6 +109,58 @@ static inline struct evmc_result evmc_execute(struct evmc_instance* instance,
|
||||
return instance->execute(instance, context, rev, msg, code, code_size);
|
||||
}
|
||||
|
||||
/// The evmc_result release function using free() for releasing the memory.
|
||||
///
|
||||
/// This function is used in the evmc_make_result(),
|
||||
/// but may be also used in other case if convenient.
|
||||
///
|
||||
/// @param result The result object.
|
||||
static void evmc_free_result_memory(const struct evmc_result* result)
|
||||
{
|
||||
free((uint8_t*)result->output_data);
|
||||
}
|
||||
|
||||
/// Creates the result from the provided arguments.
|
||||
///
|
||||
/// The provided output is copied to memory allocated with malloc()
|
||||
/// and the evmc_result::release function is set to one invoking free().
|
||||
///
|
||||
/// In case of memory allocation failure, the result has all fields zeroed
|
||||
/// and only evmc_result::status_code is set to ::EVMC_OUT_OF_MEMORY internal error.
|
||||
///
|
||||
/// @param status_code The status code.
|
||||
/// @param gas_left The amount of gas left.
|
||||
/// @param output_data The pointer to the output.
|
||||
/// @param output_size The output size.
|
||||
static inline struct evmc_result evmc_make_result(enum evmc_status_code status_code,
|
||||
int64_t gas_left,
|
||||
const uint8_t* output_data,
|
||||
size_t output_size)
|
||||
{
|
||||
struct evmc_result result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
|
||||
if (output_size != 0)
|
||||
{
|
||||
uint8_t* buffer = (uint8_t*)malloc(output_size);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
result.status_code = EVMC_OUT_OF_MEMORY;
|
||||
return result;
|
||||
}
|
||||
|
||||
memcpy(buffer, output_data, output_size);
|
||||
result.output_data = buffer;
|
||||
result.output_size = output_size;
|
||||
result.release = evmc_free_result_memory;
|
||||
}
|
||||
|
||||
result.status_code = status_code;
|
||||
result.gas_left = gas_left;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the resources allocated to the execution result.
|
||||
*
|
||||
|
@ -12,58 +12,78 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <evmc/evmc.h>
|
||||
#include <evmc/evmc.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
using evmc::is_zero;
|
||||
|
||||
/// The comparator for std::map<evmc_address, ...>.
|
||||
EVMC_DEPRECATED
|
||||
inline bool operator<(const evmc_address& a, const evmc_address& b)
|
||||
{
|
||||
return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0;
|
||||
}
|
||||
|
||||
/// The comparator for std::map<evmc_bytes32, ...>.
|
||||
EVMC_DEPRECATED
|
||||
inline bool operator<(const evmc_bytes32& a, const evmc_bytes32& b)
|
||||
{
|
||||
return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) < 0;
|
||||
}
|
||||
|
||||
/// The comparator for equality.
|
||||
EVMC_DEPRECATED
|
||||
inline bool operator==(const evmc_address& a, const evmc_address& b)
|
||||
{
|
||||
return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0;
|
||||
}
|
||||
|
||||
/// The comparator for equality.
|
||||
EVMC_DEPRECATED
|
||||
inline bool operator==(const evmc_bytes32& a, const evmc_bytes32& b)
|
||||
{
|
||||
return std::memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0;
|
||||
}
|
||||
|
||||
/// Check if the address is zero (all bytes are zeros).
|
||||
inline bool is_zero(const evmc_address& address) noexcept
|
||||
/// Parameters for the fnv1a hash function, specialized by the hash result size (size_t).
|
||||
///
|
||||
/// The values for the matching size are taken from
|
||||
/// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters.
|
||||
///
|
||||
/// @tparam size The size of the hash result (size_t).
|
||||
template <size_t size>
|
||||
struct fnv1_params
|
||||
{
|
||||
return address == evmc_address{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Check if the hash is zero (all bytes are zeros).
|
||||
inline bool is_zero(const evmc_bytes32& x) noexcept
|
||||
/// Parameters for the fnv1a hash function, specialized for the hash result of 4 bytes.
|
||||
template <>
|
||||
struct fnv1_params<4>
|
||||
{
|
||||
return x == evmc_bytes32{};
|
||||
}
|
||||
static constexpr auto prime = 0x1000193; ///< The FNV prime.
|
||||
static constexpr auto offset_basis = 0x811c9dc5; ///< The FNV offset basis.
|
||||
};
|
||||
|
||||
/// FNV1a hash function with 64-bit result.
|
||||
inline uint64_t fnv1a_64(const uint8_t* ptr, size_t len)
|
||||
/// Parameters for the fnv1a hash function, specialized for the hash result of 8 bytes.
|
||||
template <>
|
||||
struct fnv1_params<8>
|
||||
{
|
||||
constexpr uint64_t prime = 1099511628211ULL;
|
||||
constexpr uint64_t offset_basis = 14695981039346656037ULL;
|
||||
static constexpr auto prime = 0x100000001b3; ///< The FNV prime.
|
||||
static constexpr auto offset_basis = 0xcbf29ce484222325; ///< The FNV offset basis.
|
||||
};
|
||||
|
||||
uint64_t ret = offset_basis;
|
||||
/// FNV1a hash function.
|
||||
inline size_t fnv1a(const uint8_t* ptr, size_t len) noexcept
|
||||
{
|
||||
using params = fnv1_params<sizeof(size_t)>;
|
||||
|
||||
auto ret = size_t{params::offset_basis};
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
ret ^= ptr[i];
|
||||
ret *= prime;
|
||||
ret *= params::prime;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -72,25 +92,23 @@ namespace std
|
||||
{
|
||||
/// Hash operator template specialization for evmc_address needed for unordered containers.
|
||||
template <>
|
||||
struct hash<evmc_address>
|
||||
struct EVMC_DEPRECATED hash<evmc_address>
|
||||
{
|
||||
/// Hash operator using FNV1a.
|
||||
std::enable_if<sizeof(size_t) == 8, std::size_t>::type operator()(const evmc_address& s) const
|
||||
noexcept
|
||||
size_t operator()(const evmc_address& s) const noexcept
|
||||
{
|
||||
return fnv1a_64(s.bytes, sizeof(s.bytes));
|
||||
return fnv1a(s.bytes, sizeof(s.bytes));
|
||||
}
|
||||
};
|
||||
|
||||
/// Hash operator template needed for std::unordered_set and others using hashes.
|
||||
template <>
|
||||
struct hash<evmc_bytes32>
|
||||
struct EVMC_DEPRECATED hash<evmc_bytes32>
|
||||
{
|
||||
/// Hash operator using FNV1a.
|
||||
std::enable_if<sizeof(size_t) == 8, std::size_t>::type operator()(const evmc_bytes32& s) const
|
||||
noexcept
|
||||
size_t operator()(const evmc_bytes32& s) const noexcept
|
||||
{
|
||||
return fnv1a_64(s.bytes, sizeof(s.bytes));
|
||||
return fnv1a(s.bytes, sizeof(s.bytes));
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
@ -42,20 +42,14 @@
|
||||
#define ATTR_FORMAT(...)
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
#define strcpy_sx strcpy_s
|
||||
#else
|
||||
/*
|
||||
* Limited variant of strcpy_s().
|
||||
*
|
||||
* Provided for C standard libraries where strcpy_s() is not available.
|
||||
* The availability check might need to adjusted for other C standard library implementations.
|
||||
*/
|
||||
#if !defined(EVMC_LOADER_MOCK)
|
||||
static
|
||||
#endif
|
||||
int
|
||||
strcpy_sx(char* restrict dest, size_t destsz, const char* restrict src)
|
||||
strcpy_sx(char* dest, size_t destsz, const char* src)
|
||||
{
|
||||
size_t len = strlen(src);
|
||||
if (len >= destsz)
|
||||
@ -70,7 +64,6 @@ static
|
||||
dest[len] = 0;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define PATH_MAX_LENGTH 4096
|
||||
|
||||
@ -238,3 +231,93 @@ exit:
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Gets the token delimited by @p delim character of the string pointed by the @p str_ptr.
|
||||
/// If the delimiter is not found, the whole string is returned.
|
||||
/// The @p str_ptr is also slided after the delimiter or to the string end
|
||||
/// if the delimiter is not found (in this case the @p str_ptr points to an empty string).
|
||||
static char* get_token(char** str_ptr, char delim)
|
||||
{
|
||||
char* str = *str_ptr;
|
||||
char* delim_pos = strchr(str, delim);
|
||||
if (delim_pos)
|
||||
{
|
||||
// If the delimiter is found, null it to get null-terminated prefix
|
||||
// and slide the str_ptr after the delimiter.
|
||||
*delim_pos = '\0';
|
||||
*str_ptr = delim_pos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, slide the str_ptr to the end and return the whole string as the prefix.
|
||||
*str_ptr += strlen(str);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
struct evmc_instance* 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;
|
||||
|
||||
char config_copy_buffer[PATH_MAX_LENGTH];
|
||||
if (strcpy_sx(config_copy_buffer, sizeof(config_copy_buffer), config) != 0)
|
||||
{
|
||||
ec = set_error(EVMC_LOADER_INVALID_ARGUMENT,
|
||||
"invalid argument: configuration is too long (maximum allowed length is %d)",
|
||||
(int)sizeof(config_copy_buffer));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
char* options = config_copy_buffer;
|
||||
const char* path = get_token(&options, ',');
|
||||
|
||||
instance = evmc_load_and_create(path, error_code);
|
||||
if (!instance)
|
||||
return NULL;
|
||||
|
||||
if (instance->set_option == NULL && strlen(options) != 0)
|
||||
{
|
||||
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options",
|
||||
instance->name, path);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
||||
while (strlen(options) != 0)
|
||||
{
|
||||
char* option = get_token(&options, ',');
|
||||
|
||||
// Slit option into name and value by taking the name token.
|
||||
// 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);
|
||||
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);
|
||||
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,
|
||||
option, name);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (error_code)
|
||||
*error_code = ec;
|
||||
|
||||
if (ec == EVMC_LOADER_SUCCESS)
|
||||
return instance;
|
||||
|
||||
if (instance)
|
||||
evmc_destroy(instance);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -40,7 +40,13 @@ enum evmc_loader_error_code
|
||||
EVMC_LOADER_INSTANCE_CREATION_FAILURE = 4,
|
||||
|
||||
/** The ABI version of the VM instance has mismatched. */
|
||||
EVMC_LOADER_ABI_VERSION_MISMATCH = 5
|
||||
EVMC_LOADER_ABI_VERSION_MISMATCH = 5,
|
||||
|
||||
/** The VM option is invalid. */
|
||||
EVMC_LOADER_INVALID_OPTION_NAME = 6,
|
||||
|
||||
/** The VM option value is invalid. */
|
||||
EVMC_LOADER_INVALID_OPTION_VALUE = 7
|
||||
};
|
||||
|
||||
/**
|
||||
@ -111,6 +117,43 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
|
||||
struct evmc_instance* 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.
|
||||
*
|
||||
* This function performs the following actions atomically:
|
||||
* - loads the EVMC module (as evmc_load()),
|
||||
* - creates the VM instance,
|
||||
* - configures the VM instance with options provided in the @p config parameter.
|
||||
*
|
||||
* The configuration string (@p config) has the following syntax:
|
||||
*
|
||||
* <path> ("," <option-name> ["=" <option-value>])*
|
||||
*
|
||||
* In this syntax, an option without a value can be specified (`,option,`)
|
||||
* as a shortcut for using empty value (`,option=,`).
|
||||
*
|
||||
* Options are passed to a VM in the order they are specified in the configuration string.
|
||||
* It is up to the VM implementation how to handle duplicated options and other conflicts.
|
||||
*
|
||||
* Example configuration string:
|
||||
*
|
||||
* ./modules/vm.so,engine=compiler,trace,verbosity=2
|
||||
*
|
||||
* The function signals the same errors as evmc_load_and_create() and additionally:
|
||||
* - ::EVMC_LOADER_INVALID_OPTION_NAME
|
||||
* when the provided options list contains an option unknown for the VM,
|
||||
* - ::EVMC_LOADER_INVALID_OPTION_VALUE
|
||||
* when there exists unsupported value for a given VM option.
|
||||
|
||||
*
|
||||
* @param config The path to the EVMC module with additional configuration options.
|
||||
* @param error_code The pointer to the error code. If not NULL the value is set to
|
||||
* ::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);
|
||||
|
||||
/**
|
||||
* Returns the human-readable message describing the most recent error
|
||||
* that occurred in EVMC loading since the last call to this function.
|
||||
|
@ -0,0 +1,10 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
bytes20 x;
|
||||
function f(bytes16 b) public view {
|
||||
b[uint8(x[2])];
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (116-120): Assertion checker does not yet support index accessing fixed bytes.
|
||||
// Warning: (108-122): Assertion checker does not yet support index accessing fixed bytes.
|
@ -92,8 +92,13 @@ message TestFunction {
|
||||
}
|
||||
|
||||
message Contract {
|
||||
enum Test {
|
||||
CALLDATA_CODER = 1;
|
||||
RETURNDATA_CODER = 2;
|
||||
}
|
||||
required VarDecl state_vars = 1;
|
||||
required TestFunction testfunction = 2;
|
||||
required Test test = 3;
|
||||
}
|
||||
|
||||
package dev.test.abiv2fuzzer;
|
@ -114,7 +114,14 @@ template <typename T>
|
||||
pair<string, string> ProtoConverter::processType(T const& _type, bool _isValueType)
|
||||
{
|
||||
ostringstream local, global;
|
||||
auto [varName, paramName] = newVarNames(getNextVarCounter());
|
||||
auto [varName, paramName] = newVarNames(getNextVarCounter(), m_isStateVar);
|
||||
|
||||
// Add variable name to the argument list of coder function call
|
||||
if (m_argsCoder.str().empty())
|
||||
m_argsCoder << varName;
|
||||
else
|
||||
m_argsCoder << ", " << varName;
|
||||
|
||||
string location{};
|
||||
if (!m_isStateVar && !_isValueType)
|
||||
location = "memory";
|
||||
@ -177,6 +184,16 @@ pair<string, string> ProtoConverter::varDecl(
|
||||
_paramName,
|
||||
((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
|
||||
);
|
||||
appendTypes(
|
||||
_isValueType,
|
||||
typeStr,
|
||||
((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
|
||||
);
|
||||
appendTypedReturn(
|
||||
_isValueType,
|
||||
typeStr,
|
||||
((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD)
|
||||
);
|
||||
|
||||
// Update dyn param only if necessary
|
||||
if (tVisitor.isLastDynParamRightPadded())
|
||||
@ -259,6 +276,41 @@ void ProtoConverter::appendTypedParams(
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::appendTypes(
|
||||
bool _isValueType,
|
||||
string const& _typeString,
|
||||
Delimiter _delimiter
|
||||
)
|
||||
{
|
||||
string qualifiedTypeString = (
|
||||
_isValueType ?
|
||||
_typeString :
|
||||
_typeString + " memory"
|
||||
);
|
||||
m_types << Whiskers(R"(<delimiter><type>)")
|
||||
("delimiter", delimiterToString(_delimiter))
|
||||
("type", qualifiedTypeString)
|
||||
.render();
|
||||
}
|
||||
|
||||
void ProtoConverter::appendTypedReturn(
|
||||
bool _isValueType,
|
||||
string const& _typeString,
|
||||
Delimiter _delimiter
|
||||
)
|
||||
{
|
||||
string qualifiedTypeString = (
|
||||
_isValueType ?
|
||||
_typeString :
|
||||
_typeString + " memory"
|
||||
);
|
||||
m_typedReturn << Whiskers(R"(<delimiter><type> <varName>)")
|
||||
("delimiter", delimiterToString(_delimiter))
|
||||
("type", qualifiedTypeString)
|
||||
("varName", "lv_" + to_string(m_varCounter - 1))
|
||||
.render();
|
||||
}
|
||||
|
||||
// Adds the qualifier "calldata" to non-value parameter of an external function.
|
||||
void ProtoConverter::appendTypedParamsExternal(
|
||||
bool _isValueType,
|
||||
@ -310,7 +362,6 @@ std::string ProtoConverter::typedParametersAsString(CalleeType _calleeType)
|
||||
}
|
||||
}
|
||||
|
||||
// Test function to be called externally.
|
||||
string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDefs)
|
||||
{
|
||||
// TODO: Support more than one but less than N local variables
|
||||
@ -320,38 +371,76 @@ string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDe
|
||||
string localVarDefs = localVarBuffers.second;
|
||||
|
||||
ostringstream testBuffer;
|
||||
string functionDecl = "function test() public returns (uint)";
|
||||
|
||||
string testFunction = Whiskers(R"(
|
||||
function test() public returns (uint) {
|
||||
<?calldata>return test_calldata_coding();</calldata>
|
||||
<?returndata>return test_returndata_coding();</returndata>
|
||||
})")
|
||||
("calldata", m_test == Contract_Test::Contract_Test_CALLDATA_CODER)
|
||||
("returndata", m_test == Contract_Test::Contract_Test_RETURNDATA_CODER)
|
||||
.render();
|
||||
|
||||
string functionDeclCalldata = "function test_calldata_coding() internal returns (uint)";
|
||||
string functionDeclReturndata = "function test_returndata_coding() internal returns (uint)";
|
||||
|
||||
testBuffer << Whiskers(R"(<structTypeDecl>
|
||||
<functionDecl> {
|
||||
<testFunction>
|
||||
<?calldata>
|
||||
<functionDeclCalldata> {
|
||||
<storageVarDefs>
|
||||
<localVarDefs>
|
||||
<testCode>
|
||||
})")
|
||||
<calldataTestCode>
|
||||
}
|
||||
<calldataHelperFuncs>
|
||||
</calldata>
|
||||
<?returndata>
|
||||
<functionDeclReturndata> {
|
||||
<returndataTestCode>
|
||||
}
|
||||
|
||||
<?varsPresent>
|
||||
function coder_returndata_external() external returns (<return_types>) {
|
||||
<storageVarDefs>
|
||||
<localVarDefs>
|
||||
return (<return_values>);
|
||||
}
|
||||
</varsPresent>
|
||||
</returndata>)")
|
||||
("testFunction", testFunction)
|
||||
("calldata", m_test == Contract_Test::Contract_Test_CALLDATA_CODER)
|
||||
("returndata", m_test == Contract_Test::Contract_Test_RETURNDATA_CODER)
|
||||
("calldataHelperFuncs", calldataHelperFunctions())
|
||||
("varsPresent", !m_types.str().empty())
|
||||
("structTypeDecl", structTypeDecl)
|
||||
("functionDecl", functionDecl)
|
||||
("functionDeclCalldata", functionDeclCalldata)
|
||||
("functionDeclReturndata", functionDeclReturndata)
|
||||
("storageVarDefs", _storageVarDefs)
|
||||
("localVarDefs", localVarDefs)
|
||||
("testCode", testCode(_x.invalid_encoding_length()))
|
||||
("calldataTestCode", testCallDataFunction(_x.invalid_encoding_length()))
|
||||
("returndataTestCode", testReturnDataFunction())
|
||||
("return_types", m_types.str())
|
||||
("return_values", m_argsCoder.str())
|
||||
.render();
|
||||
return testBuffer.str();
|
||||
}
|
||||
|
||||
string ProtoConverter::testCode(unsigned _invalidLength)
|
||||
string ProtoConverter::testCallDataFunction(unsigned _invalidLength)
|
||||
{
|
||||
return Whiskers(R"(
|
||||
uint returnVal = this.coder_public(<parameterNames>);
|
||||
uint returnVal = this.coder_calldata_public(<argumentNames>);
|
||||
if (returnVal != 0)
|
||||
return returnVal;
|
||||
|
||||
returnVal = this.coder_external(<parameterNames>);
|
||||
returnVal = this.coder_calldata_external(<argumentNames>);
|
||||
if (returnVal != 0)
|
||||
return uint(200000) + returnVal;
|
||||
|
||||
<?atLeastOneVar>
|
||||
bytes memory argumentEncoding = abi.encode(<parameterNames>);
|
||||
bytes memory argumentEncoding = abi.encode(<argumentNames>);
|
||||
|
||||
returnVal = checkEncodedCall(
|
||||
this.coder_public.selector,
|
||||
this.coder_calldata_public.selector,
|
||||
argumentEncoding,
|
||||
<invalidLengthFuzz>,
|
||||
<isRightPadded>
|
||||
@ -360,7 +449,7 @@ string ProtoConverter::testCode(unsigned _invalidLength)
|
||||
return returnVal;
|
||||
|
||||
returnVal = checkEncodedCall(
|
||||
this.coder_external.selector,
|
||||
this.coder_calldata_external.selector,
|
||||
argumentEncoding,
|
||||
<invalidLengthFuzz>,
|
||||
<isRightPadded>
|
||||
@ -370,26 +459,32 @@ string ProtoConverter::testCode(unsigned _invalidLength)
|
||||
</atLeastOneVar>
|
||||
return 0;
|
||||
)")
|
||||
("parameterNames", dev::suffixedVariableNameList(s_varNamePrefix, 0, m_varCounter))
|
||||
("argumentNames", m_argsCoder.str())
|
||||
("invalidLengthFuzz", std::to_string(_invalidLength))
|
||||
("isRightPadded", isLastDynParamRightPadded() ? "true" : "false")
|
||||
("atLeastOneVar", m_varCounter > 0)
|
||||
.render();
|
||||
}
|
||||
|
||||
string ProtoConverter::helperFunctions()
|
||||
string ProtoConverter::testReturnDataFunction()
|
||||
{
|
||||
stringstream helperFuncs;
|
||||
helperFuncs << R"(
|
||||
function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
|
||||
if(a.length != b.length)
|
||||
return false;
|
||||
for (uint i = 0; i < a.length; i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return Whiskers(R"(
|
||||
<?varsPresent>
|
||||
(<varDecl>) = this.coder_returndata_external();
|
||||
<equality_checks>
|
||||
</varsPresent>
|
||||
return 0;
|
||||
)")
|
||||
("varsPresent", !m_typedReturn.str().empty())
|
||||
("varDecl", m_typedReturn.str())
|
||||
("equality_checks", m_checks.str())
|
||||
.render();
|
||||
}
|
||||
|
||||
string ProtoConverter::calldataHelperFunctions()
|
||||
{
|
||||
stringstream calldataHelperFuncs;
|
||||
calldataHelperFuncs << R"(
|
||||
/// Accepts function selector, correct argument encoding, and length of
|
||||
/// invalid encoding and returns the correct and incorrect abi encoding
|
||||
/// for calling the function specified by the function selector.
|
||||
@ -452,19 +547,18 @@ string ProtoConverter::helperFunctions()
|
||||
if (success == true)
|
||||
return 400001;
|
||||
return 0;
|
||||
}
|
||||
)";
|
||||
})";
|
||||
|
||||
// These are callee functions that encode from storage, decode to
|
||||
// memory/calldata and check if decoded value matches storage value
|
||||
// return true on successful match, false otherwise
|
||||
helperFuncs << Whiskers(R"(
|
||||
function coder_public(<parameters_memory>) public pure returns (uint) {
|
||||
calldataHelperFuncs << Whiskers(R"(
|
||||
function coder_calldata_public(<parameters_memory>) public pure returns (uint) {
|
||||
<equality_checks>
|
||||
return 0;
|
||||
}
|
||||
|
||||
function coder_external(<parameters_calldata>) external pure returns (uint) {
|
||||
function coder_calldata_external(<parameters_calldata>) external pure returns (uint) {
|
||||
<equality_checks>
|
||||
return 0;
|
||||
}
|
||||
@ -473,6 +567,25 @@ string ProtoConverter::helperFunctions()
|
||||
("equality_checks", equalityChecksAsString())
|
||||
("parameters_calldata", typedParametersAsString(CalleeType::EXTERNAL))
|
||||
.render();
|
||||
|
||||
return calldataHelperFuncs.str();
|
||||
}
|
||||
|
||||
string ProtoConverter::commonHelperFunctions()
|
||||
{
|
||||
stringstream helperFuncs;
|
||||
helperFuncs << R"(
|
||||
/// Compares bytes, returning true if they are equal and false otherwise.
|
||||
function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
|
||||
if(a.length != b.length)
|
||||
return false;
|
||||
for (uint i = 0; i < a.length; i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
)";
|
||||
|
||||
return helperFuncs.str();
|
||||
}
|
||||
|
||||
@ -481,6 +594,9 @@ void ProtoConverter::visit(Contract const& _x)
|
||||
string pragmas = R"(pragma solidity >=0.0;
|
||||
pragma experimental ABIEncoderV2;)";
|
||||
|
||||
// Record test spec
|
||||
m_test = _x.test();
|
||||
|
||||
// TODO: Support more than one but less than N state variables
|
||||
auto storageBuffers = visit(_x.state_vars());
|
||||
string storageVarDecls = storageBuffers.first;
|
||||
@ -499,7 +615,7 @@ pragma experimental ABIEncoderV2;)";
|
||||
ostringstream contractBody;
|
||||
contractBody << storageVarDecls
|
||||
<< testFunction
|
||||
<< helperFunctions();
|
||||
<< commonHelperFunctions();
|
||||
m_output << Whiskers(R"(<pragmas>
|
||||
<contractStart>
|
||||
<contractBody>
|
||||
@ -1085,4 +1201,4 @@ string ValueGetterVisitor::bytesArrayValueAsString(unsigned _counter, bool _isHe
|
||||
_counter,
|
||||
_isHexLiteral
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -19,84 +19,118 @@
|
||||
/**
|
||||
* Template of the solidity test program generated by this converter is as follows:
|
||||
*
|
||||
* pragma solidity >=0.0;
|
||||
* pragma experimental ABIEncoderV2;
|
||||
* pragma solidity >=0.0;
|
||||
* pragma experimental ABIEncoderV2;
|
||||
*
|
||||
* contract C {
|
||||
* // State variable
|
||||
* string x_0;
|
||||
* // Test function that is called by the VM.
|
||||
* function test() public returns (uint) {
|
||||
* // Local variable
|
||||
* bytes x_1 = "1";
|
||||
* x_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
|
||||
* uint returnVal = this.coder_public(x_0, x_1);
|
||||
* if (returnVal != 0)
|
||||
* return returnVal;
|
||||
* // Since the return codes in the public and external coder functions are identical
|
||||
* // we offset error code by a fixed amount (200000) for differentiation.
|
||||
* returnVal = this.coder_external(x_0, x_1);
|
||||
* if (returnVal != 0)
|
||||
* return 200000 + returnVal;
|
||||
* // Encode parameters
|
||||
* bytes memory argumentEncoding = abi.encode(<parameter_names>);
|
||||
* returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||
* // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data.
|
||||
* if (returnVal != 0)
|
||||
* return returnVal;
|
||||
* contract C {
|
||||
* // State variable
|
||||
* string sv_0;
|
||||
* // Test function that is called by the VM.
|
||||
* // There are 2 variations of this function: one returns
|
||||
* // the output of test_calldata_coding() and the other
|
||||
* // returns the output of test_returndata_coding(). The
|
||||
* // proto field called Test decides which one of the two
|
||||
* // are chosen for creating a test case.
|
||||
* function test() public returns (uint) {
|
||||
* // The protobuf field "Contract.test" decides which of
|
||||
* // the two internal functions "test_calldata_coding()"
|
||||
* // and "test_returndata_coding()" are called. Here,
|
||||
* // we assume that the protobuf field equals "CALLDATA_CODER"
|
||||
* return this.test_calldata_coding()
|
||||
* }
|
||||
*
|
||||
* returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||
* // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data.
|
||||
* // Offset return value to distinguish between failures originating from coder_public and coder_external.
|
||||
* if (returnVal != 0)
|
||||
* return uint(200000) + returnVal;
|
||||
* // Return zero if all checks pass.
|
||||
* return 0;
|
||||
* }
|
||||
* // The following function is generated if the protobuf field
|
||||
* // "Contract.test" is equal to "RETURNDATA_CODER".
|
||||
* function test_returndata_coding() internal returns (uint) {
|
||||
* string memory lv_0, bytes memory lv_1 = test_returndata_external();
|
||||
* if (lv_0 != 044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d)
|
||||
* return 1;
|
||||
* if (lv_1 != "1")
|
||||
* return 2;
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* /// Accepts function selector, correct argument encoding, and an invalid encoding length as input.
|
||||
* /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding
|
||||
* /// succeeds. Returns zero if both calls meet expectation.
|
||||
* function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
||||
* public returns (uint) {
|
||||
* ...
|
||||
* }
|
||||
* // The following function is generated if the protobuf field
|
||||
* // "Contract.test" is equal to "RETURNDATA_CODER".
|
||||
* function test_returndata_external() external returns (string memory, bytes memory)
|
||||
* {
|
||||
* sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
|
||||
* bytes memory lv_0 = "1";
|
||||
* return (sv_0, lv_0);
|
||||
* }
|
||||
*
|
||||
* /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns
|
||||
* /// the correct and incorrect abi encoding for calling the function specified by the function selector.
|
||||
* function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
||||
* internal pure returns (bytes memory, bytes memory) {
|
||||
* ...
|
||||
* }
|
||||
* // The following function is generated if the protobuf field
|
||||
* // "Contract.test" is equal to "CALLDATA_CODER".
|
||||
* function test_calldata_coding() internal returns (uint) {
|
||||
* // Local variable
|
||||
* bytes lv_1 = "1";
|
||||
* sv_0 = "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d";
|
||||
* uint returnVal = this.coder_public(sv_0, lv_1);
|
||||
* if (returnVal != 0)
|
||||
* return returnVal;
|
||||
* // Since the return codes in the public and external coder functions are identical
|
||||
* // we offset error code by a fixed amount (200000) for differentiation.
|
||||
* returnVal = this.coder_external(sv_0, lv_1);
|
||||
* if (returnVal != 0)
|
||||
* return 200000 + returnVal;
|
||||
* // Encode parameters
|
||||
* bytes memory argumentEncoding = abi.encode(<parameter_names>);
|
||||
* returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||
* // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data.
|
||||
* if (returnVal != 0)
|
||||
* return returnVal;
|
||||
*
|
||||
* /// Compares two dynamically sized bytes arrays for equality.
|
||||
* function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
|
||||
* ...
|
||||
* }
|
||||
* returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||
* // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data.
|
||||
* // Offset return value to distinguish between failures originating from coder_public and coder_external.
|
||||
* if (returnVal != 0)
|
||||
* return uint(200000) + returnVal;
|
||||
* // Return zero if all checks pass.
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* // Public function that is called by test() function. Accepts one or more arguments and returns
|
||||
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
|
||||
* function coder_public(string memory c_0, bytes memory c_1) public pure returns (uint) {
|
||||
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
||||
* return 1;
|
||||
* if (!bytesCompare(c_1, "1"))
|
||||
* return 2;
|
||||
* return 0;
|
||||
* }
|
||||
* /// Accepts function selector, correct argument encoding, and an invalid encoding length as input.
|
||||
* /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding
|
||||
* /// succeeds. Returns zero if both calls meet expectation.
|
||||
* function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
||||
* public returns (uint) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* // External function that is called by test() function. Accepts one or more arguments and returns
|
||||
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
|
||||
* function coder_external(string calldata c_0, bytes calldata c_1) external pure returns (uint) {
|
||||
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
||||
* return 1;
|
||||
* if (!bytesCompare(c_1, "1"))
|
||||
* return 2;
|
||||
* return 0;
|
||||
* }
|
||||
* }
|
||||
* /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns
|
||||
* /// the correct and incorrect abi encoding for calling the function specified by the function selector.
|
||||
* function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
||||
* internal pure returns (bytes memory, bytes memory) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* /// Compares two dynamically sized bytes arrays for equality.
|
||||
* function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* // Public function that is called by test() function. Accepts one or more arguments and returns
|
||||
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
|
||||
* function coder_public(string memory c_0, bytes memory c_1) public pure returns (uint) {
|
||||
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
||||
* return 1;
|
||||
* if (!bytesCompare(c_1, "1"))
|
||||
* return 2;
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* // External function that is called by test() function. Accepts one or more arguments and returns
|
||||
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
|
||||
* function coder_external(string calldata c_0, bytes calldata c_1) external pure returns (uint) {
|
||||
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
||||
* return 1;
|
||||
* if (!bytesCompare(c_1, "1"))
|
||||
* return 2;
|
||||
* return 0;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace test
|
||||
@ -242,6 +276,20 @@ private:
|
||||
Delimiter _delimiter = Delimiter::ADD
|
||||
);
|
||||
|
||||
/// Append types to typed stream used by returndata coders.
|
||||
void appendTypes(
|
||||
bool _isValueType,
|
||||
std::string const& _typeString,
|
||||
Delimiter _delimiter
|
||||
);
|
||||
|
||||
/// Append typed return value.
|
||||
void appendTypedReturn(
|
||||
bool _isValueType,
|
||||
std::string const& _typeString,
|
||||
Delimiter _delimiter
|
||||
);
|
||||
|
||||
/// Returns a Solidity variable declaration statement
|
||||
/// @param _type: string containing Solidity type of the
|
||||
/// variable to be declared.
|
||||
@ -262,11 +310,17 @@ private:
|
||||
/// Return comma separated typed function parameters as string
|
||||
std::string typedParametersAsString(CalleeType _calleeType);
|
||||
|
||||
/// Return Solidity helper functions as string
|
||||
std::string helperFunctions();
|
||||
/// Return commonly used Solidity helper functions as string
|
||||
std::string commonHelperFunctions();
|
||||
|
||||
/// Return top-level test code as string
|
||||
std::string testCode(unsigned _invalidLength);
|
||||
/// Return helper functions used to test calldata coding
|
||||
std::string calldataHelperFunctions();
|
||||
|
||||
/// Return top-level calldata coder test function as string
|
||||
std::string testCallDataFunction(unsigned _invalidLength);
|
||||
|
||||
/// Return top-level returndata coder test function as string
|
||||
std::string testReturnDataFunction();
|
||||
|
||||
/// Return the next variable count that is used for
|
||||
/// variable naming.
|
||||
@ -276,15 +330,30 @@ private:
|
||||
}
|
||||
|
||||
/// Return a pair of names for Solidity variable and the same variable when
|
||||
/// passed as a function parameter.
|
||||
static std::pair<std::string, std::string> newVarNames(unsigned _varCounter)
|
||||
/// passed either as a function parameter or used to store the tuple
|
||||
/// returned from a function.
|
||||
/// @param _varCounter: name suffix
|
||||
/// @param _stateVar: predicate that is true for state variables, false otherwise
|
||||
std::pair<std::string, std::string> newVarNames(unsigned _varCounter, bool _stateVar)
|
||||
{
|
||||
std::string varName = _stateVar ? s_stateVarNamePrefix : s_localVarNamePrefix;
|
||||
return std::make_pair(
|
||||
s_varNamePrefix + std::to_string(_varCounter),
|
||||
s_paramNamePrefix + std::to_string(_varCounter)
|
||||
varName + std::to_string(_varCounter),
|
||||
paramName() + std::to_string(_varCounter)
|
||||
);
|
||||
}
|
||||
|
||||
std::string paramName()
|
||||
{
|
||||
switch (m_test)
|
||||
{
|
||||
case Contract_Test::Contract_Test_CALLDATA_CODER:
|
||||
return s_paramNamePrefix;
|
||||
case Contract_Test::Contract_Test_RETURNDATA_CODER:
|
||||
return s_localVarNamePrefix;
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the last dynamically encoded Solidity type is right
|
||||
/// padded, returning true if it is and false otherwise.
|
||||
bool isLastDynParamRightPadded()
|
||||
@ -303,6 +372,12 @@ private:
|
||||
/// Contains typed parameter list to be passed to callee functions
|
||||
std::ostringstream m_typedParamsExternal;
|
||||
std::ostringstream m_typedParamsPublic;
|
||||
/// Contains type stream to be used in returndata coder function
|
||||
/// signature
|
||||
std::ostringstream m_types;
|
||||
std::ostringstream m_typedReturn;
|
||||
/// Argument names to be passed to coder functions
|
||||
std::ostringstream m_argsCoder;
|
||||
/// Predicate that is true if we are in contract scope
|
||||
bool m_isStateVar;
|
||||
unsigned m_counter;
|
||||
@ -316,9 +391,12 @@ private:
|
||||
/// Struct counter
|
||||
unsigned m_structCounter;
|
||||
unsigned m_numStructsAdded;
|
||||
/// Enum stating abiv2 coder to be tested
|
||||
Contract_Test m_test;
|
||||
/// Prefixes for declared and parameterized variable names
|
||||
static auto constexpr s_varNamePrefix = "x_";
|
||||
static auto constexpr s_paramNamePrefix = "c_";
|
||||
static auto constexpr s_localVarNamePrefix = "lv_";
|
||||
static auto constexpr s_stateVarNamePrefix = "sv_";
|
||||
static auto constexpr s_paramNamePrefix = "p_";
|
||||
};
|
||||
|
||||
/// Visitor interface for Solidity protobuf types.
|
||||
@ -857,4 +935,4 @@ public:
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user